From 1753670cb9689c3d70446335f34cebd54d8b75f2 Mon Sep 17 00:00:00 2001 From: mhayes2772 Date: Wed, 15 May 2024 15:23:13 -0700 Subject: [PATCH 1/4] Refactoring and testing --- .../controllers/BackEndControllerHelper.java | 30 ---------- .../ui/controllers/MedicalController.java | 33 +++++++--- .../services/TranslationServiceTest.java | 60 +++++++++++++++++++ 3 files changed, 84 insertions(+), 39 deletions(-) create mode 100644 test/unit/app/femr/business/services/TranslationServiceTest.java diff --git a/app/femr/ui/controllers/BackEndControllerHelper.java b/app/femr/ui/controllers/BackEndControllerHelper.java index 2214444a1..dc3db7dde 100644 --- a/app/femr/ui/controllers/BackEndControllerHelper.java +++ b/app/femr/ui/controllers/BackEndControllerHelper.java @@ -1,17 +1,8 @@ package femr.ui.controllers; -import femr.util.translation.TranslationServer; -import femr.util.translation.TranslationJson; import java.io.*; -import java.net.MalformedURLException; import java.util.ArrayList; -import java.net.HttpURLConnection; -import java.net.URL; - -import com.fasterxml.jackson.databind.ObjectMapper; -import org.json.JSONArray; -import org.json.JSONObject; public class BackEndControllerHelper { @@ -52,25 +43,4 @@ public static ArrayList executeSpeedTestScript(String absPath) { return speedInfo; } - - public static String translate(String arg, String from, String to) { - String output = ""; - try { - output = TranslationServer.makeServerRequest(arg, from, to); - - //parse translation from JSON - ObjectMapper mapper = new ObjectMapper(); - TranslationJson api = mapper.readValue(output, TranslationJson.class); - output = api.translatedText; - - } catch(MalformedURLException e){ - System.out.println("Malformed URL Exception"); - System.out.println(e.getMessage()); - } catch(IOException e){ - System.out.println("IOException for parsing JSON"); - System.out.println(e.getMessage()); - } - return output; - } - } diff --git a/app/femr/ui/controllers/MedicalController.java b/app/femr/ui/controllers/MedicalController.java index 4d7221b20..36db15e02 100644 --- a/app/femr/ui/controllers/MedicalController.java +++ b/app/femr/ui/controllers/MedicalController.java @@ -1,5 +1,6 @@ package femr.ui.controllers; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.inject.Inject; import controllers.AssetsFinder; import femr.business.services.core.*; @@ -19,6 +20,8 @@ import femr.util.DataStructure.Mapping.TabFieldMultiMap; import femr.util.DataStructure.Mapping.VitalMultiMap; import femr.util.stringhelpers.StringUtils; +import femr.util.translation.TranslationJson; +import femr.util.translation.TranslationServer; import play.data.Form; import play.data.FormFactory; import play.libs.Json; @@ -259,15 +262,15 @@ public Result editGet(int patientId) { } public Result translateGet() { - String text = request().getQueryString("text"); + String jsonText = request().getQueryString("text"); + int patientId = Integer.parseInt(request().getQueryString("patientId")); + //Harrison Shu - CurrentUser currentUserSession = sessionService.retrieveCurrentUserSession(); - String toLanguage = currentUserSession.getLanguageCode(); + String toLanguage = sessionService.retrieveCurrentUserSession().getLanguageCode(); // retrieve current patient encounter encounter - int patientId = Integer.parseInt(request().getQueryString("patientId")); - ServiceResponse currentEncounterByPatientId = searchService.retrieveRecentPatientEncounterItemByPatientId(patientId); + ServiceResponse currentEncounterByPatientId = searchService.retrieveRecentPatientEncounterItemByPatientId(patientId); if (currentEncounterByPatientId.hasErrors()) { throw new RuntimeException(); } @@ -284,24 +287,36 @@ public Result translateGet() { if (Objects.equals(toLanguage, fromLanguage)) { responseMap.put("translation", "SameToSame"); } else { - responseMap.put("translation", translate(text, fromLanguage,toLanguage)); + responseMap.put("translation", translate(jsonText, fromLanguage,toLanguage)); } return ok(Json.toJson(responseMap)); } // Calls Python Script to translate - private String translate(String text, String fromLanguage, String toLanguage) { - + private String translate(String jsonText, String fromLanguage, String toLanguage) { String data = ""; try { - data = BackEndControllerHelper.translate(text, fromLanguage, toLanguage); + data = TranslationServer.makeServerRequest(jsonText, fromLanguage, toLanguage); + data = parseJsonResponse(data); } catch (Exception e) { e.printStackTrace(); } return data; } + public String parseJsonResponse(String jsonResponse){ + try{ + ObjectMapper mapper = new ObjectMapper(); + TranslationJson api = mapper.readValue(jsonResponse, TranslationJson.class); + jsonResponse = api.translatedText; + return jsonResponse; + } catch(Exception e){ + System.out.println(e.getMessage()); + throw new RuntimeException(); + } + } + /** * Get the populated partial view that represents 1 row of new prescription fields * - meant to be an AJAX call diff --git a/test/unit/app/femr/business/services/TranslationServiceTest.java b/test/unit/app/femr/business/services/TranslationServiceTest.java new file mode 100644 index 000000000..d7e6251c2 --- /dev/null +++ b/test/unit/app/femr/business/services/TranslationServiceTest.java @@ -0,0 +1,60 @@ +package unit.app.femr.business.services; + +import controllers.AssetsFinder; +import femr.business.services.core.*; +import femr.business.services.system.UserService; +import femr.common.IItemModelMapper; +import femr.data.IDataModelMapper; +import femr.data.daos.core.IUserRepository; +import femr.ui.controllers.helpers.FieldHelper; +import femr.util.encryptions.IPasswordEncryptor; +import femr.ui.controllers.MedicalController; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import play.data.FormFactory; + +import static org.mockito.Mockito.mock; + +public class TranslationServiceTest { + + AssetsFinder assetsFinder; + FormFactory formFactory; + ITabService tabService; + IEncounterService encounterService; + IMedicationService medicationService; + IPhotoService photoService; + ISessionService sessionService; + ISearchService searchService; + IVitalService vitalService; + MedicalController medicalController; + + @Before + public void setUp(){ + assetsFinder = mock(AssetsFinder.class); + formFactory = mock(FormFactory.class); + tabService = mock(ITabService.class); + encounterService = mock(IEncounterService.class); + sessionService = mock(ISessionService.class); + searchService = mock(ISearchService.class); + medicationService = mock(IMedicationService.class); + photoService = mock(IPhotoService.class); + vitalService = mock(IVitalService.class); + medicalController = new MedicalController( + assetsFinder, + formFactory, + tabService, + encounterService, + medicationService, + photoService, + sessionService, + searchService, + vitalService); + } + + @Test + public void parseJsonResponse() { + String data = "{\"translate_data\" : \"Hello, World\"}"; + Assert.assertEquals("Hello, World", medicalController.parseJsonResponse(data)); + } +} From a6bf6cacc0890c337fd917130558ec2c49225bda Mon Sep 17 00:00:00 2001 From: mhayes2772 Date: Thu, 16 May 2024 08:35:09 -0700 Subject: [PATCH 2/4] Creating patient encounter saves langCode from user session --- .../business/services/system/EncounterService.java | 13 ++++++++++--- app/femr/data/daos/core/IEncounterRepository.java | 2 +- app/femr/data/daos/system/EncounterRepository.java | 4 ++-- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/app/femr/business/services/system/EncounterService.java b/app/femr/business/services/system/EncounterService.java index 7bbeeddaa..73dead914 100644 --- a/app/femr/business/services/system/EncounterService.java +++ b/app/femr/business/services/system/EncounterService.java @@ -18,12 +18,15 @@ */ package femr.business.services.system; +import femr.business.services.core.ISessionService; +import femr.common.dtos.CurrentUser; import io.ebean.ExpressionList; import io.ebean.Query; import com.google.inject.Inject; import com.google.inject.name.Named; import femr.business.helpers.QueryProvider; import femr.business.services.core.IEncounterService; +import femr.business.services.core.ISessionService; import femr.common.IItemModelMapper; import femr.common.dtos.ServiceResponse; import femr.common.models.*; @@ -46,6 +49,7 @@ public class EncounterService implements IEncounterService { private final IRepository chiefComplaintRepository; private final IPatientRepository patientRepository; private final IEncounterRepository patientEncounterRepository; + private final ISessionService sessionService; private final IRepository patientEncounterTabFieldRepository; private final IRepository tabFieldRepository; private final IUserRepository userRepository; @@ -56,6 +60,7 @@ public class EncounterService implements IEncounterService { public EncounterService(IRepository chiefComplaintRepository, IPatientRepository patientRepository, IEncounterRepository patientEncounterRepository, + ISessionService sessionService, IRepository patientEncounterTabFieldRepository, IRepository tabFieldRepository, IUserRepository userRepository, @@ -65,6 +70,7 @@ public EncounterService(IRepository chiefComplaintRepository, this.chiefComplaintRepository = chiefComplaintRepository; this.patientRepository = patientRepository; this.patientEncounterRepository = patientEncounterRepository; + this.sessionService = sessionService; this.patientEncounterTabFieldRepository = patientEncounterTabFieldRepository; this.tabFieldRepository = tabFieldRepository; this.userRepository = userRepository; @@ -77,7 +83,7 @@ public EncounterService(IRepository chiefComplaintRepository, */ @Override public ServiceResponse createPatientEncounter(int patientId, int userId, Integer tripId, String ageClassification, List chiefComplaints) { - + System.out.println("Create Patient Encounter"); ServiceResponse response = new ServiceResponse<>(); try { @@ -98,8 +104,9 @@ public ServiceResponse createPatientEncounter(int patientI if (patientAgeClassification != null) patientAgeClassificationId = patientAgeClassification.getId(); - IPatientEncounter newPatientEncounter = patientEncounterRepository.createPatientEncounter(patientId, dateUtils.getCurrentDateTime(), nurseUser.getId(), patientAgeClassificationId, tripId); - + CurrentUser currentUserSession = sessionService.retrieveCurrentUserSession(); + String languageCode = currentUserSession.getLanguageCode(); + IPatientEncounter newPatientEncounter = patientEncounterRepository.createPatientEncounter(patientId, dateUtils.getCurrentDateTime(), nurseUser.getId(), patientAgeClassificationId, tripId, languageCode); List chiefComplaintBeans = new ArrayList<>(); Integer chiefComplaintSortOrder = 0; for (String cc : chiefComplaints) { diff --git a/app/femr/data/daos/core/IEncounterRepository.java b/app/femr/data/daos/core/IEncounterRepository.java index e966d3c12..fa6dad7ac 100644 --- a/app/femr/data/daos/core/IEncounterRepository.java +++ b/app/femr/data/daos/core/IEncounterRepository.java @@ -17,7 +17,7 @@ public interface IEncounterRepository { * @param tripId id of the trip, may be null * @return the new patient encounter or null if an error happens */ - IPatientEncounter createPatientEncounter(int patientID, DateTime date, int userId, Integer patientAgeClassificationId, Integer tripId); + IPatientEncounter createPatientEncounter(int patientID, DateTime date, int userId, Integer patientAgeClassificationId, Integer tripId, String languageCode); /** * Deletes a patient's encounter. this is a soft delete diff --git a/app/femr/data/daos/system/EncounterRepository.java b/app/femr/data/daos/system/EncounterRepository.java index cd0a1dd83..a4a7ddc41 100644 --- a/app/femr/data/daos/system/EncounterRepository.java +++ b/app/femr/data/daos/system/EncounterRepository.java @@ -41,7 +41,7 @@ public EncounterRepository(Provider missionTripProvider, * {@inheritDoc} */ @Override - public IPatientEncounter createPatientEncounter(int patientID, DateTime date, int userId, Integer patientAgeClassificationId, Integer tripId){ + public IPatientEncounter createPatientEncounter(int patientID, DateTime date, int userId, Integer patientAgeClassificationId, Integer tripId, String languageCode){ IPatientEncounter patientEncounter = patientEncounterProvider.get(); @@ -57,7 +57,7 @@ public IPatientEncounter createPatientEncounter(int patientID, DateTime date, in patientEncounter.setPatientAgeClassification(Ebean.getReference(patientAgeClassificationProvider.get().getClass(), patientAgeClassificationId)); if (tripId != null) patientEncounter.setMissionTrip(Ebean.getReference(missionTripProvider.get().getClass(), tripId)); - + patientEncounter.setLanguageCode(languageCode); Ebean.save(patientEncounter); }catch (Exception ex){ From 4e9affcb76f4e4a27d53ee0efccba3ceea02990d Mon Sep 17 00:00:00 2001 From: mhayes2772 Date: Thu, 16 May 2024 08:35:38 -0700 Subject: [PATCH 3/4] POST requests functionality added --- .../util/translation/TranslationServer.java | 23 +++-- public/js/medical/medical.js | 89 +++++++++---------- translator/server.py | 80 +++++++++++------ 3 files changed, 115 insertions(+), 77 deletions(-) diff --git a/app/femr/util/translation/TranslationServer.java b/app/femr/util/translation/TranslationServer.java index fc573ee51..94f4f292a 100644 --- a/app/femr/util/translation/TranslationServer.java +++ b/app/femr/util/translation/TranslationServer.java @@ -9,6 +9,7 @@ import java.net.URLConnection; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Scanner; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @@ -33,15 +34,25 @@ public static String makeServerRequest(String text, String from, String to) thro try { // Harrison Shu // Encode the URL String parameter before creating URL to allow arabic and hebrew to be in the URL - String encodedText = URLEncoder.encode(text, StandardCharsets.UTF_8.toString()); +// String encodedText = URLEncoder.encode(text, StandardCharsets.UTF_8.toString()); +// System.out.println(text); + byte[] json = text.getBytes(StandardCharsets.UTF_8); + int length = json.length; //Make GET request - URL url = new URL("http://localhost:" + portNumber +"/?text=" + - encodedText + "&from=" + from + "&to=" + to); + URL url = new URL("http://localhost:" + portNumber + "/?from=" + from + "&to=" + to); HttpURLConnection con = (HttpURLConnection) url.openConnection(); - con.setRequestMethod("GET"); + con.setRequestMethod("POST"); + con.setDoOutput(true); + + con.setFixedLengthStreamingMode(length); + con.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); con.connect(); + try(OutputStream os = con.getOutputStream()) { + os.write(json); + } + //read response from server BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); response = in.readLine(); @@ -49,7 +60,9 @@ public static String makeServerRequest(String text, String from, String to) thro con.disconnect(); } catch(IOException e){ - return makeServerRequest(text, from, to); + System.out.println(e.getMessage()); + return "Translation Unavailable"; +// return makeServerRequest(text, from, to); } return response; } diff --git a/public/js/medical/medical.js b/public/js/medical/medical.js index b7193fa17..1538fa7e2 100644 --- a/public/js/medical/medical.js +++ b/public/js/medical/medical.js @@ -341,62 +341,59 @@ $(document).ready(function () { } console.log("text:", textToTranslate); + console.log(jsonObj); // get translation $.ajax({ type: 'get', url: '/translate', - data: {text : textToTranslate, patientId: patientId}, + data: {text : JSON.stringify(jsonObj), patientId: patientId}, success: function(response){ + console.log("response:", response); console.log("translation:", response.translation); - var listTranslated = response.translation.split("@"); - - if (response.translation.split(":")[0] === "SameToSame") { - // same to same (like en to en) - // option 1 - delete button + if(response.translation === "SameToSame"){ $("#toggleBtn").remove(); - // option 2 - say original - //$("#loading").remove(); - //$("#toggleBtn").text("Original"); - - } else if (response.translation.split(".")[0] === "Translation Unavailable") { - // option 1 - end buffering - $("#loading").remove(); - $("#toggleBtn").text("Unavailable"); - // option 2 - delete button - // $("#toggleBtn").remove(); - console.error(response.translation); - - } else if (listTranslated.length !== jsonObj.length) { - console.log("backup translation required out of 16 tabs ", listTranslated.length, " recovered"); - for (let i = 0; i < jsonObj.length; i++) { - $.ajax({ - type: 'get', - url: '/translate', - data: {text: jsonObj[i].text, patientId: patientId}, - success: function (response) { - if (i === jsonObj.length - 1) { - // end buffering on last field - $("#loading").remove(); - $("#toggleBtn").text("Show Original"); - } - populateField(response.translation, jsonObj, response.fromLanguageIsRtl, response.toLanguageIsRtl, i); - }, - failure: function (result) { - console.error('Failed to fetch backup translation'); - } - }); + } + else{ + var listTranslated = JSON.parse(response.translation); + console.log(listTranslated); + if(listTranslated[0]["text"] === "Translation Unavailable"){ + //option 1 - end buffering + $("#loading").remove(); + $("#toggleBtn").text("Unavailable"); } - } else { - // end buffering if no backup - $("#loading").remove(); - $("#toggleBtn").text("Show Original"); - - // for each field populate them - for (let i = 0; i < jsonObj.length; i++) { - var textOut = listTranslated[i]; - populateField(textOut, jsonObj, response.fromLanguageIsRtl, response.toLanguageIsRtl, i); + //ELSE IF NOT CORRECT ATM + else if (listTranslated.length !== jsonObj.length) { + console.log("backup translation required out of 16 tabs ", listTranslated.length, " recovered"); + for (let i = 0; i < jsonObj.length; i++) { + $.ajax({ + type: 'get', + url: '/translate', + data: {text: jsonObj[i].text, patientId: patientId}, + success: function (response) { + if (i === jsonObj.length - 1) { + // end buffering on last field + $("#loading").remove(); + $("#toggleBtn").text("Show Original"); + } + populateField(response.translation, jsonObj, response.fromLanguageIsRtl, response.toLanguageIsRtl, i); + }, + failure: function (result) { + console.error('Failed to fetch backup translation'); + } + }); + } + } + else{ + $("#loading").remove(); + $("#toggleBtn").text("Show Original"); + + // for each field populate them + for (let i = 0; i < jsonObj.length; i++) { + var textOut = listTranslated[i]["text"]; + populateField(textOut, jsonObj, response.fromLanguageIsRtl, response.toLanguageIsRtl, i); + } } } }, diff --git a/translator/server.py b/translator/server.py index c2ba7ba49..163de8ac7 100644 --- a/translator/server.py +++ b/translator/server.py @@ -42,35 +42,51 @@ def query_data(self): @cached_property def translate_data(self): - text = self.query_data['text'] + #parse request + length = int(self.headers['Content-Length']) + tab_list = json.loads(self.rfile.read(length).decode('utf-8')) from_code = self.query_data['from'] to_code = self.query_data['to'] - # Use Argos if Language Package Exists - if Path(f"{PATH}/translator/argos_models/translate-{from_code}_{to_code}.argosmodel").exists(): - translatedText = argostranslate.translate.translate(text, from_code, to_code) - return translatedText - # Use Marian if Language Package Exists in Marian but not Argos - elif Path(f"{PATH}/translator/marian_models/opus-mt-{from_code}-{to_code}").exists(): - marian = MarianModel(from_code, to_code) - translatedText = marian.translate([text]) - return translatedText[0] - # Use Argos "English in the Middle" if not in Argos and Marian by Default - elif (Path(f"{PATH}/translator/argos_models/translate-{from_code}_en.argosmodel").exists() and \ - Path(f"{PATH}/translator/argos_models/translate-{to_code}_en.argosmodel").exists()) or \ - (Path(f"{PATH}/translator/argos_models/translate-en_{from_code}.argosmodel").exists() and \ - Path(f"{PATH}/translator/argos_models/translate-en_{to_code}.argosmodel").exists()): - translatedText = argostranslate.translate.translate(text, from_code, to_code) - return translatedText - # If a package doesn't exist - else: - return "Translation Unavailable:" + from_code + to_code - - def do_GET(self): - self.send_response(200) - self.send_header("Content-Type", "application/json") - self.end_headers() - self.wfile.write(self.get_response().encode("utf-8")) + #for each tab in tab_list, build delimiter string to translate + num_tabs = len(tab_list) + translate_string = "" + for i in range(num_tabs): + if(i == 0): + translate_string = tab_list[i]["text"] + else: + translate_string = translate_string + " @ " + tab_list[i]["text"].replace("@", "at") + + #translate string and split into list on delimiter + translated_text = self.translate(translate_string, from_code, to_code) + translated_list = list(translated_text.split("@")) + + #for each translation, place text in copy of tab_list to return + out = tab_list + for i in range(len(translated_list)): + out[i]["text"] = translated_list[i] + return json.dumps(out) + + def translate(self, text, from_code, to_code): + # Use Argos if Language Package Exists + if Path(f"{PATH}/translator/argos_models/translate-{from_code}_{to_code}.argosmodel").exists(): + translatedText = argostranslate.translate.translate(text, from_code, to_code) + return translatedText + # Use Marian if Language Package Exists in Marian but not Argos + elif Path(f"{PATH}/translator/marian_models/opus-mt-{from_code}-{to_code}").exists(): + marian = MarianModel(from_code, to_code) + translatedText = marian.translate([text]) + return translatedText[0] + # Use Argos "English in the Middle" if not in Argos and Marian by Default + elif (Path(f"{PATH}/translator/argos_models/translate-{from_code}_en.argosmodel").exists() and \ + Path(f"{PATH}/translator/argos_models/translate-{to_code}_en.argosmodel").exists()) or \ + (Path(f"{PATH}/translator/argos_models/translate-en_{from_code}.argosmodel").exists() and \ + Path(f"{PATH}/translator/argos_models/translate-en_{to_code}.argosmodel").exists()): + translatedText = argostranslate.translate.translate(text, from_code, to_code) + return translatedText + # If a package doesn't exist + else: + return "Translation Unavailable" def get_response(self): return json.dumps( @@ -80,6 +96,18 @@ def get_response(self): ensure_ascii=False ) + def do_GET(self): + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.end_headers() + self.wfile.write(self.get_response().encode("utf-8")) + + + def do_POST(self): + self.send_response(200) + self.send_header("Content-Type", "application/json") + self.end_headers() + self.wfile.write(self.get_response().encode("utf-8")) def port_open(port): #connect_ex returns 0 if it connects to a socket meaning port is closed From 7e5b0cf155506f0b716ad92de471dc33254d32cc Mon Sep 17 00:00:00 2001 From: mhayes2772 Date: Thu, 23 May 2024 10:08:09 -0700 Subject: [PATCH 4/4] TranslationServer and server fixes, TranslationServer tests --- .../util/translation/TranslationServer.java | 123 +++++++++--------- public/js/medical/medical.js | 1 - .../services/TranslationServiceTest.java | 71 ++++++++-- translator/server.py | 17 +-- 4 files changed, 132 insertions(+), 80 deletions(-) diff --git a/app/femr/util/translation/TranslationServer.java b/app/femr/util/translation/TranslationServer.java index 94f4f292a..3bb04972c 100644 --- a/app/femr/util/translation/TranslationServer.java +++ b/app/femr/util/translation/TranslationServer.java @@ -4,47 +4,78 @@ import javax.inject.Singleton; import java.io.*; import java.net.HttpURLConnection; -import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; import java.util.Scanner; -import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @Singleton public class TranslationServer { @Inject public TranslationServer(){ - this.start(); + start(timeout); } private static int portNumber = -1; + private static final String timeout = "600"; - //Takes a string, a from code and a to code, and returns the translatedtext - public static String makeServerRequest(String text, String from, String to) throws MalformedURLException { - if(serverNotRunning()){ - start(); - while(serverNotRunning()); //block + public static int getPortFromLog(String logPath, Boolean wait){ + int portNumber = 0; + try{ + File log = new File(logPath); + Scanner s = new Scanner(log); + if(wait){ + while(log.length() == 0); + } + if (s.hasNext()) { + portNumber = Integer.parseInt(s.nextLine().split(": ")[1]); + } + s.close(); + } + catch(FileNotFoundException e){ + return portNumber; + } + catch(Exception e){ + System.out.println("Problem retrieving port number from log"); + throw new RuntimeException(e); + } + return portNumber; + } + public static boolean serverNotRunning(String logPath){ + //initial value of portNumber + if(portNumber == -1){ + portNumber = getPortFromLog(logPath, false); + if(portNumber == 0){ + return true; + } } - //Build GET request argument, replacing spaces and newlines - String response = ""; + try{ + final URL url = new URL("http://localhost:" + portNumber); + final URLConnection conn = url.openConnection(); + conn.connect(); + conn.getInputStream().close(); + return false; + } catch (IOException e){ + return true; + } + } + public static String makeServerRequest(String jsonString, String from, String to) { + String logPath = "translator/server.log"; + if(serverNotRunning(logPath)){ + start(timeout); + while(serverNotRunning(logPath)); //block + } + + String response; try { - // Harrison Shu - // Encode the URL String parameter before creating URL to allow arabic and hebrew to be in the URL -// String encodedText = URLEncoder.encode(text, StandardCharsets.UTF_8.toString()); -// System.out.println(text); - byte[] json = text.getBytes(StandardCharsets.UTF_8); + byte[] json = jsonString.getBytes(StandardCharsets.UTF_8); int length = json.length; - //Make GET request + //Make POST request URL url = new URL("http://localhost:" + portNumber + "/?from=" + from + "&to=" + to); HttpURLConnection con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("POST"); con.setDoOutput(true); - con.setFixedLengthStreamingMode(length); con.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); con.connect(); @@ -62,63 +93,29 @@ public static String makeServerRequest(String text, String from, String to) thro } catch(IOException e){ System.out.println(e.getMessage()); return "Translation Unavailable"; -// return makeServerRequest(text, from, to); } return response; } - public static boolean serverNotRunning(){ - //initial value of portNumber - if(portNumber == -1){ - File log = new File("translator/server.log"); - try { - Scanner s = new Scanner(log); - if(s.hasNext()){ - portNumber = Integer.parseInt(s.nextLine().split(": ")[1]); - } - else{ - return true; - } - } catch (FileNotFoundException e) { - throw new RuntimeException(e); - } - } - try{ - final URL url = new URL("http://localhost:" + portNumber); - final URLConnection conn = url.openConnection(); - conn.connect(); - conn.getInputStream().close(); - return false; - } catch (IOException e){ - return true; - } - } - public static void start(){ + public static void start(String timeout) { System.out.println("Starting translation server..."); - if(serverNotRunning()){ - File log = new File("translator/server.log"); - String absPath = "translator/server.py"; + String logPath = "translator/server.log"; + String absPath = "translator/server.py"; + if(serverNotRunning(logPath)){ + File log = new File(logPath); try { - ProcessBuilder pb = new ProcessBuilder("python", absPath); + log.createNewFile(); + ProcessBuilder pb = new ProcessBuilder("python", absPath, timeout); pb.redirectOutput(log); pb.redirectErrorStream(true); pb.start(); - } catch (IOException e) { System.out.println("An I/O error has occurred."); System.out.println(e.getMessage()); } - try { - Scanner s = new Scanner(log); - //Wait for server.log to be written to (port number) - while(log.length() == 0); - portNumber = Integer.parseInt(s.nextLine().split(": ")[1]); - s.close(); - } catch (FileNotFoundException e) { - System.out.println("A FileNotFound error has occurred."); - System.out.println(e.getMessage()); - } + portNumber = getPortFromLog(logPath, true); + } System.out.println("Translation server running!"); } diff --git a/public/js/medical/medical.js b/public/js/medical/medical.js index 1538fa7e2..0568f5073 100644 --- a/public/js/medical/medical.js +++ b/public/js/medical/medical.js @@ -339,7 +339,6 @@ $(document).ready(function () { textToTranslate = textToTranslate + " @ " + $(jsonObj[i].id).val().replace("@","at"); jsonObj[i].text = $(jsonObj[i].id).val(); } - console.log("text:", textToTranslate); console.log(jsonObj); diff --git a/test/unit/app/femr/business/services/TranslationServiceTest.java b/test/unit/app/femr/business/services/TranslationServiceTest.java index d7e6251c2..b9cac5a87 100644 --- a/test/unit/app/femr/business/services/TranslationServiceTest.java +++ b/test/unit/app/femr/business/services/TranslationServiceTest.java @@ -2,22 +2,20 @@ import controllers.AssetsFinder; import femr.business.services.core.*; -import femr.business.services.system.UserService; -import femr.common.IItemModelMapper; -import femr.data.IDataModelMapper; -import femr.data.daos.core.IUserRepository; -import femr.ui.controllers.helpers.FieldHelper; -import femr.util.encryptions.IPasswordEncryptor; import femr.ui.controllers.MedicalController; +import femr.util.translation.TranslationServer; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import play.data.FormFactory; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + import static org.mockito.Mockito.mock; public class TranslationServiceTest { - AssetsFinder assetsFinder; FormFactory formFactory; ITabService tabService; @@ -52,9 +50,66 @@ public void setUp(){ vitalService); } + //MedicalController Tests @Test - public void parseJsonResponse() { + public void parseJsonResponseTest() { String data = "{\"translate_data\" : \"Hello, World\"}"; Assert.assertEquals("Hello, World", medicalController.parseJsonResponse(data)); } + + // TranslationServer Tests + @Test + public void getPortFromLogNoFileTest(){ + String logPath = "test/server.log"; + Assert.assertEquals(0, TranslationServer.getPortFromLog(logPath, false)); + } + + @Test + public void getPortFromLogEmptyFileTest() throws IOException { + String logPath = "test/server.log"; + File log = new File(logPath); + log.createNewFile(); + Assert.assertEquals(0, TranslationServer.getPortFromLog(logPath, false)); + log.delete(); + } + + @Test + public void getPortFromLogTest() throws IOException { + String logPath = "test/server.log"; + File log = new File(logPath); + log.createNewFile(); + FileWriter writer = new FileWriter(logPath); + writer.write("Serving at port: 8000"); + writer.close(); + Assert.assertEquals(8000, TranslationServer.getPortFromLog(logPath, false)); + log.delete(); + } + + @Test + public void serverNotRunningTrue() throws IOException { + String logPath = "test/server.log"; + File log = new File(logPath); + log.createNewFile(); + FileWriter writer = new FileWriter(logPath); + writer.write("Serving at port: 8000"); + writer.close(); + Assert.assertTrue(TranslationServer.serverNotRunning("test/server.log")); + log.delete(); + } + + @Test + public void serverNotRunningFalse() { + TranslationServer.start("10"); + Assert.assertFalse(TranslationServer.serverNotRunning("translator/server.log")); + } + @Test + public void makeServerRequestTest() { + TranslationServer.start("10"); + String jsonString = "[{\"id\":\"#complaintInfo\", \"text\":\"Hello, World\"}, " + + "{\"id\":\"#onset\", \"text\":\"Hello, World\"}]"; + String outputString = + "{\"translate_data\": \"[{\\\"id\\\": \\\"#complaintInfo\\\", \\\"text\\\": \\\"Hola, Mundo\\\"}, " + + "{\\\"id\\\": \\\"#onset\\\", \\\"text\\\": \\\"Hola, Mundo\\\"}]\"}"; + Assert.assertEquals(outputString, TranslationServer.makeServerRequest(jsonString, "en", "es")); + } } diff --git a/translator/server.py b/translator/server.py index 163de8ac7..e755d8506 100644 --- a/translator/server.py +++ b/translator/server.py @@ -15,11 +15,9 @@ import time PORTS = [8000, 5000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8008] -TIMEOUT = 3600 +TIMEOUT = int(sys.argv[1]) PATH = os.getcwd() - - class MarianModel: def __init__(self, source_lang: str, dest_lang: str) -> None: path = f"{PATH}/translator/marian_models/opus-mt-{source_lang}-{dest_lang}" @@ -55,16 +53,20 @@ def translate_data(self): if(i == 0): translate_string = tab_list[i]["text"] else: - translate_string = translate_string + " @ " + tab_list[i]["text"].replace("@", "at") + if(tab_list[i]["text"] != ""): + translate_string = translate_string + " @ " + tab_list[i]["text"].replace("@", "at") #translate string and split into list on delimiter translated_text = self.translate(translate_string, from_code, to_code) - translated_list = list(translated_text.split("@")) + translated_list = list(translated_text.split(" @ ")) #for each translation, place text in copy of tab_list to return out = tab_list - for i in range(len(translated_list)): - out[i]["text"] = translated_list[i] + for i in range(len(out)): + if(i < len(translated_list)): + out[i]["text"] = translated_list[i] + else: + out[i]["text"] = "" return json.dumps(out) def translate(self, text, from_code, to_code): @@ -102,7 +104,6 @@ def do_GET(self): self.end_headers() self.wfile.write(self.get_response().encode("utf-8")) - def do_POST(self): self.send_response(200) self.send_header("Content-Type", "application/json")