From 96fcf80ea0bec457e7adffa2c6c8f2b519c3cfcb Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Sun, 23 Jun 2024 13:29:14 +0200 Subject: [PATCH] Spanish translations --- docs/cases/PMID_28183707.txt | 36 ++ docs/cases/PMID_28503313.txt | 24 + pom.xml | 2 +- .../PpktPhenotypicFeatureGenerator.java | 2 +- .../impl/german/GermanPromptGenerator.java | 8 +- .../german/PpktPhenotypicfeatureGerman.java | 37 +- .../impl/spanish/PpktIndividualSpanish.java | 528 ++++++------------ .../spanish/PpktPhenotypicfeatureSpanish.java | 78 ++- .../impl/spanish/SpanishBuildingBlocks.java | 291 ++++++++++ .../impl/spanish/SpanishPromptGenerator.java | 34 ++ 10 files changed, 655 insertions(+), 385 deletions(-) create mode 100644 docs/cases/PMID_28183707.txt create mode 100644 docs/cases/PMID_28503313.txt create mode 100644 src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/SpanishBuildingBlocks.java diff --git a/docs/cases/PMID_28183707.txt b/docs/cases/PMID_28183707.txt new file mode 100644 index 0000000..2740ed7 --- /dev/null +++ b/docs/cases/PMID_28183707.txt @@ -0,0 +1,36 @@ +[source] +pmid = PMID:28183707 +title = Chédiak-Higashi syndrome with novel gene mutation +[diagnosis] +disease_id = OMIM:214500 +disease_label = Chediak-Higashi syndrome +[text] +A boy aged 2½ years was admitted with right-sided pneumonia, which did not improve on antibiotics. Prior to admission, +the child had a history of fever and poor appetite of 2 weeks duration. He had no cough, sweating, vomiting, diarrhoea or urinary symptoms. +The child had a history of recurrent wheezing which was managed by nebulised bronchodilators. Moreover, there was a history of skin +discolouration and weight loss for the past 8 months. The child was under treatment for infantile eczema. One year back, +he had scalp abscess, which was resolved by oral antibiotics. The child was born term by Lower segment Caesarean section (LSCS) +due to previous LSCS and Placenta Previa. His mother had gestational diabetes. He had an uneventful prenatal, natal and postnatal +history. His birth weight was 3kg. He was born to consanguineous parents; his parents are cousins. He has two healthy +elder brothers and a sister. His vaccination schedule was up to date. There was a history of contact with a caregiver who had old treated tuberculosis. +On examination, he was afebrile with normal vital signs. His general condition was good apart from pallor with good nutritional status. +No lymph nodes were palpable. He has greyish short sparse hair. There was a hyperpigmented area at the forehead with other +hypopigmented patches on the face, behind the ear (figure 1A), at the trunk, the scapular area (figure 1B), the dorsum of the hand (figure 1C), +and extremities, especially the dorsum of the foot and upper inner thighs (figure 1D). The scrotum, flexural areas and creases were spared. +There was no joint swelling or tenderness. His weight was between the 3rd and 10th centile, and his height was between the 50th and 75th centile. +Systemic examination was normal apart from reduced air entry on the right side, mainly the posterior lower zone of the chest. +There was no hepatosplenomegaly or bleeding tendency and nor was there neurological or developmental impairment. +Preliminary investigation revealed anaemia with an elevated acute phase reactant. The relevant haematological findings were haemoglobin of +8.8g %, and total white cell count was within the normal range of a healthy population with no leucopenia, neutropenia or lymphopenia. +The total leucocyte count was 11900/μL and the platelet count 531000/μL. Differential leucocyte counts showed 60% lymphocytes, 26% neutrophils, +10% monocytes and eosinophils 4%. C reactive protein: 12mg/L. Serum ferritin: 14.1ng/mL (N: 9.29–58.7). +T-Spot Tuberculosis test: non-reactive. Acid-fast bacilli (AFB) Smear only: AFB not seen. AFB culture and sensitivity (gastric aspirate): not seen. +CXR showed right middle lobe consolidation. Repeated chest X-ray after 1week revealed patchy opacities, noted in the right lung and left lower lobe, +suggestive of progressive pneumonic consolidation. +Immune status screen showed a non-specific reactive picture: CD4,8 RATIO 1.729% (normal range (N) 1.20–2.40), +TOTAL T CELLS 67.7% (N:62.0–72.0), CD3 ABS 6119.0 CELL/μL (N:812–2318), CD4% 37.7% (N:35.0–55.0), CD4 ABS 3408.0 CELL/μL (N:589–1505) +CD8% 21.8% (N:16.0–28.0), CD8 ABS 1971.0 CELL/μL (N:325–995), CD56% 2.3% (N:5.00–13.0), CD56 ABS 208.0 CELL/μL (N:78–602, CD19% 21.6% (N:12.0–28.0) +CD19 ABS 1952.0 CELL/μL (N: 99–426). +Immunoglobulins assay showed elevated immunoglobulin E (IgE) 2680.0KU/L (N: 0–64), IGG 21.80g/L (N: 4.07–10.09), IGA 2.01g/L (N: 0.14–1.22), +IGM 1.94g/L (N:0.46–1.60). +Liver and renal function tests were normal. \ No newline at end of file diff --git a/docs/cases/PMID_28503313.txt b/docs/cases/PMID_28503313.txt new file mode 100644 index 0000000..4ec9983 --- /dev/null +++ b/docs/cases/PMID_28503313.txt @@ -0,0 +1,24 @@ +[source] +pmid = PMID:28503313 +title = Novel DHCR7 mutation in a case of Smith-Lemli-Opitz syndrome showing 46,XY disorder of sex development +[diagnosis] +disease_id = OMIM:270400 +disease_label = Smith-Lemli-Opitz syndrome +[text] +The patient was born at 38 weeks of gestation to non-consanguineous Japanese parents. Birth weight, length and head +circumference were 2,59Z g (−0.92 s.d.), 45 cm (−1.7 s.d.) and 33 cm (−0.1 s.d.), respectively. +Ambiguous genitalia, low set ears, a short nose and Y-shaped 2–3 toe syndactyly were detected at birth (Figure 1a and b). +Examination of the patient’s genitalia revealed a micropenis (stretch length: 1.5 cm), +left lateral testicular hypoplasia (right and left diameters: 10 and 5 mm, respectively), hypospadias and a bifid scrotum (Figure 1c). +Echography and magnetic resonance imaging failed to detect the presence of a uterus or ovaries. +Blood examination revealed a relatively low total cholesterol (T. Chol) level (46 mg/dl) (reference: 129–232), +normal testosterone and slightly low anti-mullerian hormone level for males. +Urine steroid profile was normal. +G-banding analysis confirmed a normal 46,XY male karyotype. From these results, the patient was recorded in public office documents as male. +We performed cholesterol analysis with high-performance liquid chromatography. +This analysis revealed relative hypocholesterolemia and high 7-DHC and 8-dehydrocholesterol levels in the patient and normal levels +(undetectable) in his parents (Table 1). T. +Chol/7-DHC was significantly lower in the patient than in his parents. +Therefore, he was biochemically diagnosed with SLOS. +The patient also presented with allergies for milk and eggs, and eczema throughout his body. +His eczema, considered to be atopic dermatitis, was treated with corticosteroid ointment. \ No newline at end of file diff --git a/pom.xml b/pom.xml index 06d37e6..3fbacba 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.monarchinitiative phenopacket2prompt - 0.4.4 + 0.4.6 phenopacket2prompt https://github.com/monarch-initiative/phenopacket2prompt diff --git a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/PpktPhenotypicFeatureGenerator.java b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/PpktPhenotypicFeatureGenerator.java index e5dd348..da9d47e 100644 --- a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/PpktPhenotypicFeatureGenerator.java +++ b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/PpktPhenotypicFeatureGenerator.java @@ -10,7 +10,7 @@ public interface PpktPhenotypicFeatureGenerator { String formatFeatures(List ontologyTerms); - default String featuresAtEncounter(List ontologyTerms) { + default String featuresAtEncounter(String personString, String ageString, List ontologyTerms) { return ""; //TODO - implement and make this not default } diff --git a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/german/GermanPromptGenerator.java b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/german/GermanPromptGenerator.java index 5af55b7..44478d0 100644 --- a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/german/GermanPromptGenerator.java +++ b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/german/GermanPromptGenerator.java @@ -50,8 +50,12 @@ public String formatFeatures(List ontologyTerms) { @Override public String getVignetteAtAge(PhenopacketAge page, PhenopacketSex psex, List terms) { String ageString = this.ppktAgeSexGenerator.atAgeForVignette(page); - String features = formatFeatures(terms); - return String.format("%s, präsentierte %s mit den folgenden Symptomen: %s", ageString, ppktAgeSexGenerator.heSheIndividual(psex), features); + String person = switch (psex) { + case MALE -> "er"; + case FEMALE -> "sie"; + default -> "die betroffene Person"; + }; + return this.ppktPhenotypicFeatureGenerator.featuresAtEncounter(person, ageString, terms); } @Override diff --git a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/german/PpktPhenotypicfeatureGerman.java b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/german/PpktPhenotypicfeatureGerman.java index 4ac3cd7..8dcb4f3 100644 --- a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/german/PpktPhenotypicfeatureGerman.java +++ b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/german/PpktPhenotypicfeatureGerman.java @@ -1,5 +1,6 @@ package org.monarchinitiative.phenopacket2prompt.output.impl.german; +import org.monarchinitiative.phenol.base.PhenolRuntimeException; import org.monarchinitiative.phenopacket2prompt.international.HpInternational; import org.monarchinitiative.phenopacket2prompt.model.OntologyTerm; import org.monarchinitiative.phenopacket2prompt.output.PpktPhenotypicFeatureGenerator; @@ -82,13 +83,41 @@ public Set getMissingTranslations() { } + @Override + public String featuresAtEncounter(String personString, String ageString, List ontologyTerms) { + List observed = getObservedFeatures(ontologyTerms); + List excluded = getExcludedFeatures(ontologyTerms); + List observedGerman = getTranslations(observed); + List excludedGerman = getTranslations(excluded); + var observedStr = getCommaList(observedGerman); + var excludedStr = getCommaList(excludedGerman); + if (!observed.isEmpty() && ! excluded.isEmpty()) { + return String.format("%s präsentierte %s mit den folgenden Symptomen: %s. Im Gegensatz %s die folgenden Symptome ausgeschlossen: %s.", + ageString, + personString, + observedStr, + excluded.size()>1? "wurden":"wurde", + excludedStr); + } else if (!observed.isEmpty()) { + return String.format("%s präsentierte %s mit den folgenden Symptomen: %s.", ageString, personString, observedStr); + } else if (!excluded.isEmpty()) { + return String.format("%s %s die folgenden Symptome ausgeschlossen: %s.", + ageString, + excluded.size()>1? "wurden":"wurde", excludedStr); + } else { + throw new PhenolRuntimeException("No features found for time point " + ageString); // should never happen + } + } @Override public String featuresAtOnset(String personString, List ontologyTerms) { - List observed = getObservedFeaturesAsStr(ontologyTerms); - List excluded = getExcludedFeaturesAsStr(ontologyTerms); - var observedStr = getCommaList(observed); - var excludedStr = getCommaList(excluded); + List observed = getObservedFeatures(ontologyTerms); + List excluded = getExcludedFeatures(ontologyTerms); + List observedGerman = getTranslations(observed); + List excludedGerman = getTranslations(excluded); + var observedStr = getCommaList(observedGerman); + var excludedStr = getCommaList(excludedGerman); + if (!observed.isEmpty() && ! excluded.isEmpty()) { return String.format("%s präsentierte mit den folgenden Symptomen: %s. Im Gegensatz %s die folgenden Symptome ausgeschlossen: %s.", personString, diff --git a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/PpktIndividualSpanish.java b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/PpktIndividualSpanish.java index 23f4422..ee133ec 100644 --- a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/PpktIndividualSpanish.java +++ b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/PpktIndividualSpanish.java @@ -2,6 +2,7 @@ import org.monarchinitiative.phenol.base.PhenolRuntimeException; import org.monarchinitiative.phenopacket2prompt.model.*; +import org.monarchinitiative.phenopacket2prompt.output.BuildingBlockGenerator; import org.monarchinitiative.phenopacket2prompt.output.PPKtIndividualInfoGenerator; import java.util.ArrayList; @@ -10,117 +11,135 @@ public class PpktIndividualSpanish implements PPKtIndividualInfoGenerator { - //TODO translate from ita to spanish and edit this file in order to actually use these - private static final String FEMALE_FETUS = "un feto femenino"; - private static final String MALE_FETUS = "un feto masculino"; - private static final String FETUS = "un feto"; - private static final String FEMALE_NEWBORN = "una niña recién nacida"; // CHECK - private static final String MALE_NEWBORN = "un neonato maschio"; - private static final String NEWBORN = "un neonato"; - private static final String FEMALE_INFANT = "un bebé femenino"; - private static final String MALE_INFANT = "un bebé masculino"; - private static final String INFANT = "un bebé"; + private final BuildingBlockGenerator bbGenerator; - private static final String FEMALE_CHILD = "una niña"; - private static final String MALE_CHILD = "un niño"; - private static final String CHILD = "un niño"; + public PpktIndividualSpanish() { + this.bbGenerator = new SpanishBuildingBlocks(); + } - private static final String FEMALE_ADOLESCENT = "un'adolescente femmina"; - private static final String MALE_ADOLESCENT = "un adolescente maschio"; - private static final String ADOLESCENT = "un adolescente"; - private static final String FEMALE_ADULT = "una donna"; - private static final String MALE_ADULT = "un uomo"; - private static final String ADULT = "una persona adulta"; + @Override + public String getIndividualDescription(PpktIndividual individual) { + if (individual.annotationCount() == 0) { + throw new PhenolRuntimeException("No HPO annotations"); + } + Optional lastExamOpt = individual.getAgeAtLastExamination(); + Optional onsetOpt = individual.getAgeAtOnset(); + PhenopacketSex psex = individual.getSex(); + String individualDescription; + String onsetDescription; + if (lastExamOpt.isPresent()) { + var lastExamAge = lastExamOpt.get(); + if (lastExamAge.ageType().equals(PhenopacketAgeType.ISO8601_AGE_TYPE)) { + Iso8601Age isoAge = (Iso8601Age) lastExamAge; + individualDescription = iso8601individualDescription(psex, isoAge); + } else if (lastExamAge.ageType().equals(PhenopacketAgeType.HPO_ONSET_AGE_TYPE)) { + HpoOnsetAge hpoOnsetTermAge = (HpoOnsetAge) lastExamAge; + individualDescription = hpoOnsetIndividualDescription(psex, hpoOnsetTermAge); + } else { + // should never happen + throw new PhenolRuntimeException("Did not recognize last exam age type " + lastExamAge.ageType()); + } + } else { + individualDescription = switch (psex) { + case FEMALE -> individualDescription = "La paciente era de sexo femenino de edad no especificada"; + case MALE -> individualDescription = "El paciente era de sexo masculino de edad no especificada"; + default -> individualDescription = "El paciente era una persona de sexo y edad no especificados"; + }; + } + if (onsetOpt.isPresent()) { + var onsetAge = onsetOpt.get(); + if (onsetAge.ageType().equals(PhenopacketAgeType.ISO8601_AGE_TYPE)) { + Iso8601Age isoAge = (Iso8601Age) onsetAge; + onsetDescription = iso8601onsetDescription(isoAge); + } else if (onsetAge.ageType().equals(PhenopacketAgeType.HPO_ONSET_AGE_TYPE)) { + HpoOnsetAge hpoOnsetTermAge = (HpoOnsetAge) onsetAge; + onsetDescription = hpoOnsetDescription(hpoOnsetTermAge, psex); + } else { + // should never happen + throw new PhenolRuntimeException("Did not recognize last exam age type " + onsetAge.ageType()); + } + } else { + onsetDescription = "No se indicó la edad del inicio de la enfermedad"; + } + return String.format("%s. %s.", individualDescription, onsetDescription); + } - /** - * Equivalent of "The clinical - * @param individual - * @return - */ - public String ageAndSexAtOnset(PpktIndividual individual) { - Optional ageOpt = individual.getAgeAtOnset(); - return ""; + private String hpoOnsetDescription(HpoOnsetAge hpoOnsetTermAge, PhenopacketSex psex) { + return String.format("El inicio de la enfermedad ocurrió %s", + nameOfLifeStage(hpoOnsetTermAge, psex)); } + private String nameOfLifeStage(HpoOnsetAge hpoOnsetTermAge, PhenopacketSex psex) { + if (hpoOnsetTermAge.isFetus()) { + return "durante el período fetal"; + } else if (hpoOnsetTermAge.isCongenital()) { + return "en el momento del nacimiento"; + } else if (hpoOnsetTermAge.isInfant()) { + return "en la infancia temprana"; + } else if (hpoOnsetTermAge.isChild()) { + return "en la niñez"; + } else if (hpoOnsetTermAge.isJuvenile()) { + return "en la adolescencia"; + } else if (hpoOnsetTermAge.isNeonate()) { + if (psex.equals(PhenopacketSex.FEMALE)) { + return "como recién nacida"; + } else { + return "como recién nacido"; + } + } else if (hpoOnsetTermAge.isYoungAdult()) { + return "en la edad joven adulta" ; + } else if (hpoOnsetTermAge.isMiddleAge()) { + return "en la mediana edad" ; + } else if (hpoOnsetTermAge.isLateAdultAge()) { + return "en la edad adulta avanzada" ; + } else if (hpoOnsetTermAge.isAdult()) { + // d.h. nicht weiter spezifiziert + return "en la edad adulta" ; + } else { + throw new PhenolRuntimeException("Could not identify Spanish life stage name for HpoOnsetAge " + hpoOnsetTermAge.toString()); + } + } + private String ymd(Iso8601Age iso8601Age) { + int y = iso8601Age.getYears(); + int m = iso8601Age.getMonths(); + int d = iso8601Age.getDays(); - public String ageAndSexAtLastExamination(PpktIndividual individual) { - PhenopacketSex psex = individual.getSex(); - Optional ageOpt = individual.getAgeAtLastExamination(); - if (ageOpt.isEmpty()) { - ageOpt = individual.getAgeAtOnset(); + List components = new ArrayList<>(); + if (y > 0) { + components.add(String.format("%d %s", y, y > 1 ? "años" : "año")); } - String sex; - switch (psex) { - case FEMALE -> sex = "una paciente femenina"; - case MALE -> sex = "un paciente masculino"; - default -> sex = "una persona"; - }; - - if (ageOpt.isEmpty()) { - return sex; + if (m > 0) { + components.add(String.format("%d %s", m, m > 1 ? "meses" : "mes")); } - PhenopacketAge age = ageOpt.get(); - if (age.ageType().equals(PhenopacketAgeType.ISO8601_AGE_TYPE)) { - Iso8601Age isoage = (Iso8601Age) age; - int y = isoage.getYears(); - int m = isoage.getMonths(); - int d = isoage.getDays(); - if (psex.equals(PhenopacketSex.FEMALE)) { - if (y > 17) { - return String.format("una mujer de %d años", y); - } else if (y > 9) { - return String.format("una adolescente de %d años", y); - - } else if (y > 0) { - return String.format("una niña de %d años", y); - } else if (m > 0) { - return String.format("una bebe niña de %d meses", m); - } else { - return String.format("una recien nacida de %d dias de edad", d); - } - } - } else { - // age is an HPO onset term, we do not have an exact date + if (d > 0) { + components.add(String.format("%d %s", d, d > 1 ? "días" : "día")); } - if (age.isChild()) { - return switch (psex) { - case FEMALE -> "una niña"; - case MALE -> "un niño"; - default -> "un niño"; // difficult to be gender neutral - }; - } else if (age.isCongenital()) { - return switch (psex) { - case FEMALE -> "una recien nacida"; - case MALE -> "un recien nacido"; - default -> "un recien nacido"; - }; - } else if (age.isFetus()) { - return switch (psex) { - case FEMALE -> FEMALE_FETUS; - case MALE -> MALE_FETUS; - default -> FETUS; - }; - } else if (age.isInfant()) { - return switch (psex) { - case FEMALE -> FEMALE_INFANT; - case MALE -> MALE_INFANT; - default -> INFANT; - }; + if (components.isEmpty()) { + return "en el primer día de vida"; + } else if (components.size() == 1) { + return components.get(0); + } else if (components.size() == 2) { + return String.format("en la edad de %s y %s", components.get(0), components.get(1)); } else { - return switch (psex) { - case FEMALE -> "una mujer"; - case MALE -> "un hombre"; - default -> "una persona adulta"; - }; + // we must have y,m,d + return String.format("en la edad de %s, %s y %s", components.get(0), components.get(1), components.get(2)); } } + private String iso8601onsetDescription(Iso8601Age isoAge) { + return String.format("El inicio de la enfermedad ocurrió a los %s de edad", ymd(isoAge)); + } + + + + + private String atIsoAgeExact(PhenopacketAge ppktAge) { Iso8601Age iso8601Age = (Iso8601Age) ppktAge; @@ -146,98 +165,6 @@ private String atIsoAgeExact(PhenopacketAge ppktAge) { } - @Override - public String getIndividualDescription(PpktIndividual individual) { - if (individual.annotationCount() == 0) { - throw new PhenolRuntimeException("No HPO annotations"); - } - Optional lastExamOpt = individual.getAgeAtLastExamination(); - Optional onsetOpt = individual.getAgeAtOnset(); - PhenopacketSex psex = individual.getSex(); - if (lastExamOpt.isPresent() && onsetOpt.isPresent()) { - return onsetAndLastEncounterAvailable(psex, lastExamOpt.get(), onsetOpt.get()); - } else if (lastExamOpt.isPresent()) { - return lastEncounterAvailable(psex, lastExamOpt.get()); - } else if (onsetOpt.isPresent()) { - return onsetAvailable(psex, onsetOpt.get()); - } else { - return ageNotAvailable(psex); - } - } - - - private String iso8601ToYearMonth(Iso8601Age iso8601Age) { - if (iso8601Age.getMonths() == 0) { - return String.format("de %d años", iso8601Age.getYears()); - } else { - return String.format("de %d años y %d meses", iso8601Age.getYears(), iso8601Age.getMonths()); - } - } - - private String iso8601ToMonthDay(Iso8601Age iso8601Age) { - int m = iso8601Age.getMonths(); - int d = iso8601Age.getDays(); - if (m == 0) { - return String.format("de %d dias", d); - } else if (d>0){ - return String.format("de %d meses y %d dias", m, d); - } else { - return String.format("de %d meses", m); - } - } - - /** - * Create a phrase such as "at the age of 7 years, 4 months, and 2 days" - * Leave out the months and days if they are zero. - * @param isoAge - * @return - */ - private String iso8601AtAgeOf(Iso8601Age isoAge) { - List components = new ArrayList<>(); - - if (isoAge.getYears()>1) { - components.add(String.format("%d años", isoAge.getYears())); - } else if (isoAge.getYears() == 1) { - components.add("1 año"); - } - if (isoAge.getMonths() > 1) { - components.add(String.format("%d meses", isoAge.getMonths())); - } else if (isoAge.getMonths() == 1) { - components.add("1 mes"); - } - if (isoAge.getDays()>1) { - components.add(String.format("%d dias", isoAge.getDays())); - } else if (isoAge.getDays()==1) { - components.add("1 dia"); - } - if (components.isEmpty()) { - return "en el período neonatal"; - } else if (components.size() == 1) { - return "a la edad de " + components.getFirst(); - } else if (components.size() == 2) { - return "a la edad de " + components.get(0) + " y " + components.get(1); - } else { - return "a la edad de " + components.get(0) + ", " + components.get(1) + - " y " + components.get(2); - } - } - - private String onsetTermAtAgeOf(HpoOnsetAge hpoOnsetTermAge, PhenopacketSex psex) { - if (hpoOnsetTermAge.isFetus()) { - return "en el período fetal"; - } else if (hpoOnsetTermAge.isCongenital()) { - return "al nacer"; - } else if (hpoOnsetTermAge.isInfant()) { - return "en el primer año de vida"; - } else if (hpoOnsetTermAge.isChild()) { - return "en la niñez"; - } else if (hpoOnsetTermAge.isJuvenile()) { - return "como adolescente"; - } else { - return "en la edad adulta"; - } - } - private String iso8601individualDescription(PhenopacketSex psex, Iso8601Age iso8601Age) { int y = iso8601Age.getYears(); @@ -246,34 +173,34 @@ private String iso8601individualDescription(PhenopacketSex psex, Iso8601Age iso8 // if older if (y>17) { return switch (psex) { - case FEMALE -> String.format("una mujer de %d años", y); - case MALE -> String.format("un hombre de %d años", y); - default -> String.format("una persona de %d años", y); + case FEMALE -> String.format("La paciente era una mujer de %d años", y); + case MALE -> String.format("El paciente era un hombre de %d años", y); + default -> String.format("El paciente era una persona de %d años", y); }; } else if (y>9) { return switch (psex) { - case FEMALE -> String.format("una adolescente de %d años", y); - case MALE -> String.format("un adolescente de %d años", y); - default -> String.format("un adolescente de %d años", y); + case FEMALE -> String.format("La paciente era una adolescente de %d años", y); + case MALE -> String.format("El paciente era un adolescente de %d años", y); + default -> String.format("El paciente era un adolescente de %d años", y); }; } else if (y>0) { return switch (psex) { - case FEMALE -> String.format("una niña %s", iso8601ToYearMonth(iso8601Age)); - case MALE -> String.format("un niño %s", iso8601ToYearMonth(iso8601Age)); - default -> String.format("un niño %s", iso8601ToYearMonth(iso8601Age)); + case FEMALE -> String.format("La paciente una niña %s", ymd(iso8601Age)); + case MALE -> String.format("El paciente era un niño %s", ymd(iso8601Age)); + default -> String.format("El paciente era un niño %s", ymd(iso8601Age)); }; } else if (m>0 || d> 0) { return switch (psex) { - // note that in Spanishm infante is up to 5 years - case FEMALE -> String.format("una bebé %s", iso8601ToMonthDay(iso8601Age)); - case MALE -> String.format("un bebé %s", iso8601ToMonthDay(iso8601Age)); - default -> String.format("un bebé %s", iso8601ToMonthDay(iso8601Age)); + // note that in Spanish infante is up to 5 years + case FEMALE -> String.format("La paciente era una bebé %s", ymd(iso8601Age)); + case MALE -> String.format("El paciente era un bebé %s", ymd(iso8601Age)); + default -> String.format("El paciente era un bebé %s", ymd(iso8601Age)); }; } else { return switch (psex) { - case FEMALE -> "recien nacida"; - case MALE -> "recien nacido"; - default -> "recien nacido"; + case FEMALE -> "La paciente era una recien nacida"; + case MALE -> "El paciente era un recien nacido"; + default -> "El paciente era un recien nacido"; }; } } @@ -281,140 +208,63 @@ private String iso8601individualDescription(PhenopacketSex psex, Iso8601Age iso8 private String hpoOnsetIndividualDescription(PhenopacketSex psex, HpoOnsetAge hpoOnsetTermAge) { if (hpoOnsetTermAge.isFetus()) { return switch (psex) { - case FEMALE -> FEMALE_FETUS; - case MALE -> MALE_FETUS; - default -> FETUS; + case FEMALE -> "La paciente era un feto feminino"; + case MALE -> "El paciente era un feto masculino"; + default -> "El paciente era un feto"; }; } else if (hpoOnsetTermAge.isCongenital()) { return switch (psex) { - case FEMALE -> "una niña recién nacida"; - case MALE -> "un niño recién nacido"; - default -> "un bebe recién nacido"; + case FEMALE -> "La paciente era una niña recién nacida"; + case MALE -> "El paciente era un niño masculino recién nacido"; + default -> "El paciente era un bebe recién nacido"; }; } else if (hpoOnsetTermAge.isInfant()) { return switch (psex) { - case FEMALE -> FEMALE_INFANT; - case MALE -> MALE_INFANT; - default -> INFANT; + case FEMALE -> "La paciente era una bebé"; + case MALE -> "El paciente era un bebé"; + default -> "El paciente era un bebé"; }; } else if (hpoOnsetTermAge.isChild()) { return switch (psex) { - case FEMALE -> "niña"; - case MALE -> "niño"; - default -> "niño"; + case FEMALE -> "La paciente era una niña"; + case MALE -> "El paciente era un niño"; + default -> "El paciente era un niño"; }; } else if (hpoOnsetTermAge.isJuvenile()) { return switch (psex) { - case FEMALE -> "una adolescente femenina"; - case MALE -> "un adolescente masculino"; - default -> "un adolescente"; + case FEMALE -> "La paciente era una adolescente femenina"; + case MALE -> "El paciente era un adolescente masculino"; + default -> "El paciente era un adolescente"; }; - }else { + } else if (hpoOnsetTermAge.isMiddleAge()) { return switch (psex) { - case FEMALE -> "una mujer"; - case MALE -> "un hombre"; - default -> "un adulto"; + case FEMALE -> "La paciente era una mujer de mediana edad"; + case MALE -> "El paciente era un hombre de mediana edad"; + default -> "El paciente era un adulto de mediana edad"; + }; + } else if (hpoOnsetTermAge.isYoungAdult()) { + return switch (psex) { + case FEMALE -> "La paciente era una mujer de edad adulta joven"; + case MALE -> "El paciente era un hombre de edad adulta joven"; + default -> "El paciente era un adulto de edad adulta joven"; + }; + } else if (hpoOnsetTermAge.isLateAdultAge()) { + return switch (psex) { + case FEMALE -> "La paciente era una mujer de edad avanzada"; + case MALE -> "El paciente era un hombre de edad avanzada"; + default -> "El paciente era un adulto de edad avanzada"; + }; + } else if (hpoOnsetTermAge.isAdult()) { + return switch (psex) { + case FEMALE -> "La paciente era una mujer"; + case MALE -> "El paciente era un hombre"; + default -> "El paciente era un adulto"; }; - } - } - - /** - * A sentence such as The proband was a 39-year old woman who presented at the age of 12 years with - * HPO1, HPO2, and HPO3. HPO4 and HPO5 were excluded. This method returns the phrase that ends with "with" - * El sujeto era un niño de 1 año y 10 meses que se presentó como recién nacido con un filtrum largo. - * @param psex - * @param lastExamAge - * @param onsetAge - * @return - */ - private String onsetAndLastEncounterAvailable(PhenopacketSex psex, PhenopacketAge lastExamAge, PhenopacketAge onsetAge) { - String individualDescription; - String onsetDescription; - if (lastExamAge.ageType().equals(PhenopacketAgeType.ISO8601_AGE_TYPE)) { - Iso8601Age isoAge = (Iso8601Age) lastExamAge; - individualDescription = iso8601individualDescription(psex, isoAge); - } else if (lastExamAge.ageType().equals(PhenopacketAgeType.HPO_ONSET_AGE_TYPE)) { - HpoOnsetAge hpoOnsetTermAge = (HpoOnsetAge) lastExamAge; - individualDescription = hpoOnsetIndividualDescription(psex,hpoOnsetTermAge); - } else { - // should never happen - throw new PhenolRuntimeException("Did not recognize last exam age type " + lastExamAge.ageType()); - } - if (onsetAge.ageType().equals(PhenopacketAgeType.ISO8601_AGE_TYPE)) { - Iso8601Age isoAge = (Iso8601Age) onsetAge; - onsetDescription = iso8601AtAgeOf(isoAge); - } else if (onsetAge.ageType().equals(PhenopacketAgeType.HPO_ONSET_AGE_TYPE)) { - HpoOnsetAge hpoOnsetTermAge = (HpoOnsetAge) onsetAge; - onsetDescription = onsetTermAtAgeOf(hpoOnsetTermAge, psex); - } else { - // should never happen - throw new PhenolRuntimeException("Did not recognize onset age type " + onsetAge.ageType()); - } - return switch (psex){ - case FEMALE -> String.format("La paciente era %s que se presentó %s con", individualDescription, onsetDescription); - case MALE -> String.format("El paciente era %s que se presentó %s con", individualDescription, onsetDescription); - default -> String.format("El paciente era %s que se presentó %s con", individualDescription, onsetDescription); - }; - } - - - - - - /** - * Age at last examination available but age of onset not available - * The proband was a 39-year old woman who presented with HPO1, HPO2, and HPO3. HPO4 and HPO5 were excluded. - * @param psex - * @param lastExamAge - */ - private String lastEncounterAvailable(PhenopacketSex psex, PhenopacketAge lastExamAge) { - String individualDescription; - if (lastExamAge.ageType().equals(PhenopacketAgeType.ISO8601_AGE_TYPE)) { - Iso8601Age isoAge = (Iso8601Age) lastExamAge; - individualDescription = iso8601individualDescription(psex, isoAge); - } else if (lastExamAge.ageType().equals(PhenopacketAgeType.HPO_ONSET_AGE_TYPE)) { - HpoOnsetAge hpoOnsetTermAge = (HpoOnsetAge) lastExamAge; - individualDescription = hpoOnsetIndividualDescription(psex,hpoOnsetTermAge); - } else { - // should never happen - throw new PhenolRuntimeException("Did not recognize last exam age type " + lastExamAge.ageType()); - } - if (psex.equals(PhenopacketSex.FEMALE)) { - return String.format("La paciente era %s que se presentó con", individualDescription); - } else { - return String.format("El paciente era %s qui se presentó con", individualDescription); - } - } - - /** - * Age at last examination not available but age of onset available - * The proband presented at the age of 12 years with HPO1, HPO2, and HPO3. HPO4 and HPO5 were excluded. - * @param psex - * @param onsetAge - * @return - */ - private String onsetAvailable(PhenopacketSex psex, PhenopacketAge onsetAge) { - String onsetDescription; - if (onsetAge.ageType().equals(PhenopacketAgeType.ISO8601_AGE_TYPE)) { - Iso8601Age isoAge = (Iso8601Age) onsetAge; - onsetDescription = iso8601AtAgeOf(isoAge); - } else if (onsetAge.ageType().equals(PhenopacketAgeType.HPO_ONSET_AGE_TYPE)) { - HpoOnsetAge hpoOnsetTermAge = (HpoOnsetAge) onsetAge; - onsetDescription = onsetTermAtAgeOf(hpoOnsetTermAge, psex); } else { - // should never happen - throw new PhenolRuntimeException("Did not recognize onset age type " + onsetAge.ageType()); + throw new PhenolRuntimeException("Did not recognize Spanish HPO Onset term"); } - return String.format("El paciente se presentó %s con", onsetDescription); } - private String ageNotAvailable(PhenopacketSex psex) { - return switch (psex) { - case FEMALE -> "La paciente se presentó con"; - case MALE -> "El paciente se presentó con"; - default -> "El paciente se presentó con"; - }; - } @Override public String heSheIndividual(PhenopacketSex psex) { @@ -444,53 +294,5 @@ public String atAgeForVignette(PhenopacketAge ppktAge) { } } - // @Override - public String ppktSex(PpktIndividual individual) { - PhenopacketSex psex = individual.getSex(); - Optional ageOpt = individual.getAgeAtLastExamination(); - if (ageOpt.isEmpty()) { - ageOpt = individual.getAgeAtOnset(); - } - if (ageOpt.isEmpty()) { - return switch (psex) { - case FEMALE -> "mujer"; - case MALE -> "hombre"; - default -> "individuo"; - }; - } - PhenopacketAge age = ageOpt.get();; - if (age.isChild()) { - return switch (psex) { - case FEMALE -> "girl"; - case MALE -> "boy"; - default -> "child"; - }; - } else if (age.isCongenital()) { - return switch (psex) { - case FEMALE -> "female newborn"; - case MALE -> "male newborn"; - default -> "newborn"; - }; - } else if (age.isFetus()) { - return switch (psex) { - case FEMALE -> "female fetus"; - case MALE -> "male fetus"; - default -> "fetus"; - }; - } else if (age.isInfant()) { - return switch (psex) { - case FEMALE -> FEMALE_INFANT; - case MALE -> "un infante masculino"; - default -> "un infante"; - }; - } else { - return switch (psex) { - case FEMALE -> "mujer"; - case MALE -> "hombre"; - default -> "adulto"; - }; - } - } - } diff --git a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/PpktPhenotypicfeatureSpanish.java b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/PpktPhenotypicfeatureSpanish.java index 476ff09..62710ce 100644 --- a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/PpktPhenotypicfeatureSpanish.java +++ b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/PpktPhenotypicfeatureSpanish.java @@ -7,6 +7,7 @@ import java.util.*; import java.util.function.Predicate; +import java.util.stream.Collectors; public class PpktPhenotypicfeatureSpanish implements PpktPhenotypicFeatureGenerator { @@ -53,7 +54,10 @@ String getConnector(String nextWord) { } - private String getOxfordCommaList(List items) { + + + + private String getCommaList(List items) { if (items.size() == 1) { return items.getFirst(); } @@ -63,14 +67,12 @@ private String getOxfordCommaList(List items) { String connector = getConnector(items.get(1)); return String.join(connector, items); } - String symList = String.join(", ", items); - int jj = symList.lastIndexOf(", "); - if (jj > 0) { - String end = symList.substring(jj+2); - String connector = getConnector(end); - symList = symList.substring(0, jj) + connector + end; - } - return symList; + // if we have more than two, join all but the very last item with a comma + String penultimate = items.stream() + .limit(items.size() - 1) + .collect(Collectors.joining(",")); + String ultimate = items.get(items.size() - 1); + return penultimate + getConnector(ultimate) + ultimate; } @Override @@ -85,21 +87,21 @@ public String formatFeatures(List ontologyTerms) { if (observedLabels.isEmpty() && excludedLabels.isEmpty()) { throw new PhenolRuntimeException("No phenotypic abnormalities"); // should never happen, actually! } else if (excludedLabels.isEmpty()) { - return getOxfordCommaList(observedLabels) + ". "; + return getCommaList(observedLabels) + "."; } else if (observedLabels.isEmpty()) { if (excludedLabels.size() > 1) { - return String.format("se descartaron %s.", getOxfordCommaList(excludedLabels)); + return String.format("se descartaron %s.", getCommaList(excludedLabels)); } else { return String.format("se descartó %s.",excludedLabels.getFirst()); } } else { String exclusion; if (excludedLabels.size() == 1) { - exclusion = String.format(". En cambio, se descartó %s.", getOxfordCommaList(excludedLabels)); + exclusion = String.format(". En cambio, se descartó %s.", getCommaList(excludedLabels)); } else { - exclusion = String.format(". En cambio, se descartaron %s.", getOxfordCommaList(excludedLabels)); + exclusion = String.format(". En cambio, se descartaron %s.", getCommaList(excludedLabels)); } - return getOxfordCommaList(observedLabels) + exclusion; + return getCommaList(observedLabels) + exclusion; } } @@ -107,4 +109,52 @@ public Set getMissingTranslations() { return missingTranslations; } + @Override + public String featuresAtOnset(String personString, List ontologyTerms) { + List observed = getObservedFeatures(ontologyTerms); + List excluded = getExcludedFeatures(ontologyTerms); + List observedSpanish = getTranslations(observed); + List excludedSpanish = getTranslations(excluded); + var observedStr = getCommaList(observedSpanish); + var excludedStr = getCommaList(excludedSpanish); + if (!observed.isEmpty() && ! excluded.isEmpty()) { + return String.format("%s presentó los siguientes síntomas: %s. Por el contrario, se %s los siguientes síntomas: %s.", + personString, + observedStr, + excluded.size()>1? "excluyeron":"excluyeró", + excludedStr); + } else if (!observed.isEmpty()) { + return String.format("%s presentó los siguientes síntomas: %s.", personString, observedStr); + } else if (!excluded.isEmpty()) { + return String.format("Al inicio de la enfermedad, se %s los siguientes síntomas: %s.", + excluded.size()>1? "excluyeron":"excluyeró", excludedStr); + } else { + return "No se describieron explícitamente anomalías fenotípicas al inicio de la enfermedad"; + } + } + @Override + public String featuresAtEncounter(String personString, String ageString, List ontologyTerms) { + List observed = getObservedFeatures(ontologyTerms); + List excluded = getExcludedFeatures(ontologyTerms); + List observedGerman = getTranslations(observed); + List excludedGerman = getTranslations(excluded); + var observedStr = getCommaList(observedGerman); + var excludedStr = getCommaList(excludedGerman); + if (!observed.isEmpty() && ! excluded.isEmpty()) { + return String.format("%s presentaba %s los siguientes síntomas: %s. Por el contrario, se %s los siguientes síntomas: %s.", + personString, + ageString, + observedStr, + excluded.size()>1? "excluyeron":"excluyeró", + excludedStr); + } else if (!observed.isEmpty()) { + return String.format("%s presentaba %s los siguientes síntomas: %s.", ageString, personString, observedStr); + } else if (!excluded.isEmpty()) { + return String.format("%s se %s los siguientes síntomas: %s.", + ageString, + excluded.size()>1? "excluyeron":"excluyeró", excludedStr); + } else { + throw new PhenolRuntimeException("No features found for time point " + ageString); // should never happen + } + } } diff --git a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/SpanishBuildingBlocks.java b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/SpanishBuildingBlocks.java new file mode 100644 index 0000000..9dde88f --- /dev/null +++ b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/SpanishBuildingBlocks.java @@ -0,0 +1,291 @@ +package org.monarchinitiative.phenopacket2prompt.output.impl.spanish; + +import org.monarchinitiative.phenopacket2prompt.model.Iso8601Age; +import org.monarchinitiative.phenopacket2prompt.output.BuildingBlockGenerator; + +public class SpanishBuildingBlocks implements BuildingBlockGenerator { + @Override + public String days(int d) { + return ""; + } + + @Override + public String months(int m) { + return ""; + } + + @Override + public String years(int y) { + return ""; + } + + @Override + public String yearsOld(int y) { + return ""; + } + + @Override + public String monthsOld(int m) { + return ""; + } + + @Override + public String daysOld(int d) { + return ""; + } + + @Override + public String monthDayOld(int m, int d) { + return ""; + } + + @Override + public String yearsMonthsDaysOld(int y, int m, int d) { + return ""; + } + + @Override + public String asNewborn() { + return ""; + } + + @Override + public String atTheAgeOf() { + return ""; + } + + @Override + public String she() { + return ""; + } + + @Override + public String he() { + return ""; + } + + @Override + public String theProband() { + return ""; + } + + @Override + public String woman() { + return ""; + } + + @Override + public String man() { + return ""; + } + + @Override + public String individual() { + return ""; + } + + @Override + public String theIndividual() { + return ""; + } + + @Override + public String girl() { + return ""; + } + + @Override + public String boy() { + return ""; + } + + @Override + public String child() { + return ""; + } + + @Override + public String adolescentGirl() { + return ""; + } + + @Override + public String adolescentBoy() { + return ""; + } + + @Override + public String adolescentChild() { + return ""; + } + + @Override + public String maleInfant() { + return ""; + } + + @Override + public String femaleInfant() { + return ""; + } + + @Override + public String infant() { + return ""; + } + + @Override + public String newbornBoy() { + return ""; + } + + @Override + public String newbornGirl() { + return ""; + } + + @Override + public String newborn() { + return ""; + } + + @Override + public String maleFetus() { + return ""; + } + + @Override + public String femaleFetus() { + return ""; + } + + @Override + public String fetus() { + return ""; + } + + @Override + public String female() { + return ""; + } + + @Override + public String male() { + return ""; + } + + @Override + public String adult() { + return ""; + } + + @Override + public String probandWasA() { + return ""; + } + + @Override + public String whoPresented() { + return ""; + } + + @Override + public String presented() { + return ""; + } + + @Override + public String probandNoAgePresented() { + return ""; + } + + @Override + public String probandNoAgePresentedWith() { + return ""; + } + + @Override + public String probandWasAMale() { + return ""; + } + + @Override + public String probandWasAFemale() { + return ""; + } + + @Override + public String probandWasAnIndividual() { + return ""; + } + + @Override + public String presentedWith() { + return ""; + } + + @Override + public String with() { + return ""; + } + + @Override + public String inWhomManifestationsWereExcluded() { + return ""; + } + + @Override + public String duringFetal() { + return ""; + } + + @Override + public String asNeonate() { + return ""; + } + + @Override + public String atBirth() { + return ""; + } + + @Override + public String asInfant() { + return ""; + } + + @Override + public String inChildhood() { + return ""; + } + + @Override + public String asAdolescent() { + return ""; + } + + @Override + public String asAdult() { + return ""; + } + + @Override + public String asYoungAdult() { + return ""; + } + + @Override + public String asMiddleAge() { + return ""; + } + + @Override + public String asLateOnset() { + return ""; + } + + @Override + public String fromIso(Iso8601Age ppktAge) { + return ""; + } +} diff --git a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/SpanishPromptGenerator.java b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/SpanishPromptGenerator.java index 3838f04..de82746 100644 --- a/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/SpanishPromptGenerator.java +++ b/src/main/java/org/monarchinitiative/phenopacket2prompt/output/impl/spanish/SpanishPromptGenerator.java @@ -55,5 +55,39 @@ public Set getMissingTranslations() { } + @Override + public String getVignetteAtOnset(PpktIndividual individual){ + String person = switch (individual.getSex()) { + case MALE -> "Él"; + case FEMALE -> "Ella"; + default -> "La persona afectada"; + }; + return this.ppktPhenotypicFeatureGenerator.featuresAtOnset(person, individual.getPhenotypicFeaturesAtOnset()); + } + + + /** + * The following structure should work for most other languages, but the function + * can be overridden if necessary. + * @param individual The individual for whom we are creating the prompt + * @return the prompt text + */ + @Override + public String createPrompt(PpktIndividual individual) { + String individualInfo = getIndividualInformation(individual); + // For creating the prompt, we first report the onset and the unspecified terms together, and then + String onsetDescription = getVignetteAtOnset(individual); + Map> pfMap = individual.extractSpecifiedAgePhenotypicFeatures(); + // We then report the rest, one for each specified time + //String onsetFeatures = formatFeatures(onsetTerms); + StringBuilder sb = new StringBuilder(); + sb.append(queryHeader()); + sb.append(individualInfo).append("\n").append(onsetDescription).append("\n"); + for (var entry: pfMap.entrySet()) { + String vignette = getVignetteAtAge(entry.getKey(), individual.getSex(), entry.getValue()); + sb.append(vignette).append("\n"); + } + return sb.toString(); + } }