Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 68 additions & 2 deletions examples/webapp/app.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env python3

from flask import Flask, jsonify, make_response, redirect, render_template, request
from flask import Flask, jsonify, make_response, redirect, render_template, request, send_file
import os
import tempfile

import pseudol10nutil.transforms as xforms
from pseudol10nutil import PseudoL10nUtil
from pseudol10nutil import PseudoL10nUtil, POFileUtil

app = Flask(__name__)
appname = "pseudol10nutil"
Expand Down Expand Up @@ -106,5 +108,69 @@ def do_pseudo_ui():
return render_template("pseudolocalize_template.html", **default_options)


@app.route(ui_base_url + "po_upload", methods=["POST"])
def do_pseudo_po_upload():
input_temp_file_path = None
output_temp_file_path = None
try:
if "po_file" not in request.files:
return make_response(
jsonify({"error": "400 Error: No file part in the request."}), 400
)

file = request.files["po_file"]

if file.filename == "":
return make_response(
jsonify({"error": "400 Error: No file selected for uploading."}), 400
)

if not file.filename.endswith(".po"):
return make_response(
jsonify(
{"error": "400 Error: Invalid file type. Please upload a .po file."}
),
400,
)

# Create temporary files
fd_input, input_temp_file_path = tempfile.mkstemp(suffix=".po")
os.close(fd_input) # close file descriptor as pofileutil will open and close the file
fd_output, output_temp_file_path = tempfile.mkstemp(suffix=".po")
os.close(fd_output) # close file descriptor as pofileutil will open and close the file


file.save(input_temp_file_path)

pofileutil = POFileUtil()
# Apply the same transforms as the UI text input for consistency
# Default to diacritics, square brackets, and padding
# This could be made configurable in the UI later if needed
current_transforms = [
xforms.transliterate_diacritic,
xforms.pad_length,
xforms.square_brackets
]
pofileutil.transforms = current_transforms
pofileutil.pseudolocalizefile(input_temp_file_path, output_temp_file_path)

return send_file(
output_temp_file_path,
as_attachment=True,
download_name="pseudolocalized.po",
mimetype="application/x-po",
)
except Exception as e:
# Log the exception e for debugging if necessary
return make_response(
jsonify({"error": f"500 Error: Internal server error during processing. {str(e)}"}), 500
)
finally:
if input_temp_file_path and os.path.exists(input_temp_file_path):
os.remove(input_temp_file_path)
if output_temp_file_path and os.path.exists(output_temp_file_path):
os.remove(output_temp_file_path)


if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
8 changes: 8 additions & 0 deletions examples/webapp/templates/pseudolocalize_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,13 @@ <h3 class="centered">Pseudo-localization options:</h3>
<br />
<textarea id="pseudolocalize_output" name="pseudolocalize_output" readonly>{{ pseudolocalized_text_output }}</textarea>
</form>
<form class="centered" id="pseudolocalize_po_form" method="post" action="/pseudol10nutil/po_upload" enctype="multipart/form-data">
<h2 class="centered">Pseudolocalize PO File</h2>
<label for="po_file_input">PO file to pseudo-localize:</label>
<br />
<input type="file" id="po_file_input" name="po_file" accept=".po" />
<br />
<input type="submit" name="do_pseudolocalize_po" id="do_pseudolocalize_po" value="Pseudolocalize PO File"/>
</form>
</body>
</html>
105 changes: 94 additions & 11 deletions examples/webapp/test_app.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,112 @@
import unittest
import io
import json

import requests
from examples.webapp.app import app, ui_base_url, api_base_url
from src.pseudol10nutil.pseudol10nutil import PseudoL10nUtil
from src.pseudol10nutil import transforms as xforms

from pseudol10nutil import PseudoL10nUtil

base_url = "http://localhost:5000/pseudol10nutil/api/v1.0/"
headers = {"Accept": "application/json", "Content-Type": "application/json"}


class TestPseudoL10nUtil(unittest.TestCase):
class TestWebApp(unittest.TestCase):
def setUp(self):
self.client = app.test_client()
# Keep util if other tests directly use its methods,
# or for comparing results in API tests.
self.util = PseudoL10nUtil()
# Default transforms for PO file for assertion comparison
# This should match the defaults set in app.py's po_upload route
self.util.transforms = [
xforms.transliterate_diacritic,
xforms.pad_length,
xforms.square_brackets
]

def test_pseudo(self):
def test_pseudo_api(self):
data = {
"key1": "The quick brown fox jumps over the lazy dog.",
"key2": "The quick brown {animal1} jumps over the lazy {animal2}.",
"key3": "The quick brown %s jumps over the lazy %s.",
"key4": "The quick brown %(animal1)s jumps over the lazy %(animal2)s.",
}
request_data = {"strings": data}
resp = requests.post(base_url + "pseudo", headers=headers, json=request_data)
results = resp.json()["strings"]
# Reset transforms to default for this specific API test if needed,
# as self.util might be configured with PO file defaults in setUp.
# For this test, we use a fresh PseudoL10nUtil instance with its own default transforms.
fresh_util_for_api_test = PseudoL10nUtil()
# If the main API also uses specific non-default transforms, set them here for fresh_util_for_api_test
# For now, assuming it uses the class defaults.

resp = self.client.post(
api_base_url + "pseudo",
headers={"Content-Type": "application/json", "Accept": "application/json"},
data=json.dumps(request_data),
)
self.assertEqual(resp.status_code, 200)
results = resp.get_json()["strings"]
for k, v in results.items():
self.assertEqual(self.util.pseudolocalize(data[k]), v)
self.assertEqual(fresh_util_for_api_test.pseudolocalize(data[k]), v)

def test_po_file_upload_success(self):
po_content = 'msgid "Hello"\nmsgstr ""'
# Apply the specific transforms to "Hello" to get the expected output part
# The self.util is already configured with these transforms in setUp
expected_output_part = self.util.pseudolocalize("Hello")

data = {
'po_file': (io.BytesIO(po_content.encode('utf-8')), 'test.po')
}
resp = self.client.post(
ui_base_url + "po_upload",
content_type='multipart/form-data',
data=data
)
self.assertEqual(resp.status_code, 200)
self.assertIn('attachment', resp.headers['Content-Disposition'])
self.assertIn('filename=pseudolocalized.po', resp.headers['Content-Disposition'])
# Check if the pseudolocalized string is in the output
# The actual PO file will have more structure (headers, etc.)
# but checking for the transformed msgid is a good indicator.
self.assertIn(expected_output_part, resp.data.decode('utf-8'))


def test_po_file_upload_no_file(self):
resp = self.client.post(
ui_base_url + "po_upload",
content_type='multipart/form-data',
data={}
)
self.assertEqual(resp.status_code, 400)
json_data = resp.get_json()
self.assertIn("error", json_data)
self.assertIn("No file part", json_data["error"])

def test_po_file_upload_wrong_file_type(self):
data = {
'po_file': (io.BytesIO(b"this is not a po file"), 'test.txt')
}
resp = self.client.post(
ui_base_url + "po_upload",
content_type='multipart/form-data',
data=data
)
self.assertEqual(resp.status_code, 400)
json_data = resp.get_json()
self.assertIn("error", json_data)
self.assertIn("Invalid file type", json_data["error"])

def test_po_file_upload_empty_filename(self):
data = {
'po_file': (io.BytesIO(b"some content"), '') # Empty filename
}
resp = self.client.post(
ui_base_url + "po_upload",
content_type='multipart/form-data',
data=data
)
self.assertEqual(resp.status_code, 400)
json_data = resp.get_json()
self.assertIn("error", json_data)
self.assertIn("No file selected", json_data["error"])


if __name__ == "__main__":
Expand Down