Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rm to markdown serializer #503

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
489d061
Attempt at human readable markdown generator from RM instances
pieterbos May 16, 2023
ed876bc
Fix the reflection rm serializer
pieterbos May 16, 2023
03778d5
Implement more serializers
pieterbos May 22, 2023
7d489cd
Refactor writeToText rename to append
pieterbos May 22, 2023
3d827c5
Added a generic LocatableSerializer
pieterbos May 22, 2023
a6dd7ce
Add I18n, add appendIfNotNull for generic appending
pieterbos May 22, 2023
074d80d
Add more I18n + appendIfNull refactors
pieterbos May 22, 2023
4ea5eb3
More refactors, some additional types
pieterbos May 22, 2023
b6287bb
Add test with Composition
pieterbos May 22, 2023
d5c81a5
Add extra tests plus layout tweaks, refactor adding newlines
pieterbos May 22, 2023
67184a8
Refactor name of RmToMarkdownSerializer, add proper date/time formats
pieterbos May 30, 2023
8ba4bde
Add basic assertions to tests
pieterbos May 30, 2023
309af58
Add tests for more types, plus some minor layout fixes
pieterbos May 30, 2023
157d683
Add more tests + some small fixes
pieterbos May 31, 2023
347986d
Implement more tests plus layout fixes
pieterbos Aug 21, 2023
da408ff
Fix date+time localization in markdown formatter
pieterbos Aug 31, 2023
4e15a06
Fix I18n in markdown serializer
pieterbos Aug 31, 2023
ff06bb3
Make translation files up to date
pieterbos Aug 31, 2023
7d6a367
Update i18n plus Dutch translation for most RM markdown serialization
pieterbos Aug 31, 2023
6432de3
Add test to confirm I18n is working for rm markdown serializer
pieterbos Aug 31, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.nedap.archie.text;

import com.nedap.archie.rm.RMObject;
import com.nedap.archie.rminfo.ArchieRMInfoLookup;
import com.nedap.archie.rminfo.RMAttributeInfo;
import com.nedap.archie.rminfo.RMTypeInfo;

import java.lang.reflect.InvocationTargetException;

public class ReflectionRmSerializer {

private final ArchieRMInfoLookup modelLookup;

public ReflectionRmSerializer() {
this.modelLookup = ArchieRMInfoLookup.getInstance();
}
public void serialize(RMObject object, RmToMarkdownSerializer serializer) {
if(object == null) {
return;
}
RMTypeInfo typeInfo = modelLookup.getTypeInfo(object.getClass());
if(typeInfo == null) {
serializer.append(object.toString());
return;
}
for(RMAttributeInfo attribute: typeInfo.getAttributes().values()) {
if(attribute.getRmName().equalsIgnoreCase("parent") ||
attribute.getRmName().equalsIgnoreCase("path") ||
attribute.getRmName().equalsIgnoreCase("archetype_node_id")) {
//ignore parent to prevent endless loops. Ignore path and archetype node id because these are very technical fields
continue;
}
serializer.append(attribute.getRmName());
serializer.append(": ");
try {
Object result = attribute.getGetMethod().invoke(object);
if(result == null) {
serializer.append(" - ");
} else if(result instanceof RMObject) {
serializer.append((RMObject) result);
} else {
serializer.append(result.toString());
}
} catch (IllegalAccessException e) {
//...
} catch (InvocationTargetException e) {
//...
}
serializer.appendNewLine();

}
}
}
10 changes: 10 additions & 0 deletions archie-utils/src/main/java/com/nedap/archie/text/RmSerializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.nedap.archie.text;

import com.nedap.archie.rm.RMObject;

public interface RmSerializer<T extends RMObject> {

void serialize(T data, RmToMarkdownSerializer serializer);

Class getSerializedClass();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package com.nedap.archie.text;

import com.nedap.archie.base.OpenEHRBase;
import com.nedap.archie.rm.RMObject;
import com.nedap.archie.text.serializers.*;
import com.nedap.archie.text.serializers.action.ActivitySerializer;
import com.nedap.archie.text.serializers.action.InstructionDetailsSerializer;
import com.nedap.archie.text.serializers.action.IsmTransitionSerializer;
import com.nedap.archie.text.serializers.audit.FeederAuditDetailsSerializer;
import com.nedap.archie.text.serializers.audit.FeederAuditSerializer;
import com.nedap.archie.text.serializers.datatypes.*;
import com.nedap.archie.text.serializers.demographic.PartyIdentifiedSerializer;
import com.nedap.archie.text.serializers.demographic.PartyRelatedSerializer;
import com.nedap.archie.text.serializers.demographic.PartySelfSerializer;
import com.nedap.archie.text.serializers.entries.*;

import java.util.HashMap;
import java.util.Map;

/**
* Converts instances of the openEHR RM to human readable text
*/
public class RmToMarkdownSerializer {

Map<Class, RmSerializer> serializers;
ReflectionRmSerializer fallbackSerializer;

StringBuilder stringBuilder;

public RmToMarkdownSerializer() {
serializers = new HashMap<>();

//composition and data structures
addSerializer(new ClusterSerializer());
addSerializer(new CompositionSerializer());
addSerializer(new ElementSerializer());
addSerializer(new ItemListSerializer());
addSerializer(new ItemTreeSerializer());
addSerializer(new SectionSerializer());

//composition extra classes
addSerializer(new EventContextSerializer());
addSerializer(new ParticipationSerializer());

//entries
addSerializer(new EvaluationSerializer());
addSerializer(new ObservationSerializer());
addSerializer(new InstructionSerializer());
addSerializer(new ActionSerializer());
addSerializer(new GenericEntrySerializer());
addSerializer(new AdminEntrySerializer());

//classes used in Action and Instruction
addSerializer(new ActivitySerializer());
addSerializer(new InstructionDetailsSerializer());
addSerializer(new IsmTransitionSerializer());

//references to demographics
addSerializer(new PartyIdentifiedSerializer());
addSerializer(new PartyRelatedSerializer());
addSerializer(new PartySelfSerializer());

//data values
addSerializer(new DvBooleanSerializer());
addSerializer(new DvCodedTextSerializer());
addSerializer(new DvCountSerializer());
addSerializer(new DvDateSerializer());
addSerializer(new DvDateTimeSerializer());
addSerializer(new DvDurationSerializer());
addSerializer(new DvOrdinalSerializer());
addSerializer(new DvProportionSerializer());
addSerializer(new DvQuantitySerializer());
addSerializer(new DvTextSerializer());
addSerializer(new DvTimeSerializer());
addSerializer(new DvIdentifierSerializer());
addSerializer(new DvIntervalSerializer());

//supporting DV-classes
addSerializer(new ReferenceRangeSerializer());

//Feeder audits
addSerializer(new FeederAuditSerializer());
addSerializer(new FeederAuditDetailsSerializer());

//if no serializer is known, just serialize every field
//better would be to filter on non-technical attributes - However currently this works without the BMM
fallbackSerializer = new ReflectionRmSerializer();
stringBuilder = new StringBuilder();
}

private void addSerializer(RmSerializer serializer) {
serializers.put(serializer.getSerializedClass(), serializer);
}

public void append(RMObject object){
if(object == null) {
return;
}
RmSerializer serializer = serializers.get(object.getClass());

if(serializer != null) {
serializer.serialize(object, this);
} else {
fallbackSerializer.serialize(object, this);
}
}

public void appendIfNotNull(String name, RMObject data) {
if(data != null) {
append(name);
append(": ");
append(data);
appendNewLine();
}
}

/**
* Append a header with the data, if data is not null. Adds a newline after the header
* @param headerString the markdown header string, for example '## ' or '#### '
* @param name the name to serialize as the name of the header
* @param data the data to further serialize.
*/
public void appendWithHeaderIfNotNull(String headerString, String name, RMObject data) {
if(data != null) {
append(headerString);
append(name);
appendNewLine();
append(data);
appendNewLine();
}
}

public void appendIfNotNull(String name, String data) {
if(data != null) {
append(name);
append(": ");
append(data);
appendNewLine();
}
}

public void appendNewLine() {
append(" \n");
}

public String toString() {
return stringBuilder.toString();
}

public void append(String toWrite) {
stringBuilder.append(toWrite);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.nedap.archie.text.serializers;

import com.nedap.archie.rm.datastructures.Cluster;
import com.nedap.archie.rm.datastructures.Item;
import com.nedap.archie.text.RmSerializer;
import com.nedap.archie.text.RmToMarkdownSerializer;

public class ClusterSerializer implements RmSerializer<Cluster> {

@Override
public void serialize(Cluster data, RmToMarkdownSerializer serializer) {
serializer.append("#### ");
LocatableUtil.serialize(data, serializer);
for(Item item: data.getItems()) {
serializer.append(item);
serializer.appendNewLine();
}
}

@Override
public Class getSerializedClass() {
return Cluster.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.nedap.archie.text.serializers;

import com.nedap.archie.rm.composition.Composition;
import com.nedap.archie.rm.composition.ContentItem;
import com.nedap.archie.text.RmSerializer;
import com.nedap.archie.text.RmToMarkdownSerializer;
import org.openehr.utils.message.I18n;

public class CompositionSerializer implements RmSerializer<Composition> {

@Override
public void serialize(Composition data, RmToMarkdownSerializer serializer) {
serializer.append("# ");
LocatableUtil.serialize(data, serializer);
serializer.appendIfNotNull(I18n.t("Composer"), data.getComposer());

for(ContentItem item: data.getContent()) {
serializer.append(item);
serializer.appendNewLine();
}

serializer.appendWithHeaderIfNotNull("## ", I18n.t("Context"), data.getContext());
//not written for now: language, territory

}

@Override
public Class getSerializedClass() {
return Composition.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.nedap.archie.text.serializers;

import com.nedap.archie.rm.datastructures.Element;
import com.nedap.archie.text.RmSerializer;
import com.nedap.archie.text.RmToMarkdownSerializer;
import org.openehr.utils.message.I18n;

public class ElementSerializer implements RmSerializer<Element> {

@Override
public void serialize(Element data, RmToMarkdownSerializer serializer) {
serializer.append(data.getName());
serializer.append(": ");

if(data.getValue() != null) {
serializer.append(data.getValue());
} else {
if(data.getNullFlavour() != null) {
serializer.append(I18n.t("No value. Reason: "));
serializer.append(data.getNullFlavour());
if(data.getNullReason() != null) {
serializer.append(" ");
serializer.append(data.getNullReason());
}
}
}

}

@Override
public Class getSerializedClass() {
return Element.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.nedap.archie.text.serializers;

import com.nedap.archie.rm.composition.EventContext;
import com.nedap.archie.rm.generic.Participation;
import com.nedap.archie.text.RmSerializer;
import com.nedap.archie.text.RmToMarkdownSerializer;
import org.openehr.utils.message.I18n;

public class EventContextSerializer implements RmSerializer<EventContext> {
@Override
public void serialize(EventContext data, RmToMarkdownSerializer serializer) {

serializer.appendIfNotNull(I18n.t("Location"), data.getLocation());
serializer.appendIfNotNull(I18n.t("Start time"), data.getStartTime());
serializer.appendIfNotNull(I18n.t("End time"), data.getEndTime());
serializer.appendIfNotNull(I18n.t("Setting"), data.getSetting());

if(data.getParticipations() != null && !data.getParticipations().isEmpty()) {

serializer.append("##### ");
serializer.append(I18n.t("participations"));
serializer.appendNewLine();
for(Participation participation:data.getParticipations()) {
serializer.append(participation);
serializer.appendNewLine();
}
}
if(data.getOtherContext() != null) {
serializer.append(data.getOtherContext());
}
}

@Override
public Class getSerializedClass() {
return EventContext.class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.nedap.archie.text.serializers;

import com.nedap.archie.rm.datastructures.Item;
import com.nedap.archie.rm.datastructures.ItemList;
import com.nedap.archie.text.RmSerializer;
import com.nedap.archie.text.RmToMarkdownSerializer;

public class ItemListSerializer implements RmSerializer<ItemList> {

@Override
public void serialize(ItemList data, RmToMarkdownSerializer serializer) {
//name is rarely used, neither is feeder audit - do not show, is confusing
//serializer.append("#### ");
//LocatableUtil.serialize(data, serializer);
for(Item item: data.getItems()) {
serializer.append(item);
serializer.appendNewLine();
}
}

@Override
public Class getSerializedClass() {
return ItemList.class;
}
}
Loading