mrfirdauss commited on
Commit
24beb7e
·
1 Parent(s): 78a797a

feat: japan letter pdf

Browse files
app/util/JapanMultiEntryvisaLetterGenerator.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import datetime
2
+ from fpdf import FPDF
3
+ from .PDFDocumentGenerator import PDFDocumentGenerator # The fpdf2-based one
4
+
5
+ class JapanMultiEntryVisaLetterGenerator(PDFDocumentGenerator):
6
+ """
7
+ Generates the specific PDF for the Japan Multiple Entry Visa application
8
+ using the pure-Python fpdf2 library.
9
+ """
10
+ def __init__(self, data: dict):
11
+ super().__init__(data)
12
+
13
+ def build_document(self, pdf: FPDF):
14
+ """
15
+ Creates the full PDF document for the Japan visa letter
16
+ by adding content to the FPDF object.
17
+ """
18
+
19
+ name = self.data.get("name", "YOUR NAME")
20
+ address = self.data.get("address", "YOUR ADDRESS")
21
+ city_postal = self.data.get("city_postal", "CITY, POSTAL CODE")
22
+ email = self.data.get("email", "your.email@example.com")
23
+ phone = self.data.get("phone", "+123456789")
24
+
25
+ today = datetime.date.today()
26
+ formatted_date = today.strftime("%d, %B, %Y")
27
+
28
+ pdf.set_font('Arial', '', 11)
29
+ pdf.set_margins(25, 25)
30
+
31
+ sender_info = (
32
+ f"{name}\n"
33
+ f"{address}\n"
34
+ f"{city_postal}\n"
35
+ f"{email}\n"
36
+ f"{phone}"
37
+ )
38
+ pdf.multi_cell(0, 5, sender_info)
39
+ pdf.ln(10)
40
+
41
+ pdf.cell(0, 5, f"Jakarta, {formatted_date}")
42
+
43
+ pdf.ln(10)
44
+ recipient_info = (
45
+ "To:\n"
46
+ "Embassy of Japan in Indonesia\n"
47
+ "Jl. M.H. Thamrin No.24, Gondangdia, Menteng, Jakarta Pusat\n"
48
+ "Jakarta 10350"
49
+ )
50
+ pdf.multi_cell(0, 5, recipient_info)
51
+
52
+ pdf.ln(10)
53
+ pdf.set_font('Arial', 'B', 11)
54
+ pdf.cell(0, 5, "Subject: Reason for Applying for a Japan Multiple Entry Tourist Visa")
55
+ pdf.set_font('Arial', '', 11) #
56
+ pdf.ln(5)
57
+ pdf.multi_cell(0, 5, "Dear Sir/Madam,")
58
+ pdf.ln(5)
59
+
60
+ body_text = (
61
+ "I am writing to apply for a Japan multiple-entry tourist visa. The purpose "
62
+ "of my application is to facilitate easier travel to Japan for tourism "
63
+ "and personal reasons. I plan to visit Japan multiple times over the "
64
+ "next few years, and a multiple-entry visa would offer the flexibility "
65
+ "to do so without needing to reapply for a new visa each time.\n\n"
66
+ "This visa will enable me to explore various regions, experience "
67
+ "cultural events, and take part in Japan's tourism offerings.\n\n"
68
+ "I have attached all necessary documents to support my application. "
69
+ "Should you require any further information or additional "
70
+ "documentation, please feel free to contact me.\n\n"
71
+ "Thank you for considering my application. I look forward to your response."
72
+ )
73
+ pdf.multi_cell(0, 5, body_text)
74
+
75
+ pdf.ln(10)
76
+ pdf.cell(0, 5, "Sincerely,")
77
+
78
+ pdf.ln(25)
79
+
80
+ pdf.cell(0, 5, name)
app/util/PDFDocumentGenerator.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import tempfile
3
+ import datetime
4
+ from abc import ABC, abstractmethod
5
+ from fpdf import FPDF # <-- Import FPDF
6
+
7
+ class PDFDocumentGenerator(ABC):
8
+ """
9
+ Abstract Base Class for generating a PDF document from data
10
+ using a pure-Python library (fpdf2).
11
+ """
12
+ def __init__(self, data: dict):
13
+ """
14
+ Initializes the generator with user-provided data.
15
+
16
+ :param data: A dictionary containing the data for the document.
17
+ """
18
+ self.data = data
19
+
20
+ @abstractmethod
21
+ def build_document(self, pdf: FPDF):
22
+ """
23
+ Subclasses must implement this method to build the PDF
24
+ by calling methods on the provided fpdf.FPDF object.
25
+
26
+ :param pdf: An FPDF object to build upon.
27
+ """
28
+ pass
29
+
30
+ def compile_pdf(self, tempdir: str) -> (str, str):
31
+ """
32
+ Generates the PDF document using fpdf2 and saves it in tempdir.
33
+
34
+ :param tempdir: The temporary directory to use for compilation.
35
+ :return: A tuple of (pdf_filepath, error_message).
36
+ On success, (pdf_filepath, None).
37
+ On failure, (None, error_message).
38
+ """
39
+ try:
40
+ # 1. Initialize the FPDF object
41
+ pdf = FPDF()
42
+ pdf.add_page()
43
+
44
+ # 2. Let the subclass build the document
45
+ self.build_document(pdf)
46
+
47
+ # 3. Define file path and save the document
48
+ pdf_filename = "document.pdf"
49
+ pdf_filepath = os.path.join(tempdir, pdf_filename)
50
+
51
+ pdf.output(pdf_filepath)
52
+
53
+ # 4. Check if PDF was successfully created
54
+ if not os.path.exists(pdf_filepath):
55
+ return None, "PDF file was not created by fpdf2."
56
+
57
+ # Success
58
+ return pdf_filepath, None
59
+
60
+ except Exception as e:
61
+ print(f"Error during PDF generation: {e}")
62
+ return None, str(e)
docker-compose.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ app:
5
+ build: .
6
+ ports:
7
+ - "7860:7860"
8
+ environment:
9
+ - PORT=7860
10
+ - PLAYWRIGHT_BROWSERS_PATH=/home/user/.cache/ms-playwright
11
+ restart: always
requirements.txt CHANGED
@@ -10,4 +10,5 @@ google-genai
10
  beautifulsoup4
11
  pymupdf
12
  flask[async]
13
- pandas
 
 
10
  beautifulsoup4
11
  pymupdf
12
  flask[async]
13
+ pandas
14
+ fpdf
server.py CHANGED
@@ -1,16 +1,19 @@
1
  import os
 
2
  os.environ["PLAYWRIGHT_BROWSERS_PATH"] = "/home/user/.cache/ms-playwright"
3
 
4
  import logging
5
- from flask import Flask, request, jsonify
6
  from dotenv import load_dotenv
7
  import json
8
  import requests
9
  import uuid
 
 
10
 
11
  from app.util.gen_ai_base import GenAIBaseClient
12
  from app.util.browser_agent import BrowserAgent
13
- # from app.util.visa_availability_scraper_playwright import PassportIndexVisaScraper
14
  import sys
15
  sys.stdout.reconfigure(line_buffering=True)
16
  API = "https://api-dev.spun.global"
@@ -109,7 +112,48 @@ def create_app() -> Flask:
109
  # else:
110
  # return jsonify({"error": "Failed to retrieve visa information"}), 404
111
  # return jsonify({"error": "Unexpected error"}), 500
112
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  @app.route('/', methods=['GET'])
114
  def hello_world():
115
  return "Flask server is running.", 200
 
1
  import os
2
+ import tempfile
3
  os.environ["PLAYWRIGHT_BROWSERS_PATH"] = "/home/user/.cache/ms-playwright"
4
 
5
  import logging
6
+ from flask import Flask, request, jsonify, send_file
7
  from dotenv import load_dotenv
8
  import json
9
  import requests
10
  import uuid
11
+ import asyncio
12
+ import io
13
 
14
  from app.util.gen_ai_base import GenAIBaseClient
15
  from app.util.browser_agent import BrowserAgent
16
+ from app.util.JapanMultiEntryvisaLetterGenerator import JapanMultiEntryVisaLetterGenerator
17
  import sys
18
  sys.stdout.reconfigure(line_buffering=True)
19
  API = "https://api-dev.spun.global"
 
112
  # else:
113
  # return jsonify({"error": "Failed to retrieve visa information"}), 404
114
  # return jsonify({"error": "Unexpected error"}), 500
115
+ @app.route('/generate/japan-multientry-tourist', methods=['POST'])
116
+ def generate_japan_multientry_tourist():
117
+ """
118
+ Generates a Japan Visa Letter PDF and returns it as a downloadable file.
119
+ Expects JSON payload:
120
+ {
121
+ "name": "Your Name",
122
+ "address": "Your Address Line 1",
123
+ "city_postal": "City, Postal Code",
124
+ "email": "your.email@example.com",
125
+ "phone": "+123456789"
126
+ }
127
+ """
128
+ try:
129
+ data = request.get_json()
130
+ if not data:
131
+ return jsonify({"error": "No JSON payload provided"}), 400
132
+
133
+ letter_generator = JapanMultiEntryVisaLetterGenerator(data)
134
+
135
+ with tempfile.TemporaryDirectory() as tempdir:
136
+ pdf_filepath, error_msg = letter_generator.compile_pdf(tempdir)
137
+
138
+ if error_msg:
139
+ return jsonify({"error": error_msg}), 500
140
+
141
+ with open(pdf_filepath, "rb") as f:
142
+ pdf_bytes = io.BytesIO(f.read())
143
+
144
+ pdf_bytes.seek(0)
145
+
146
+ return send_file(
147
+ pdf_bytes,
148
+ mimetype="application/pdf",
149
+ as_attachment=True,
150
+ download_name="Japan_Visa_Application_Letter.pdf"
151
+ )
152
+
153
+ except Exception as e:
154
+ print(f"Error in route: {e}")
155
+ return jsonify({"error": str(e)}), 500
156
+
157
  @app.route('/', methods=['GET'])
158
  def hello_world():
159
  return "Flask server is running.", 200