diff --git a/examples/webapp/app.py b/examples/webapp/app.py index 839e197..9daf556 100644 --- a/examples/webapp/app.py +++ b/examples/webapp/app.py @@ -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" @@ -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) diff --git a/examples/webapp/templates/pseudolocalize_template.html b/examples/webapp/templates/pseudolocalize_template.html index c8c9517..8ec96e0 100644 --- a/examples/webapp/templates/pseudolocalize_template.html +++ b/examples/webapp/templates/pseudolocalize_template.html @@ -48,5 +48,13 @@

Pseudo-localization options:


+
+

Pseudolocalize PO File

+ +
+ +
+ +
diff --git a/examples/webapp/test_app.py b/examples/webapp/test_app.py index cdcd380..9d2bc99 100644 --- a/examples/webapp/test_app.py +++ b/examples/webapp/test_app.py @@ -1,18 +1,27 @@ 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}.", @@ -20,10 +29,84 @@ def test_pseudo(self): "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__":