001package org.hl7.fhir.r5.renderers.spreadsheets;
002
003import java.io.FileOutputStream;
004import java.io.IOException;
005import java.io.OutputStream;
006import java.util.List;
007
008import lombok.extern.slf4j.Slf4j;
009import org.apache.poi.ss.usermodel.Row;
010import org.apache.poi.ss.usermodel.Sheet;
011import org.hl7.fhir.exceptions.DefinitionException;
012import org.hl7.fhir.r5.context.IWorkerContext;
013import org.hl7.fhir.r5.context.SimpleWorkerContext;
014import org.hl7.fhir.r5.model.CanonicalType;
015import org.hl7.fhir.r5.model.CodeSystem;
016import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent;
017import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
018import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent;
019import org.hl7.fhir.r5.model.ElementDefinition;
020import org.hl7.fhir.r5.model.Enumeration;
021import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
022import org.hl7.fhir.r5.model.ValueSet;
023import org.hl7.fhir.r5.model.ValueSet.ConceptReferenceComponent;
024import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
025import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent;
026import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
027import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent;
028import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionMappingComponent;
029import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
030import org.hl7.fhir.utilities.MarkedToMoveToAdjunctPackage;
031import org.hl7.fhir.utilities.i18n.I18nConstants;
032
033@MarkedToMoveToAdjunctPackage
034@Slf4j
035public class CodeSystemSpreadsheetGenerator extends CanonicalSpreadsheetGenerator {
036
037  public CodeSystemSpreadsheetGenerator(IWorkerContext context) {
038    super(context);
039  }
040
041  public boolean canGenerate(CodeSystem cs) {
042    return true;
043  }
044
045  public CodeSystemSpreadsheetGenerator renderCodeSystem(CodeSystem cs) throws IOException {
046    if (cs == null) {
047      log.warn("no code system!");
048    }
049    addCodeSystemMetadata(renderCanonicalResource(cs, false), cs);
050    
051    if (cs.hasProperty()) {
052      addProperties(cs.getProperty());
053    }
054    if (cs.hasFilter()) {
055      addFilters(cs.getFilter());
056    }
057    if (cs.hasConcept()) {
058      addConcepts(cs.getConcept());
059    }
060    return this;
061  }
062
063  private void addCodeSystemMetadata(Sheet sheet, CodeSystem cs) {
064    addMetadataRow(sheet, "Case Sensitive", cs.getCaseSensitiveElement().asStringValue());
065    addMetadataRow(sheet, "Value Set (all codes)", cs.getValueSet());
066    addMetadataRow(sheet, "Hierarchy", cs.getHierarchyMeaningElement().asStringValue());
067    addMetadataRow(sheet, "Compositional", cs.getCompositionalElement().asStringValue());
068    addMetadataRow(sheet, "Version Needed?", cs.getVersionNeededElement().asStringValue());
069    addMetadataRow(sheet, "Content", cs.getContentElement().asStringValue());
070    addMetadataRow(sheet, "Supplements", cs.getSupplements());
071    addMetadataRow(sheet, "Count", cs.getCountElement().asStringValue());
072  }
073  
074  private void addFilters(List<CodeSystemFilterComponent> filters) {
075    Sheet sheet = makeSheet("Filters");
076    addHeaders(sheet, "Code", "Description", "Operators", "Value");
077    for (CodeSystemFilterComponent f : filters) {
078      CommaSeparatedStringBuilder cs = new CommaSeparatedStringBuilder();
079      for (Enumeration<FilterOperator> op : f.getOperator()) {
080        cs.append(op.asStringValue());
081      }
082      addRow(sheet, f.getCode(), f.getDescription(), cs.toString(), f.getValue());
083    }        
084  }
085
086  private void addProperties(List<PropertyComponent> properties) {
087    Sheet sheet = makeSheet("Properties");
088    addHeaders(sheet, "Code", "Uri", "Description", "Type");
089    for (PropertyComponent p : properties) {
090      addRow(sheet, p.getCode(), p.getUri(), p.getDescription(), p.getTypeElement().asStringValue());
091    }    
092  }
093
094  private void addConcepts(List<ConceptDefinitionComponent> concepts) {
095    Sheet sheet = makeSheet("Concepts");
096    addHeaders(sheet, "Level", "Code", "Display", "Definition"); //todo: properties and designations
097    addConcepts(sheet, 1, concepts);    
098  }
099
100  private void addConcepts(Sheet sheet, int i, List<ConceptDefinitionComponent> concepts) {
101    for (ConceptDefinitionComponent c : concepts) {
102      addRow(sheet, Integer.toString(i), c.getCode(), c.getDisplay(), c.getDefinition());
103      if (c.hasConcept()) {
104        addConcepts(sheet, i+1, c.getConcept());
105      }
106    }    
107  }
108
109  private void genExpansionParams(List<ValueSetExpansionParameterComponent> params) {
110    Sheet sheet = makeSheet("Expansion Parameters");
111    addHeaders(sheet, "Parameter", "Value");
112    for (ValueSetExpansionParameterComponent p : params) {
113      addRow(sheet, p.getName(), dr.displayDataType(p.getValue()));          
114    }    
115  }
116
117  private void genExpansion(List<ValueSetExpansionContainsComponent> list) {
118    Sheet sheet = makeSheet("Expansion");
119    addHeaders(sheet, "Level", "System", "version", "Code", "Display", "Abstract", "Inactive");
120    genExpansionEntry(1, list, sheet);    
121  }
122
123  public void genExpansionEntry(int level, List<ValueSetExpansionContainsComponent> list, Sheet sheet) {
124    for (ValueSetExpansionContainsComponent p : list) {
125      addRow(sheet, Integer.toString(level), p.getSystem(), p.getVersion(), p.getCode(), p.getDisplay(), bool(p.getAbstract()), bool(p.getInactive()));  
126      if (p.hasContains()) {
127        genExpansionEntry(level + 1, p.getContains(), sheet);
128      }
129    }
130  }
131  
132  private String bool(boolean value) {    
133    return value ? "" : "false";
134  }
135
136//  private void genInclude(ValueSet vs, ConceptSetComponent inc, String mode) {
137//    if (inc.hasSystem()) {
138//      genIncludeSystem(vs, inc, mode);
139//    } else {
140//      genIncludeValueSets(vs, inc, mode);      
141//    }
142//    String subname = inc.hasSystem() ?  : "ValueSets";
143//    
144//
145//  Row headerRow = sheet.createRow(0);
146//  for (int i = 0; i < titles.length; i++) {
147//    addCell(headerRow, i, titles[i], styles.get("header"));
148//  }
149//  int i = titles.length - 1;
150//  for (StructureDefinitionMappingComponent map : sd.getMapping()) {
151//    i++;
152//    addCell(headerRow, i, "Mapping: " + map.getName(), styles.get("header"));
153//  }    
154//
155//  for (ElementDefinition child : sd.getSnapshot().getElement()) {
156//    processElement(sheet, sd, child);
157//  }
158//  configureSheet(sheet, sd);
159//  }
160
161//  private void genIncludeValueSets(ValueSet vs, ConceptSetComponent inc, String mode) {
162//    Sheet sheet = makeSheet(mode+" ValueSets");
163//    addValueSets(sheet, inc.getValueSet()); 
164//    configureSheet(sheet);
165//  }
166//
167//  private void genIncludeSystem(ValueSet vs, ConceptSetComponent inc, String mode) {
168//    Sheet sheet = makeSheet(mode+" from "+dr.displaySystem(inc.getSystem()));
169//    if (inc.hasValueSet()) {
170//      addValueSets(sheet, inc.getValueSet());
171//    }
172//    if (inc.hasFilter()) {
173//      addFilters(sheet, inc.getFilter());
174//    }
175//    if (inc.hasConcept()) {
176//      addConcepts(sheet, inc.getConcept());
177//    }
178//    if (!inc.hasConcept() && !inc.hasFilter()) {
179//      addAllCodes(sheet);
180//    }
181//    addRow(sheet, "", "");          
182//    addRow(sheet, "System URI", inc.getSystem());          
183//    
184//    configureSheet(sheet);
185//  }
186
187  private void addAllCodes(Sheet sheet) {
188    addHeaders(sheet, "Codes");     
189    addRow(sheet, "All codes");          
190  }
191
192  private void addValueSets(Sheet sheet, List<CanonicalType> valueSets) {
193    addHeaders(sheet, "ValueSet URL");
194    for (CanonicalType u : valueSets) {
195      addRow(sheet, u.getValue());
196    }
197  }
198
199  private void configureSheet(Sheet sheet) {
200    sheet.setColumnWidth(0, columnPixels(30));
201    sheet.setColumnWidth(1, columnPixels(40));
202    sheet.setColumnWidth(1, columnPixels(50));
203  }
204
205  private void addConcepts(Sheet sheet, List<ConceptReferenceComponent> concepts) {
206    addHeaders(sheet, "Concept", "Description"); // todo: designations    
207    for (ConceptReferenceComponent cd : concepts) {
208      addRow(sheet, cd.getCode(), cd.getDisplay());      
209    }    
210  }
211
212  private void addFilters(Sheet sheet, List<ConceptSetFilterComponent> filters) {
213    addHeaders(sheet, "Property", "Operation", "Value");    
214    for (ConceptSetFilterComponent f : filters) {
215      addRow(sheet, f.getProperty(), f.getOpElement().asStringValue(), f.getValue());
216    }
217  }
218
219}