diff --git a/LOINC_NOTES.txt b/LOINC_NOTES.txt index 83ffb09561f..e209775ee79 100644 --- a/LOINC_NOTES.txt +++ b/LOINC_NOTES.txt @@ -8,31 +8,57 @@ TODO: Comments for Loinc: Overall -- ValueSet and ConceptMap resources have a spot for copyright and contact information. Are there official values for these? +- ValueSet and ConceptMap resources have a spot for copyright and + contact information. Are there official values for these? Answer Lists -- Per the notes, there is no way in FHIR currently to map answer lists to codes based on context. For this reason, I am ignoring any entries in LoincAnswerListLink_Beta_1.csv where the "ApplicableContext" context is not empty. Is this correct? +- Per the notes, there is no way in FHIR currently to map answer lists to + codes based on context. For this reason, I am ignoring any entries in + LoincAnswerListLink_Beta_1.csv where the "ApplicableContext" context is + not empty. Is this correct? Parts -- Only parts with a status of "ACTIVE" are being imported, any others are ignored. Does this make sense? -- The PartTypeName (e.g. "ADJUSTMENT") is ignored as there is no corresponding property in loinc.xml -- PartDisplayName does not have an obvious mapping to FHIR -- Part links are not currently processed (it's not clear to me how to model these in FHIR, as CodeSystem.hierarchyMeaning has to be only one of 'is-a' or 'part-of' and presumably the 'is-a' relationship is more important. +- Only parts with a status of "ACTIVE" are being imported, any others are + ignored. +- The PartTypeName (e.g. "ADJUSTMENT") is ignored as there is no corresponding + property in loinc.xml +- PartDisplayName is not mapped +- Part links are not currently processed (it's not clear to me how to model + these in FHIR, as CodeSystem.hierarchyMeaning has to be only one of 'is-a' + or 'part-of' and presumably the 'is-a' relationship is more important. Part Mappings -- I have made LOINC the source and SCT the target for the mappings in the ConceptMap resource. Does this seem like the appropriate orientation? -- A canonical URI should be defined for the LOINC->SCT mapping ConceptMap resource. I have hardcoded "http://loinc.org/loinc-to-snomed" for now, but we should discuss what is appropriate. +- I have made LOINC the source and SCT the target for the mappings in the + ConceptMap resource. Does this seem like the appropriate orientation? +- A canonical URI should be defined for the LOINC->SCT mapping ConceptMap + resource. I have hardcoded "http://loinc.org/loinc-to-snomed" for now, but + we should discuss what is appropriate. RSNA Playbook -- A canonical URI should be defined for the "all RSNA playbook codes" ValueSet. I have hardcoded "http://loinc.org/rsna-codes" for now but we should discuss what is appropriate. +- A canonical URI should be defined for the "all RSNA playbook codes" ValueSet. + I have hardcoded "http://loinc.org/rsna-codes" for now but we should discuss + what is appropriate. - A name for the "RSNA Playbook" ValueSet is needed. -- Just to confirm, the "all RSNA playbook codes" ValueSet should contain the loinc codes (e.g. "17787-3") and not the part codes (e.g. "LP199995-4")? -- A codesystem URI for radlex RID and RPID codes is needed (currently "http://rid" and "http://rpid" are used as placeholders since I'm assuming these exist somewhere. -- For mappings from loinc part codes to RadLex RIDs, are the codes considered equivalent (or would they be wider/narrower). They look equivalent to me. +- Just to confirm, the "all RSNA playbook codes" ValueSet should contain the + loinc codes (e.g. "17787-3") and not the part codes (e.g. "LP199995-4")? +- A codesystem URI for radlex RID and RPID codes is needed (currently + "http://rid" and "http://rpid" are used as placeholders since I'm assuming + these exist somewhere. +- For mappings from loinc part codes to RadLex RIDs, are the codes considered + equivalent (or would they be wider/narrower). They look equivalent to me. Document Ontology -- Per the SOW, "A value set containing terms in the LOINC Document Ontology will be created". Just to confirm, entries in this ValueSet are therefore LOINC terms (such as "11488-4 / Consultation Note") as opposed to part codes? -- Need to define a URI for the document ontology ValueSet. Currently I am using "http://loinc.org/document-ontology-codes" +- Per the SOW, "A value set containing terms in the LOINC Document Ontology + will be created". Just to confirm, entries in this ValueSet are therefore + LOINC terms (such as "11488-4 / Consultation Note") as opposed to part + codes? +- Need to define a URI for the document ontology ValueSet. Currently I am + using "http://loinc.org/document-ontology-codes" Top 2000 -- Need to define a URI for both ValueSets. Currently I am using "http://loinc.org/top-2000-lab-results-us" and "http://loinc.org/top-2000-lab-results-si" +- Need to define a URI for both ValueSets. Currently I am using + "http://loinc.org/top-2000-lab-results-us" and + "http://loinc.org/top-2000-lab-results-si" + +Universal Order Set +- Need to define a URI for this ValueSet - Currenty using "http://loinc.org/fhir/loinc-universal-order-set" diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/.gitignore b/example-projects/hapi-fhir-jpaserver-cds-example/.gitignore new file mode 100644 index 00000000000..e52bde55f83 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/.gitignore @@ -0,0 +1,128 @@ +/target +/jpaserver_derby_files +*.log +ca.uhn.fhir.jpa.entity.ResourceTable/ + +# Created by https://www.gitignore.io + +### Java ### +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties + + +### Vim ### +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ + + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + + + +### Eclipse ### +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.loadpath + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# JDT-specific (Eclipse Java Development Tools) + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/pom.xml b/example-projects/hapi-fhir-jpaserver-cds-example/pom.xml new file mode 100644 index 00000000000..4d600fe1ba8 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/pom.xml @@ -0,0 +1,286 @@ + + 4.0.0 + + + + ca.uhn.hapi.fhir + hapi-fhir + 3.3.0-SNAPSHOT + ../../pom.xml + + + hapi-fhir-jpaserver-cds-example + war + + HAPI FHIR JPA Clinical Decision Support Server - Example + + + + oss-snapshots + + true + + https://oss.sonatype.org/content/repositories/snapshots/ + + + + + + + org.opencds.cqf + cqf-ruler + 0.1.0-SNAPSHOT + + + + org.eclipse.jetty.websocket + websocket-api + ${jetty_version} + + + org.eclipse.jetty.websocket + websocket-client + ${jetty_version} + + + mysql + mysql-connector-java + 6.0.5 + + + + + ca.uhn.hapi.fhir + hapi-fhir-base + ${project.version} + + + + + ca.uhn.hapi.fhir + hapi-fhir-structures-dstu3 + ${project.version} + + + + + ca.uhn.hapi.fhir + hapi-fhir-jpaserver-base + ${project.version} + + + + + ca.uhn.hapi.fhir + hapi-fhir-testpage-overlay + ${project.version} + war + provided + + + ca.uhn.hapi.fhir + hapi-fhir-testpage-overlay + ${project.version} + classes + provided + + + + + ch.qos.logback + logback-classic + + + + + javax.servlet + javax.servlet-api + provided + + + + + org.thymeleaf + thymeleaf + + + + + org.ebaysf.web + cors-filter + + + servlet-api + javax.servlet + + + + + + + org.springframework + spring-web + + + + + org.apache.commons + commons-dbcp2 + + + + + org.apache.derby + derby + + + org.apache.derby + derbynet + + + org.apache.derby + derbyclient + + + + + org.eclipse.jetty + jetty-servlets + test + + + org.eclipse.jetty + jetty-servlet + test + + + org.eclipse.jetty.websocket + websocket-server + test + + + org.eclipse.jetty + jetty-server + test + + + org.eclipse.jetty + jetty-util + test + + + org.eclipse.jetty + jetty-webapp + test + + + com.phloc + phloc-schematron + + + Saxon-HE + net.sf.saxon + + + + + + + javax.interceptor + javax.interceptor-api + provided + + + + + + + + hapi-fhir-jpaserver-cds + + + + + + org.eclipse.jetty + jetty-maven-plugin + + + /hapi-fhir-jpaserver-cds + true + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + + + org.apache.maven.plugins + maven-war-plugin + + + + ${maven.build.timestamp} + + + + + ca.uhn.hapi.fhir + hapi-fhir-testpage-overlay + + + src/main/webapp/WEB-INF/web.xml + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + + true + + + + + integration-test + verify + + + + + + + + + diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsHooksServerExample.java b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsHooksServerExample.java new file mode 100644 index 00000000000..e17bc747437 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsHooksServerExample.java @@ -0,0 +1,16 @@ +package ca.uhn.fhir.jpa.cds.example; + +import org.opencds.cqf.servlet.CdsServicesServlet; + +public class CdsHooksServerExample extends CdsServicesServlet { + +// @Override +// protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { +// // Change how requests are handled +// } +// +// @Override +// protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { +// // Change discovery response +// } +} diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsServerExample.java b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsServerExample.java new file mode 100644 index 00000000000..5670c8213f3 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsServerExample.java @@ -0,0 +1,165 @@ + +package ca.uhn.fhir.jpa.cds.example; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.jpa.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; +import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; +import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; +import ca.uhn.fhir.jpa.rp.dstu3.ActivityDefinitionResourceProvider; +import ca.uhn.fhir.jpa.rp.dstu3.MeasureResourceProvider; +import ca.uhn.fhir.jpa.rp.dstu3.PlanDefinitionResourceProvider; +import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; +import ca.uhn.fhir.rest.api.EncodingEnum; +import ca.uhn.fhir.rest.server.ETagSupportEnum; +import ca.uhn.fhir.rest.server.IResourceProvider; +import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Meta; +import org.opencds.cqf.providers.FHIRActivityDefinitionResourceProvider; +import org.opencds.cqf.providers.FHIRMeasureResourceProvider; +import org.opencds.cqf.providers.FHIRPlanDefinitionResourceProvider; +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.WebApplicationContext; + +import javax.servlet.ServletException; +import java.util.Collection; +import java.util.List; + +public class CdsServerExample extends RestfulServer { + + @SuppressWarnings("unchecked") + @Override + protected void initialize() throws ServletException { + super.initialize(); + + FhirVersionEnum fhirVersion = FhirVersionEnum.DSTU3; + setFhirContext(new FhirContext(fhirVersion)); + + // Get the spring context from the web container (it's declared in web.xml) + WebApplicationContext myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext(); + + if (myAppCtx == null) { + throw new ServletException("Error retrieving spring context from the web container"); + } + + String resourceProviderBeanName = "myResourceProvidersDstu3"; + List beans = myAppCtx.getBean(resourceProviderBeanName, List.class); + setResourceProviders(beans); + + Object systemProvider = myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class); + setPlainProviders(systemProvider); + + /* + * The conformance provider exports the supported resources, search parameters, etc for + * this server. The JPA version adds resource counts to the exported statement, so it + * is a nice addition. + */ + IFhirSystemDao systemDao = myAppCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class); + JpaConformanceProviderDstu3 confProvider = + new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class)); + confProvider.setImplementationDescription("Example Server"); + setServerConformanceProvider(confProvider); + + /* + * Enable ETag Support (this is already the default) + */ + setETagSupport(ETagSupportEnum.ENABLED); + + /* + * This server tries to dynamically generate narratives + */ + FhirContext ctx = getFhirContext(); + ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + + /* + * Default to JSON and pretty printing + */ + setDefaultPrettyPrint(true); + setDefaultResponseEncoding(EncodingEnum.JSON); + + /* + * -- New in HAPI FHIR 1.5 -- + * This configures the server to page search results to and from + * the database, instead of only paging them to memory. This may mean + * a performance hit when performing searches that return lots of results, + * but makes the server much more scalable. + */ + setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class)); + + /* + * Load interceptors for the server from Spring (these are defined in FhirServerConfig.java) + */ + Collection interceptorBeans = myAppCtx.getBeansOfType(IServerInterceptor.class).values(); + for (IServerInterceptor interceptor : interceptorBeans) { + this.registerInterceptor(interceptor); + } + + /* + * Adding resource providers from the cqf-ruler + */ + // Measure processing + FHIRMeasureResourceProvider measureProvider = new FHIRMeasureResourceProvider(getResourceProviders()); + MeasureResourceProvider jpaMeasureProvider = (MeasureResourceProvider) getProvider("Measure"); + measureProvider.setDao(jpaMeasureProvider.getDao()); + measureProvider.setContext(jpaMeasureProvider.getContext()); + + // PlanDefinition processing + FHIRPlanDefinitionResourceProvider planDefProvider = new FHIRPlanDefinitionResourceProvider(getResourceProviders()); + PlanDefinitionResourceProvider jpaPlanDefProvider = + (PlanDefinitionResourceProvider) getProvider("PlanDefinition"); + planDefProvider.setDao(jpaPlanDefProvider.getDao()); + planDefProvider.setContext(jpaPlanDefProvider.getContext()); + + // ActivityDefinition processing + FHIRActivityDefinitionResourceProvider actDefProvider = new FHIRActivityDefinitionResourceProvider(getResourceProviders()); + ActivityDefinitionResourceProvider jpaActDefProvider = + (ActivityDefinitionResourceProvider) getProvider("ActivityDefinition"); + actDefProvider.setDao(jpaActDefProvider.getDao()); + actDefProvider.setContext(jpaActDefProvider.getContext()); + + try { + unregisterProvider(jpaMeasureProvider); + unregisterProvider(jpaPlanDefProvider); + unregisterProvider(jpaActDefProvider); + } catch (Exception e) { + throw new ServletException("Unable to unregister provider: " + e.getMessage()); + } + + registerProvider(measureProvider); + registerProvider(planDefProvider); + registerProvider(actDefProvider); + + /* + * If you are hosting this server at a specific DNS name, the server will try to + * figure out the FHIR base URL based on what the web container tells it, but + * this doesn't always work. If you are setting links in your search bundles that + * just refer to "localhost", you might want to use a server address strategy: + */ + //setServerAddressStrategy(new HardcodedServerAddressStrategy("http://mydomain.com/fhir/baseDstu2")); + + /* + * If you are using DSTU3+, you may want to add a terminology uploader, which allows + * uploading of external terminologies such as Snomed CT. Note that this uploader + * does not have any security attached (any anonymous user may use it by default) + * so it is a potential security vulnerability. Consider using an AuthorizationInterceptor + * with this feature. + */ + registerProvider(myAppCtx.getBean(TerminologyUploaderProvider.class)); + } + + public IResourceProvider getProvider(String name) { + + for (IResourceProvider res : getResourceProviders()) { + if (res.getResourceType().getSimpleName().equals(name)) { + return res; + } + } + + throw new IllegalArgumentException("This should never happen!"); + } +} diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java new file mode 100644 index 00000000000..58a9536b281 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java @@ -0,0 +1,125 @@ +package ca.uhn.fhir.jpa.cds.example; + +import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; +import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory; +import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor; +import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; +import org.apache.commons.dbcp2.BasicDataSource; +import org.apache.commons.lang3.time.DateUtils; +import org.hibernate.jpa.HibernatePersistenceProvider; +import org.springframework.beans.factory.annotation.Autowire; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; +import java.util.Properties; + +/** + * This is the primary configuration file for the example server + */ +@Configuration +@EnableTransactionManagement() +public class FhirServerConfig extends BaseJavaConfigDstu3 { + + /** + * Configure FHIR properties around the the JPA server via this bean + */ + @Bean() + public DaoConfig daoConfig() { + DaoConfig retVal = new DaoConfig(); + retVal.setSubscriptionEnabled(true); + retVal.setSubscriptionPollDelay(5000); + retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR); + retVal.setAllowMultipleDelete(true); + return retVal; + } + + /** + * The following bean configures the database connection. The 'url' property value of "jdbc:derby:directory:jpaserver_derby_files;create=true" indicates that the server should save resources in a + * directory called "jpaserver_derby_files". + * + * A URL to a remote database could also be placed here, along with login credentials and other properties supported by BasicDataSource. + */ + @Bean(destroyMethod = "close") + public DataSource dataSource() { + BasicDataSource retVal = new BasicDataSource(); + retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver()); + retVal.setUrl("jdbc:derby:directory:target/jpaserver_derby_files;create=true"); + retVal.setUsername(""); + retVal.setPassword(""); + return retVal; + } + + @Bean() + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean(); + retVal.setPersistenceUnitName("HAPI_PU"); + retVal.setDataSource(dataSource()); + retVal.setPackagesToScan("ca.uhn.fhir.jpa.entity"); + retVal.setPersistenceProvider(new HibernatePersistenceProvider()); + retVal.setJpaProperties(jpaProperties()); + return retVal; + } + + private Properties jpaProperties() { + Properties extraProperties = new Properties(); + extraProperties.put("hibernate.dialect", org.hibernate.dialect.DerbyTenSevenDialect.class.getName()); + extraProperties.put("hibernate.format_sql", "true"); + extraProperties.put("hibernate.show_sql", "false"); + extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.jdbc.batch_size", "20"); + extraProperties.put("hibernate.cache.use_query_cache", "false"); + extraProperties.put("hibernate.cache.use_second_level_cache", "false"); + extraProperties.put("hibernate.cache.use_structured_entries", "false"); + extraProperties.put("hibernate.cache.use_minimal_puts", "false"); + extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName()); + extraProperties.put("hibernate.search.default.directory_provider", "filesystem"); + extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles"); + extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT"); +// extraProperties.put("hibernate.search.default.worker.execution", "async"); + return extraProperties; + } + + /** + * Do some fancy logging to create a nice access log that has details about each incoming request. + */ + public IServerInterceptor loggingInterceptor() { + LoggingInterceptor retVal = new LoggingInterceptor(); + retVal.setLoggerName("fhirtest.access"); + retVal.setMessageFormat( + "Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}]"); + retVal.setLogExceptions(true); + retVal.setErrorMessageFormat("ERROR - ${requestVerb} ${requestUrl}"); + return retVal; + } + + /** + * This interceptor adds some pretty syntax highlighting in responses when a browser is detected + */ + @Bean(autowire = Autowire.BY_TYPE) + public IServerInterceptor responseHighlighterInterceptor() { + ResponseHighlighterInterceptor retVal = new ResponseHighlighterInterceptor(); + return retVal; + } + + @Bean(autowire = Autowire.BY_TYPE) + public IServerInterceptor subscriptionSecurityInterceptor() { + SubscriptionsRequireManualActivationInterceptorDstu3 retVal = new SubscriptionsRequireManualActivationInterceptorDstu3(); + return retVal; + } + + @Bean() + public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager retVal = new JpaTransactionManager(); + retVal.setEntityManagerFactory(entityManagerFactory); + return retVal; + } + +} diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirTesterConfig.java b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirTesterConfig.java new file mode 100644 index 00000000000..6fbc660b27d --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirTesterConfig.java @@ -0,0 +1,56 @@ +package ca.uhn.fhir.jpa.cds.example; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.to.FhirTesterMvcConfig; +import ca.uhn.fhir.to.TesterConfig; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +//@formatter:off + +/** + * This spring config file configures the web testing module. It serves two + * purposes: + * 1. It imports FhirTesterMvcConfig, which is the spring config for the + * tester itself + * 2. It tells the tester which server(s) to talk to, via the testerConfig() + * method below + */ +@Configuration +@Import(FhirTesterMvcConfig.class) +public class FhirTesterConfig { + + /** + * This bean tells the testing webpage which servers it should configure itself + * to communicate with. In this example we configure it to talk to the local + * server, as well as one public server. If you are creating a project to + * deploy somewhere else, you might choose to only put your own server's + * address here. + * + * Note the use of the ${serverBase} variable below. This will be replaced with + * the base URL as reported by the server itself. Often for a simple Tomcat + * (or other container) installation, this will end up being something + * like "http://localhost:8080/hapi-fhir-jpaserver-example". If you are + * deploying your server to a place with a fully qualified domain name, + * you might want to use that instead of using the variable. + */ + @Bean + public TesterConfig testerConfig() { + TesterConfig retVal = new TesterConfig(); + retVal + .addServer() + .withId("home") + .withFhirVersion(FhirVersionEnum.DSTU3) + .withBaseUrl("${serverBase}/baseDstu3") + .withName("Local Tester") + .addServer() + .withId("hapi") + .withFhirVersion(FhirVersionEnum.DSTU3) + .withBaseUrl("http://fhirtest.uhn.ca/baseDstu3") + .withName("Public HAPI Test Server"); + return retVal; + } + +} +//@formatter:on diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/about.html b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/about.html new file mode 100644 index 00000000000..d552027e956 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/about.html @@ -0,0 +1,67 @@ + + + + About This Server + + + +
+
+ +
+
+
+ +
+ +
+ +
+
+

About This Server

+
+
+
+ +
+

+ This server provides a nearly complete implementation of the FHIR Specification + using a 100% open source software stack. It is hosted by University Health Network. +

+

+ The architecture in use here is shown in the image on the right. This server is built + from a number of modules of the + HAPI FHIR + project, which is a 100% open-source (Apache 2.0 Licensed) Java based + implementation of the FHIR specification. +

+

+ +

+
+
+
+
+

Data On This Server

+
+
+

+ This server is regularly loaded with a standard set of test data sourced + from UHN's own testing environment. Do not use this server to store any data + that you will need later, as we will be regularly resetting it. +

+

+ This is not a production server and it provides no privacy. Do not store any + confidential data here. +

+
+
+ +
+
+
+ +
+
+ + diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/tmpl-footer.html b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/tmpl-footer.html new file mode 100644 index 00000000000..bf18c498a78 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/tmpl-footer.html @@ -0,0 +1,16 @@ + + +
+ +
+ diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/tmpl-home-welcome.html b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/tmpl-home-welcome.html new file mode 100644 index 00000000000..dfc4769d6e4 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/tmpl-home-welcome.html @@ -0,0 +1,52 @@ + + +
+ +

+ This is the home for the FHIR test server operated by + University Health Network. This server + (and the testing application you are currently using to access it) + is entirely built using + HAPI-FHIR, + a 100% open-source Java implementation of the + FHIR specification. +

+

+ Here are some things you might wish to try: +

+
    +
  • + View a + list of patients + on this server. +
  • +
  • + Construct a + search query + on this server. +
  • +
  • + Access a + different server + (use the Server menu at the top of the page to see a list of public FHIR servers) +
  • +
+
+ +

+ You are accessing the public FHIR server + . This server is hosted elsewhere on the internet + but is being accessed using the HAPI client implementation. +

+
+

+ + + This is not a production server! + + Do not store any information here that contains personal health information + or any other confidential information. This server will be regularly purged + and reloaded with fixed test data. +

+
+ diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/web.xml b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..4dbadc86c1c --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,124 @@ + + + + org.springframework.web.context.ContextLoaderListener + + + contextClass + + org.springframework.web.context.support.AnnotationConfigWebApplicationContext + + + + contextConfigLocation + + ca.uhn.fhir.jpa.cds.example.FhirServerConfig + + + + + + + cdsServicesServlet + ca.uhn.fhir.jpa.cds.example.CdsHooksServerExample + 3 + + + + spring + org.springframework.web.servlet.DispatcherServlet + + contextClass + org.springframework.web.context.support.AnnotationConfigWebApplicationContext + + + contextConfigLocation + ca.uhn.fhir.jpa.cds.example.FhirTesterConfig + + 2 + + + + fhirServlet + ca.uhn.fhir.jpa.cds.example.CdsServerExample + + ImplementationDescription + FHIR JPA Server + + + FhirVersion + DSTU3 + + 1 + + + + cdsServicesServlet + /cds-services + + + + cdsServicesServlet + /cds-services/* + + + + fhirServlet + /baseDstu3/* + + + + spring + / + + + + + + + CORS Filter + org.ebaysf.web.cors.CORSFilter + + A comma separated list of allowed origins. Note: An '*' cannot be used for an allowed origin when using credentials. + cors.allowed.origins + * + + + A comma separated list of HTTP verbs, using which a CORS request can be made. + cors.allowed.methods + GET,POST,PUT,DELETE,OPTIONS + + + A comma separated list of allowed headers when making a non simple CORS request. + cors.allowed.headers + X-FHIR-Starter,Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Prefer + + + A comma separated list non-standard response headers that will be exposed to XHR2 object. + cors.exposed.headers + Location,Content-Location + + + A flag that suggests if CORS is supported with cookies + cors.support.credentials + true + + + A flag to control logging + cors.logging.enabled + true + + + Indicates how long (in seconds) the results of a preflight request can be cached in a preflight result cache. + cors.preflight.maxage + 300 + + + + CORS Filter + /* + + + + diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/javaee_6.xsd b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/javaee_6.xsd new file mode 100644 index 00000000000..9fb587749ce --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/javaee_6.xsd @@ -0,0 +1,2419 @@ + + + + + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2003-2009 Sun Microsystems, Inc. All rights reserved. + + The contents of this file are subject to the terms of either the + GNU General Public License Version 2 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with + the License. You can obtain a copy of the License at + https://glassfish.dev.java.net/public/CDDL+GPL.html or + glassfish/bootstrap/legal/LICENSE.txt. See the License for the + specific language governing permissions and limitations under the + License. + + When distributing the software, include this License Header + Notice in each file and include the License file at + glassfish/bootstrap/legal/LICENSE.txt. Sun designates this + particular file as subject to the "Classpath" exception as + provided by Sun in the GPL Version 2 section of the License file + that accompanied this code. If applicable, add the following + below the License Header, with the fields enclosed by brackets [] + replaced by your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + Contributor(s): + + If you wish your version of this file to be governed by only the + CDDL or only the GPL Version 2, indicate your decision by adding + "[Contributor] elects to include this software in this + distribution under the [CDDL or GPL Version 2] license." If you + don't indicate a single choice of license, a recipient has the + option to distribute your version of this file under either the + CDDL, the GPL Version 2 or to extend the choice of license to its + licensees as provided above. However, if you add GPL Version 2 + code and therefore, elected the GPL Version 2 license, then the + option applies only if the new code is made subject to such + option by the copyright holder. + + + + + + + + The following definitions that appear in the common + shareable schema(s) of Java EE deployment descriptors should be + interpreted with respect to the context they are included: + + Deployment Component may indicate one of the following: + java ee application; + application client; + web application; + enterprise bean; + resource adapter; + + Deployment File may indicate one of the following: + ear file; + war file; + jar file; + rar file; + + + + + + + + + + + This group keeps the usage of the contained description related + elements consistent across Java EE deployment descriptors. + + All elements may occur multiple times with different languages, + to support localization of the content. + + + + + + + + + + + + + + + This group keeps the usage of the contained JNDI environment + reference elements consistent across Java EE deployment descriptors. + + + + + + + + + + + + + + + + + + + + + + + This group collects elements that are common to most + JNDI resource elements. + + + + + + + + + + The JNDI name to be looked up to resolve a resource reference. + + + + + + + + + + + + This group collects elements that are common to all the + JNDI resource elements. It does not include the lookup-name + element, that is only applicable to some resource elements. + + + + + + + + + A product specific name that this resource should be + mapped to. The name of this resource, as defined by the + resource's name element or defaulted, is a name that is + local to the application component using the resource. + (It's a name in the JNDI java:comp/env namespace.) Many + application servers provide a way to map these local + names to names of resources known to the application + server. This mapped name is often a global JNDI name, + but may be a name of any form. + + Application servers are not required to support any + particular form or type of mapped name, nor the ability + to use mapped names. The mapped name is + product-dependent and often installation-dependent. No + use of a mapped name is portable. + + + + + + + + + + + + + + + + Configuration of a DataSource. + + + + + + + + + Description of this DataSource. + + + + + + + + + The name element specifies the JNDI name of the + data source being defined. + + + + + + + + + DataSource, XADataSource or ConnectionPoolDataSource + implementation class. + + + + + + + + + Database server name. + + + + + + + + + Port number where a server is listening for requests. + + + + + + + + + Name of a database on a server. + + + + + + + + url property is specified + along with other standard DataSource properties + such as serverName, databaseName + and portNumber, the more specific properties will + take precedence and url will be ignored. + + ]]> + + + + + + + + User name to use for connection authentication. + + + + + + + + + Password to use for connection authentication. + + + + + + + + + JDBC DataSource property. This may be a vendor-specific + property or a less commonly used DataSource property. + + + + + + + + + Sets the maximum time in seconds that this data source + will wait while attempting to connect to a database. + + + + + + + + + Set to false if connections should not participate in + transactions. + + + + + + + + + Isolation level for connections. + + + + + + + + + Number of connections that should be created when a + connection pool is initialized. + + + + + + + + + Maximum number of connections that should be concurrently + allocated for a connection pool. + + + + + + + + + Minimum number of connections that should be concurrently + allocated for a connection pool. + + + + + + + + + The number of seconds that a physical connection should + remain unused in the pool before the connection is + closed for a connection pool. + + + + + + + + + The total number of statements that a connection pool + should keep open. + + + + + + + + + + + + + + + + The description type is used by a description element to + provide text describing the parent element. The elements + that use this type should include any information that the + Deployment Component's Deployment File file producer wants + to provide to the consumer of the Deployment Component's + Deployment File (i.e., to the Deployer). Typically, the + tools used by such a Deployment File consumer will display + the description when processing the parent element that + contains the description. + + The lang attribute defines the language that the + description is provided in. The default value is "en" (English). + + + + + + + + + + + + + + + This type defines a dewey decimal that is used + to describe versions of documents. + + + + + + + + + + + + + + + + Employee Self Service + + + The value of the xml:lang attribute is "en" (English) by default. + + ]]> + + + + + + + + + + + + + + + + EmployeeRecord + + ../products/product.jar#ProductEJB + + ]]> + + + + + + + + + + + + + + + The ejb-local-refType is used by ejb-local-ref elements for + the declaration of a reference to an enterprise bean's local + home or to the local business interface of a 3.0 bean. + The declaration consists of: + + - an optional description + - the EJB reference name used in the code of the Deployment + Component that's referencing the enterprise bean. + - the optional expected type of the referenced enterprise bean + - the optional expected local interface of the referenced + enterprise bean or the local business interface of the + referenced enterprise bean. + - the optional expected local home interface of the referenced + enterprise bean. Not applicable if this ejb-local-ref refers + to the local business interface of a 3.0 bean. + - optional ejb-link information, used to specify the + referenced enterprise bean + - optional elements to define injection of the named enterprise + bean into a component field or property. + + + + + + + + + + + + + + + + + + + + + + ejb/Payroll + + ]]> + + + + + + + + + + + + + + + The ejb-refType is used by ejb-ref elements for the + declaration of a reference to an enterprise bean's home or + to the remote business interface of a 3.0 bean. + The declaration consists of: + + - an optional description + - the EJB reference name used in the code of + the Deployment Component that's referencing the enterprise + bean. + - the optional expected type of the referenced enterprise bean + - the optional remote interface of the referenced enterprise bean + or the remote business interface of the referenced enterprise + bean + - the optional expected home interface of the referenced + enterprise bean. Not applicable if this ejb-ref + refers to the remote business interface of a 3.0 bean. + - optional ejb-link information, used to specify the + referenced enterprise bean + - optional elements to define injection of the named enterprise + bean into a component field or property + + + + + + + + + + + + + + + + + + + + + + + The ejb-ref-typeType contains the expected type of the + referenced enterprise bean. + + The ejb-ref-type designates a value + that must be one of the following: + + Entity + Session + + + + + + + + + + + + + + + + + + + This type is used to designate an empty + element when used. + + + + + + + + + + + + + + The env-entryType is used to declare an application's + environment entry. The declaration consists of an optional + description, the name of the environment entry, a type + (optional if the value is injected, otherwise required), and + an optional value. + + It also includes optional elements to define injection of + the named resource into fields or JavaBeans properties. + + If a value is not specified and injection is requested, + no injection will occur and no entry of the specified name + will be created. This allows an initial value to be + specified in the source code without being incorrectly + changed when no override has been specified. + + If a value is not specified and no injection is requested, + a value must be supplied during deployment. + + This type is used by env-entry elements. + + + + + + + + + minAmount + + ]]> + + + + + + + java.lang.Integer + + ]]> + + + + + + + 100.00 + + ]]> + + + + + + + + + + + + + + + java.lang.Boolean + java.lang.Class + com.example.Color + + ]]> + + + + + + + + + + + + + + + The elements that use this type designate the name of a + Java class or interface. The name is in the form of a + "binary name", as defined in the JLS. This is the form + of name used in Class.forName(). Tools that need the + canonical name (the name used in source code) will need + to convert this binary name to the canonical name. + + + + + + + + + + + + + + + + This type defines four different values which can designate + boolean values. This includes values yes and no which are + not designated by xsd:boolean + + + + + + + + + + + + + + + + + + + + + The icon type contains small-icon and large-icon elements + that specify the file names for small and large GIF, JPEG, + or PNG icon images used to represent the parent element in a + GUI tool. + + The xml:lang attribute defines the language that the + icon file names are provided in. Its value is "en" (English) + by default. + + + + + + + + employee-service-icon16x16.jpg + + ]]> + + + + + + + employee-service-icon32x32.jpg + + ]]> + + + + + + + + + + + + + + + + An injection target specifies a class and a name within + that class into which a resource should be injected. + + The injection target class specifies the fully qualified + class name that is the target of the injection. The + Java EE specifications describe which classes can be an + injection target. + + The injection target name specifies the target within + the specified class. The target is first looked for as a + JavaBeans property name. If not found, the target is + looked for as a field name. + + The specified resource will be injected into the target + during initialization of the class by either calling the + set method for the target property or by setting a value + into the named field. + + + + + + + + + + + + + + The following transaction isolation levels are allowed + (see documentation for the java.sql.Connection interface): + TRANSACTION_READ_UNCOMMITTED + TRANSACTION_READ_COMMITTED + TRANSACTION_REPEATABLE_READ + TRANSACTION_SERIALIZABLE + + + + + + + + + + + + + + + + + + + The java-identifierType defines a Java identifier. + The users of this type should further verify that + the content does not contain Java reserved keywords. + + + + + + + + + + + + + + + + + + This is a generic type that designates a Java primitive + type or a fully qualified name of a Java interface/type, + or an array of such types. + + + + + + + + + + + + + + + + + : + + Example: + + jdbc:mysql://localhost:3307/testdb + + ]]> + + + + + + + + + + + + + + + + + The jndi-nameType type designates a JNDI name in the + Deployment Component's environment and is relative to the + java:comp/env context. A JNDI name must be unique within the + Deployment Component. + + + + + + + + + + + + + + + com.aardvark.payroll.PayrollHome + + ]]> + + + + + + + + + + + + + + + The lifecycle-callback type specifies a method on a + class to be called when a lifecycle event occurs. + Note that each class may have only one lifecycle callback + method for any given event and that the method may not + be overloaded. + + If the lifefycle-callback-class element is missing then + the class defining the callback is assumed to be the + component class in scope at the place in the descriptor + in which the callback definition appears. + + + + + + + + + + + + + + + + + The listenerType indicates the deployment properties for a web + application listener bean. + + + + + + + + + + The listener-class element declares a class in the + application must be registered as a web + application listener bean. The value is the fully + qualified classname of the listener class. + + + + + + + + + + + + + + + + The localType defines the fully-qualified name of an + enterprise bean's local interface. + + + + + + + + + + + + + + + + The local-homeType defines the fully-qualified + name of an enterprise bean's local home interface. + + + + + + + + + + + + + + + + This type is a general type that can be used to declare + parameter/value lists. + + + + + + + + + + The param-name element contains the name of a + parameter. + + + + + + + + + The param-value element contains the value of a + parameter. + + + + + + + + + + + + + + + + The elements that use this type designate either a relative + path or an absolute path starting with a "/". + + In elements that specify a pathname to a file within the + same Deployment File, relative filenames (i.e., those not + starting with "/") are considered relative to the root of + the Deployment File's namespace. Absolute filenames (i.e., + those starting with "/") also specify names in the root of + the Deployment File's namespace. In general, relative names + are preferred. The exception is .war files where absolute + names are preferred for consistency with the Servlet API. + + + + + + + + + + + + + + + + myPersistenceContext + + + + + myPersistenceContext + + PersistenceUnit1 + + Extended + + + ]]> + + + + + + + + + The persistence-context-ref-name element specifies + the name of a persistence context reference; its + value is the environment entry name used in + Deployment Component code. The name is a JNDI name + relative to the java:comp/env context. + + + + + + + + + The Application Assembler(or BeanProvider) may use the + following syntax to avoid the need to rename persistence + units to have unique names within a Java EE application. + + The Application Assembler specifies the pathname of the + root of the persistence.xml file for the referenced + persistence unit and appends the name of the persistence + unit separated from the pathname by #. The pathname is + relative to the referencing application component jar file. + In this manner, multiple persistence units with the same + persistence unit name may be uniquely identified when the + Application Assembler cannot change persistence unit names. + + + + + + + + + + Used to specify properties for the container or persistence + provider. Vendor-specific properties may be included in + the set of properties. Properties that are not recognized + by a vendor must be ignored. Entries that make use of the + namespace javax.persistence and its subnamespaces must not + be used for vendor-specific properties. The namespace + javax.persistence is reserved for use by the specification. + + + + + + + + + + + + + + + + + The persistence-context-typeType specifies the transactional + nature of a persistence context reference. + + The value of the persistence-context-type element must be + one of the following: + Transaction + Extended + + + + + + + + + + + + + + + + + + + Specifies a name/value pair. + + + + + + + + + + + + + + + + + + + + myPersistenceUnit + + + + + myPersistenceUnit + + PersistenceUnit1 + + + + ]]> + + + + + + + + + The persistence-unit-ref-name element specifies + the name of a persistence unit reference; its + value is the environment entry name used in + Deployment Component code. The name is a JNDI name + relative to the java:comp/env context. + + + + + + + + + The Application Assembler(or BeanProvider) may use the + following syntax to avoid the need to rename persistence + units to have unique names within a Java EE application. + + The Application Assembler specifies the pathname of the + root of the persistence.xml file for the referenced + persistence unit and appends the name of the persistence + unit separated from the pathname by #. The pathname is + relative to the referencing application component jar file. + In this manner, multiple persistence units with the same + persistence unit name may be uniquely identified when the + Application Assembler cannot change persistence unit names. + + + + + + + + + + + + + + + + com.wombat.empl.EmployeeService + + ]]> + + + + + + + + + + + + + + + jms/StockQueue + + javax.jms.Queue + + + + ]]> + + + + + + + + + The resource-env-ref-name element specifies the name + of a resource environment reference; its value is + the environment entry name used in + the Deployment Component code. The name is a JNDI + name relative to the java:comp/env context and must + be unique within a Deployment Component. + + + + + + + + + The resource-env-ref-type element specifies the type + of a resource environment reference. It is the + fully qualified name of a Java language class or + interface. + + + + + + + + + + + + + + + + + jdbc/EmployeeAppDB + javax.sql.DataSource + Container + Shareable + + + ]]> + + + + + + + + + The res-ref-name element specifies the name of a + resource manager connection factory reference. + The name is a JNDI name relative to the + java:comp/env context. + The name must be unique within a Deployment File. + + + + + + + + + The res-type element specifies the type of the data + source. The type is specified by the fully qualified + Java language class or interface + expected to be implemented by the data source. + + + + + + + + + + + + + + + + + + + The res-authType specifies whether the Deployment Component + code signs on programmatically to the resource manager, or + whether the Container will sign on to the resource manager + on behalf of the Deployment Component. In the latter case, + the Container uses information that is supplied by the + Deployer. + + The value must be one of the two following: + + Application + Container + + + + + + + + + + + + + + + + + + + The res-sharing-scope type specifies whether connections + obtained through the given resource manager connection + factory reference can be shared. The value, if specified, + must be one of the two following: + + Shareable + Unshareable + + The default value is Shareable. + + + + + + + + + + + + + + + + + + + The run-asType specifies the run-as identity to be + used for the execution of a component. It contains an + optional description, and the name of a security role. + + + + + + + + + + + + + + + + + + The role-nameType designates the name of a security role. + + The name must conform to the lexical rules for a token. + + + + + + + + + + + + + + + + + This role includes all employees who are authorized + to access the employee service application. + + employee + + + ]]> + + + + + + + + + + + + + + + + + The security-role-refType contains the declaration of a + security role reference in a component's or a + Deployment Component's code. The declaration consists of an + optional description, the security role name used in the + code, and an optional link to a security role. If the + security role is not specified, the Deployer must choose an + appropriate security role. + + + + + + + + + + The value of the role-name element must be the String used + as the parameter to the + EJBContext.isCallerInRole(String roleName) method or the + HttpServletRequest.isUserInRole(String role) method. + + + + + + + + + The role-link element is a reference to a defined + security role. The role-link element must contain + the name of one of the security roles defined in the + security-role elements. + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:QName. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:boolean. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:NMTOKEN. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:anyURI. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:integer. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:positiveInteger. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:nonNegativeInteger. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:string. + + + + + + + + + + + + + + + + + + This is a special string datatype that is defined by Java EE as + a base type for defining collapsed strings. When schemas + require trailing/leading space elimination as well as + collapsing the existing whitespace, this base type may be + used. + + + + + + + + + + + + + + + + + + This simple type designates a boolean with only two + permissible values + + - true + - false + + + + + + + + + + + + + + + + + + The url-patternType contains the url pattern of the mapping. + It must follow the rules specified in Section 11.2 of the + Servlet API Specification. This pattern is assumed to be in + URL-decoded form and must not contain CR(#xD) or LF(#xA). + If it contains those characters, the container must inform + the developer with a descriptive error message. + The container must preserve all characters including whitespaces. + + + + + + + + + + + + + + + + CorporateStocks + + + + ]]> + + + + + + + + + The message-destination-name element specifies a + name for a message destination. This name must be + unique among the names of message destinations + within the Deployment File. + + + + + + + + + A product specific name that this message destination + should be mapped to. Each message-destination-ref + element that references this message destination will + define a name in the namespace of the referencing + component or in one of the other predefined namespaces. + Many application servers provide a way to map these + local names to names of resources known to the + application server. This mapped name is often a global + JNDI name, but may be a name of any form. Each of the + local names should be mapped to this same global name. + + Application servers are not required to support any + particular form or type of mapped name, nor the ability + to use mapped names. The mapped name is + product-dependent and often installation-dependent. No + use of a mapped name is portable. + + + + + + + + + The JNDI name to be looked up to resolve the message destination. + + + + + + + + + + + + + + + + jms/StockQueue + + javax.jms.Queue + + Consumes + + CorporateStocks + + + + ]]> + + + + + + + + + The message-destination-ref-name element specifies + the name of a message destination reference; its + value is the environment entry name used in + Deployment Component code. + + + + + + + + + + + + + + + + + + + + The message-destination-usageType specifies the use of the + message destination indicated by the reference. The value + indicates whether messages are consumed from the message + destination, produced for the destination, or both. The + Assembler makes use of this information in linking producers + of a destination with its consumers. + + The value of the message-destination-usage element must be + one of the following: + Consumes + Produces + ConsumesProduces + + + + + + + + + + + + + + + + + + + javax.jms.Queue + + + ]]> + + + + + + + + + + + + + + + The message-destination-linkType is used to link a message + destination reference or message-driven bean to a message + destination. + + The Assembler sets the value to reflect the flow of messages + between producers and consumers in the application. + + The value must be the message-destination-name of a message + destination in the same Deployment File or in another + Deployment File in the same Java EE application unit. + + Alternatively, the value may be composed of a path name + specifying a Deployment File containing the referenced + message destination with the message-destination-name of the + destination appended and separated from the path name by + "#". The path name is relative to the Deployment File + containing Deployment Component that is referencing the + message destination. This allows multiple message + destinations with the same name to be uniquely identified. + + + + + + + + + diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/jsp_2_2.xsd b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/jsp_2_2.xsd new file mode 100644 index 00000000000..fa41e4266f1 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/jsp_2_2.xsd @@ -0,0 +1,389 @@ + + + + + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2003-2009 Sun Microsystems, Inc. All rights reserved. + + The contents of this file are subject to the terms of either the + GNU General Public License Version 2 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with + the License. You can obtain a copy of the License at + https://glassfish.dev.java.net/public/CDDL+GPL.html or + glassfish/bootstrap/legal/LICENSE.txt. See the License for the + specific language governing permissions and limitations under the + License. + + When distributing the software, include this License Header + Notice in each file and include the License file at + glassfish/bootstrap/legal/LICENSE.txt. Sun designates this + particular file as subject to the "Classpath" exception as + provided by Sun in the GPL Version 2 section of the License file + that accompanied this code. If applicable, add the following + below the License Header, with the fields enclosed by brackets [] + replaced by your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + Contributor(s): + + If you wish your version of this file to be governed by only the + CDDL or only the GPL Version 2, indicate your decision by adding + "[Contributor] elects to include this software in this + distribution under the [CDDL or GPL Version 2] license." If you + don't indicate a single choice of license, a recipient has the + option to distribute your version of this file under either the + CDDL, the GPL Version 2 or to extend the choice of license to its + licensees as provided above. However, if you add GPL Version 2 + code and therefore, elected the GPL Version 2 license, then the + option applies only if the new code is made subject to such + option by the copyright holder. + + + + + + + + This is the XML Schema for the JSP 2.2 deployment descriptor + types. The JSP 2.2 schema contains all the special + structures and datatypes that are necessary to use JSP files + from a web application. + + The contents of this schema is used by the web-common_3_0.xsd + file to define JSP specific content. + + + + + + + + The following conventions apply to all Java EE + deployment descriptor elements unless indicated otherwise. + + - In elements that specify a pathname to a file within the + same JAR file, relative filenames (i.e., those not + starting with "/") are considered relative to the root of + the JAR file's namespace. Absolute filenames (i.e., those + starting with "/") also specify names in the root of the + JAR file's namespace. In general, relative names are + preferred. The exception is .war files where absolute + names are preferred for consistency with the Servlet API. + + + + + + + + + + + + + + The jsp-configType is used to provide global configuration + information for the JSP files in a web application. It has + two subelements, taglib and jsp-property-group. + + + + + + + + + + + + + + + + + + The jsp-file element contains the full path to a JSP file + within the web application beginning with a `/'. + + + + + + + + + + + + + + + + The jsp-property-groupType is used to group a number of + files so they can be given global property information. + All files so described are deemed to be JSP files. The + following additional properties can be described: + + - Control whether EL is ignored. + - Control whether scripting elements are invalid. + - Indicate pageEncoding information. + - Indicate that a resource is a JSP document (XML). + - Prelude and Coda automatic includes. + - Control whether the character sequence #{ is allowed + when used as a String literal. + - Control whether template text containing only + whitespaces must be removed from the response output. + - Indicate the default contentType information. + - Indicate the default buffering model for JspWriter + - Control whether error should be raised for the use of + undeclared namespaces in a JSP page. + + + + + + + + + + + Can be used to easily set the isELIgnored + property of a group of JSP pages. By default, the + EL evaluation is enabled for Web Applications using + a Servlet 2.4 or greater web.xml, and disabled + otherwise. + + + + + + + + + The valid values of page-encoding are those of the + pageEncoding page directive. It is a + translation-time error to name different encodings + in the pageEncoding attribute of the page directive + of a JSP page and in a JSP configuration element + matching the page. It is also a translation-time + error to name different encodings in the prolog + or text declaration of a document in XML syntax and + in a JSP configuration element matching the document. + It is legal to name the same encoding through + mulitple mechanisms. + + + + + + + + + Can be used to easily disable scripting in a + group of JSP pages. By default, scripting is + enabled. + + + + + + + + + If true, denotes that the group of resources + that match the URL pattern are JSP documents, + and thus must be interpreted as XML documents. + If false, the resources are assumed to not + be JSP documents, unless there is another + property group that indicates otherwise. + + + + + + + + + The include-prelude element is a context-relative + path that must correspond to an element in the + Web Application. When the element is present, + the given path will be automatically included (as + in an include directive) at the beginning of each + JSP page in this jsp-property-group. + + + + + + + + + The include-coda element is a context-relative + path that must correspond to an element in the + Web Application. When the element is present, + the given path will be automatically included (as + in an include directive) at the end of each + JSP page in this jsp-property-group. + + + + + + + + + The character sequence #{ is reserved for EL expressions. + Consequently, a translation error occurs if the #{ + character sequence is used as a String literal, unless + this element is enabled (true). Disabled (false) by + default. + + + + + + + + + Indicates that template text containing only whitespaces + must be removed from the response output. It has no + effect on JSP documents (XML syntax). Disabled (false) + by default. + + + + + + + + + The valid values of default-content-type are those of the + contentType page directive. It specifies the default + response contentType if the page directive does not include + a contentType attribute. + + + + + + + + + The valid values of buffer are those of the + buffer page directive. It specifies if buffering should be + used for the output to response, and if so, the size of the + buffer to use. + + + + + + + + + The default behavior when a tag with unknown namespace is used + in a JSP page (regular syntax) is to silently ignore it. If + set to true, then an error must be raised during the translation + time when an undeclared tag is used in a JSP page. Disabled + (false) by default. + + + + + + + + + + + + + + + + The taglibType defines the syntax for declaring in + the deployment descriptor that a tag library is + available to the application. This can be done + to override implicit map entries from TLD files and + from the container. + + + + + + + + + A taglib-uri element describes a URI identifying a + tag library used in the web application. The body + of the taglib-uri element may be either an + absolute URI specification, or a relative URI. + There should be no entries in web.xml with the + same taglib-uri value. + + + + + + + + + the taglib-location element contains the location + (as a resource relative to the root of the web + application) where to find the Tag Library + Description file for the tag library. + + + + + + + + + diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/web-app_3_0.xsd b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/web-app_3_0.xsd new file mode 100644 index 00000000000..bbcdf43cd3a --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/web-app_3_0.xsd @@ -0,0 +1,272 @@ + + + + + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2003-2009 Sun Microsystems, Inc. All rights reserved. + + The contents of this file are subject to the terms of either the + GNU General Public License Version 2 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with + the License. You can obtain a copy of the License at + https://glassfish.dev.java.net/public/CDDL+GPL.html or + glassfish/bootstrap/legal/LICENSE.txt. See the License for the + specific language governing permissions and limitations under the + License. + + When distributing the software, include this License Header + Notice in each file and include the License file at + glassfish/bootstrap/legal/LICENSE.txt. Sun designates this + particular file as subject to the "Classpath" exception as + provided by Sun in the GPL Version 2 section of the License file + that accompanied this code. If applicable, add the following + below the License Header, with the fields enclosed by brackets [] + replaced by your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + Contributor(s): + + If you wish your version of this file to be governed by only the + CDDL or only the GPL Version 2, indicate your decision by adding + "[Contributor] elects to include this software in this + distribution under the [CDDL or GPL Version 2] license." If you + don't indicate a single choice of license, a recipient has the + option to distribute your version of this file under either the + CDDL, the GPL Version 2 or to extend the choice of license to its + licensees as provided above. However, if you add GPL Version 2 + code and therefore, elected the GPL Version 2 license, then the + option applies only if the new code is made subject to such + option by the copyright holder. + + + + + + + + ... + + + The instance documents may indicate the published version of + the schema using the xsi:schemaLocation attribute for Java EE + namespace with the following location: + + http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd + + ]]> + + + + + + + The following conventions apply to all Java EE + deployment descriptor elements unless indicated otherwise. + + - In elements that specify a pathname to a file within the + same JAR file, relative filenames (i.e., those not + starting with "/") are considered relative to the root of + the JAR file's namespace. Absolute filenames (i.e., those + starting with "/") also specify names in the root of the + JAR file's namespace. In general, relative names are + preferred. The exception is .war files where absolute + names are preferred for consistency with the Servlet API. + + + + + + + + + + + + + + The web-app element is the root of the deployment + descriptor for a web application. Note that the sub-elements + of this element can be in the arbitrary order. Because of + that, the multiplicity of the elements of distributable, + session-config, welcome-file-list, jsp-config, login-config, + and locale-encoding-mapping-list was changed from "?" to "*" + in this schema. However, the deployment descriptor instance + file must not contain multiple elements of session-config, + jsp-config, and login-config. When there are multiple elements of + welcome-file-list or locale-encoding-mapping-list, the container + must concatenate the element contents. The multiple occurence + of the element distributable is redundant and the container + treats that case exactly in the same way when there is only + one distributable. + + + + + + + + The servlet element contains the name of a servlet. + The name must be unique within the web application. + + + + + + + + + + + The filter element contains the name of a filter. + The name must be unique within the web application. + + + + + + + + + + + The ejb-local-ref-name element contains the name of an EJB + reference. The EJB reference is an entry in the web + application's environment and is relative to the + java:comp/env context. The name must be unique within + the web application. + + It is recommended that name is prefixed with "ejb/". + + + + + + + + + + + The ejb-ref-name element contains the name of an EJB + reference. The EJB reference is an entry in the web + application's environment and is relative to the + java:comp/env context. The name must be unique within + the web application. + + It is recommended that name is prefixed with "ejb/". + + + + + + + + + + + The resource-env-ref-name element specifies the name of + a resource environment reference; its value is the + environment entry name used in the web application code. + The name is a JNDI name relative to the java:comp/env + context and must be unique within a web application. + + + + + + + + + + + The message-destination-ref-name element specifies the name of + a message destination reference; its value is the + environment entry name used in the web application code. + The name is a JNDI name relative to the java:comp/env + context and must be unique within a web application. + + + + + + + + + + + The res-ref-name element specifies the name of a + resource manager connection factory reference. The name + is a JNDI name relative to the java:comp/env context. + The name must be unique within a web application. + + + + + + + + + + + The env-entry-name element contains the name of a web + application's environment entry. The name is a JNDI + name relative to the java:comp/env context. The name + must be unique within a web application. + + + + + + + + + + + A role-name-key is specified to allow the references + from the security-role-refs. + + + + + + + + + + + The keyref indicates the references from + security-role-ref to a specified role-name. + + + + + + + + + diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/web-common_3_0.xsd b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/web-common_3_0.xsd new file mode 100644 index 00000000000..f994bc2c651 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/web-common_3_0.xsd @@ -0,0 +1,1575 @@ + + + + + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2003-2009 Sun Microsystems, Inc. All rights reserved. + + The contents of this file are subject to the terms of either the + GNU General Public License Version 2 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with + the License. You can obtain a copy of the License at + https://glassfish.dev.java.net/public/CDDL+GPL.html or + glassfish/bootstrap/legal/LICENSE.txt. See the License for the + specific language governing permissions and limitations under the + License. + + When distributing the software, include this License Header + Notice in each file and include the License file at + glassfish/bootstrap/legal/LICENSE.txt. Sun designates this + particular file as subject to the "Classpath" exception as + provided by Sun in the GPL Version 2 section of the License file + that accompanied this code. If applicable, add the following + below the License Header, with the fields enclosed by brackets [] + replaced by your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + Contributor(s): + + If you wish your version of this file to be governed by only the + CDDL or only the GPL Version 2, indicate your decision by adding + "[Contributor] elects to include this software in this + distribution under the [CDDL or GPL Version 2] license." If you + don't indicate a single choice of license, a recipient has the + option to distribute your version of this file under either the + CDDL, the GPL Version 2 or to extend the choice of license to its + licensees as provided above. However, if you add GPL Version 2 + code and therefore, elected the GPL Version 2 license, then the + option applies only if the new code is made subject to such + option by the copyright holder. + + + + + + + + ... + + + The instance documents may indicate the published version of + the schema using the xsi:schemaLocation attribute for Java EE + namespace with the following location: + + http://java.sun.com/xml/ns/javaee/web-common_3_0.xsd + + ]]> + + + + + + + The following conventions apply to all Java EE + deployment descriptor elements unless indicated otherwise. + + - In elements that specify a pathname to a file within the + same JAR file, relative filenames (i.e., those not + starting with "/") are considered relative to the root of + the JAR file's namespace. Absolute filenames (i.e., those + starting with "/") also specify names in the root of the + JAR file's namespace. In general, relative names are + preferred. The exception is .war files where absolute + names are preferred for consistency with the Servlet API. + + + + + + + + + + + + + + + + + The context-param element contains the declaration + of a web application's servlet context + initialization parameters. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The metadata-complete attribute defines whether this + deployment descriptor and other related deployment + descriptors for this module (e.g., web service + descriptors) are complete, or whether the class + files available to this module and packaged with + this application should be examined for annotations + that specify deployment information. + + If metadata-complete is set to "true", the deployment + tool must ignore any annotations that specify deployment + information, which might be present in the class files + of the application. + + If metadata-complete is not specified or is set to + "false", the deployment tool must examine the class + files of the application for annotations, as + specified by the specifications. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The auth-constraintType indicates the user roles that + should be permitted access to this resource + collection. The role-name used here must either correspond + to the role-name of one of the security-role elements + defined for this web application, or be the specially + reserved role-name "*" that is a compact syntax for + indicating all roles in the web application. If both "*" + and rolenames appear, the container interprets this as all + roles. If no roles are defined, no user is allowed access + to the portion of the web application described by the + containing security-constraint. The container matches + role names case sensitively when determining access. + + + + + + + + + + + + + + + + + + The auth-methodType is used to configure the authentication + mechanism for the web application. As a prerequisite to + gaining access to any web resources which are protected by + an authorization constraint, a user must have authenticated + using the configured mechanism. Legal values are "BASIC", + "DIGEST", "FORM", "CLIENT-CERT", or a vendor-specific + authentication scheme. + + Used in: login-config + + + + + + + + + + + + + + + + The dispatcher has five legal values: FORWARD, REQUEST, + INCLUDE, ASYNC, and ERROR. + + A value of FORWARD means the Filter will be applied under + RequestDispatcher.forward() calls. + A value of REQUEST means the Filter will be applied under + ordinary client calls to the path or servlet. + A value of INCLUDE means the Filter will be applied under + RequestDispatcher.include() calls. + A value of ASYNC means the Filter will be applied under + calls dispatched from an AsyncContext. + A value of ERROR means the Filter will be applied under the + error page mechanism. + + The absence of any dispatcher elements in a filter-mapping + indicates a default of applying filters only under ordinary + client calls to the path or servlet. + + + + + + + + + + + + + + + + + + + + + + The error-code contains an HTTP error code, ex: 404 + + Used in: error-page + + + + + + + + + + + + + + + + + + + The error-pageType contains a mapping between an error code + or exception type to the path of a resource in the web + application. + + Error-page declarations using the exception-type element in + the deployment descriptor must be unique up to the class name of + the exception-type. Similarly, error-page declarations using the + status-code element must be unique in the deployment descriptor + up to the status code. + + Used in: web-app + + + + + + + + + + + The exception-type contains a fully qualified class + name of a Java exception type. + + + + + + + + + + The location element contains the location of the + resource in the web application relative to the root of + the web application. The value of the location must have + a leading `/'. + + + + + + + + + + + + + + + + The filterType is used to declare a filter in the web + application. The filter is mapped to either a servlet or a + URL pattern in the filter-mapping element, using the + filter-name value to reference. Filters can access the + initialization parameters declared in the deployment + descriptor at runtime via the FilterConfig interface. + + Used in: web-app + + + + + + + + + + + The fully qualified classname of the filter. + + + + + + + + + + The init-param element contains a name/value pair as + an initialization param of a servlet filter + + + + + + + + + + + + + + + + Declaration of the filter mappings in this web + application is done by using filter-mappingType. + The container uses the filter-mapping + declarations to decide which filters to apply to a request, + and in what order. The container matches the request URI to + a Servlet in the normal way. To determine which filters to + apply it matches filter-mapping declarations either on + servlet-name, or on url-pattern for each filter-mapping + element, depending on which style is used. The order in + which filters are invoked is the order in which + filter-mapping declarations that match a request URI for a + servlet appear in the list of filter-mapping elements.The + filter-name value must be the value of the filter-name + sub-elements of one of the filter declarations in the + deployment descriptor. + + + + + + + + + + + + + + + + + + + + + + This type defines a string which contains at least one + character. + + + + + + + + + + + + + + + + + + The logical name of the filter is declare + by using filter-nameType. This name is used to map the + filter. Each filter name is unique within the web + application. + + Used in: filter, filter-mapping + + + + + + + + + + + + + + + + The form-login-configType specifies the login and error + pages that should be used in form based login. If form based + authentication is not used, these elements are ignored. + + Used in: login-config + + + + + + + + + The form-login-page element defines the location in the web + app where the page that can be used for login can be + found. The path begins with a leading / and is interpreted + relative to the root of the WAR. + + + + + + + + + The form-error-page element defines the location in + the web app where the error page that is displayed + when login is not successful can be found. + The path begins with a leading / and is interpreted + relative to the root of the WAR. + + + + + + + + + + + + + A HTTP method type as defined in HTTP 1.1 section 2.2. + + + + + + + + + + + + + + + + + + + + + + + + + + The login-configType is used to configure the authentication + method that should be used, the realm name that should be + used for this application, and the attributes that are + needed by the form login mechanism. + + Used in: web-app + + + + + + + + + + The realm name element specifies the realm name to + use in HTTP Basic authorization. + + + + + + + + + + + + + + + + + The mime-mappingType defines a mapping between an extension + and a mime type. + + Used in: web-app + + + + + + + + The extension element contains a string describing an + extension. example: "txt" + + + + + + + + + + + + + + + + + The mime-typeType is used to indicate a defined mime type. + + Example: + "text/plain" + + Used in: mime-mapping + + + + + + + + + + + + + + + + + + The security-constraintType is used to associate + security constraints with one or more web resource + collections + + Used in: web-app + + + + + + + + + + + + + + + + + + + + The servletType is used to declare a servlet. + It contains the declarative data of a + servlet. If a jsp-file is specified and the load-on-startup + element is present, then the JSP should be precompiled and + loaded. + + Used in: web-app + + + + + + + + + + + + The servlet-class element contains the fully + qualified class name of the servlet. + + + + + + + + + + + + The load-on-startup element indicates that this + servlet should be loaded (instantiated and have + its init() called) on the startup of the web + application. The optional contents of these + element must be an integer indicating the order in + which the servlet should be loaded. If the value + is a negative integer, or the element is not + present, the container is free to load the servlet + whenever it chooses. If the value is a positive + integer or 0, the container must load and + initialize the servlet as the application is + deployed. The container must guarantee that + servlets marked with lower integers are loaded + before servlets marked with higher integers. The + container may choose the order of loading of + servlets with the same load-on-start-up value. + + + + + + + + + + + + + + + + + + + + + The servlet-mappingType defines a mapping between a + servlet and a url pattern. + + Used in: web-app + + + + + + + + + + + + + + + + + + The servlet-name element contains the canonical name of the + servlet. Each servlet name is unique within the web + application. + + + + + + + + + + + + + + + + The session-configType defines the session parameters + for this web application. + + Used in: web-app + + + + + + + + + The session-timeout element defines the default + session timeout interval for all sessions created + in this web application. The specified timeout + must be expressed in a whole number of minutes. + If the timeout is 0 or less, the container ensures + the default behaviour of sessions is never to time + out. If this element is not specified, the container + must set its default timeout period. + + + + + + + + + The cookie-config element defines the configuration of the + session tracking cookies created by this web application. + + + + + + + + + The tracking-mode element defines the tracking modes + for sessions created by this web application + + + + + + + + + + + + + + + + The cookie-configType defines the configuration for the + session tracking cookies of this web application. + + Used in: session-config + + + + + + + + + The name that will be assigned to any session tracking + cookies created by this web application. + The default is JSESSIONID + + + + + + + + + The domain name that will be assigned to any session tracking + cookies created by this web application. + + + + + + + + + The path that will be assigned to any session tracking + cookies created by this web application. + + + + + + + + + The comment that will be assigned to any session tracking + cookies created by this web application. + + + + + + + + + Specifies whether any session tracking cookies created + by this web application will be marked as HttpOnly + + + + + + + + + Specifies whether any session tracking cookies created + by this web application will be marked as secure + even if the request that initiated the corresponding session + is using plain HTTP instead of HTTPS + + + + + + + + + The lifetime (in seconds) that will be assigned to any + session tracking cookies created by this web application. + Default is -1 + + + + + + + + + + + + + + + + The name that will be assigned to any session tracking + cookies created by this web application. + The default is JSESSIONID + + Used in: cookie-config + + + + + + + + + + + + + + + + The domain name that will be assigned to any session tracking + cookies created by this web application. + + Used in: cookie-config + + + + + + + + + + + + + + + + The path that will be assigned to any session tracking + cookies created by this web application. + + Used in: cookie-config + + + + + + + + + + + + + + + + The comment that will be assigned to any session tracking + cookies created by this web application. + + Used in: cookie-config + + + + + + + + + + + + + + + + The tracking modes for sessions created by this web + application + + Used in: session-config + + + + + + + + + + + + + + + + + + + + The transport-guaranteeType specifies that the communication + between client and server should be NONE, INTEGRAL, or + CONFIDENTIAL. NONE means that the application does not + require any transport guarantees. A value of INTEGRAL means + that the application requires that the data sent between the + client and server be sent in such a way that it can't be + changed in transit. CONFIDENTIAL means that the application + requires that the data be transmitted in a fashion that + prevents other entities from observing the contents of the + transmission. In most cases, the presence of the INTEGRAL or + CONFIDENTIAL flag will indicate that the use of SSL is + required. + + Used in: user-data-constraint + + + + + + + + + + + + + + + + + + + + The user-data-constraintType is used to indicate how + data communicated between the client and container should be + protected. + + Used in: security-constraint + + + + + + + + + + + + + + + + + + The elements that use this type designate a path starting + with a "/" and interpreted relative to the root of a WAR + file. + + + + + + + + + + + + + + + This type contains the recognized versions of + web-application supported. It is used to designate the + version of the web application. + + + + + + + + + + + + + + + + The web-resource-collectionType is used to identify the + resources and HTTP methods on those resources to which a + security constraint applies. If no HTTP methods are specified, + then the security constraint applies to all HTTP methods. + If HTTP methods are specified by http-method-omission + elements, the security constraint applies to all methods + except those identified in the collection. + http-method-omission and http-method elements are never + mixed in the same collection. + + Used in: security-constraint + + + + + + + + + The web-resource-name contains the name of this web + resource collection. + + + + + + + + + + + + Each http-method names an HTTP method to which the + constraint applies. + + + + + + + + + Each http-method-omission names an HTTP method to + which the constraint does not apply. + + + + + + + + + + + + + + + + + The welcome-file-list contains an ordered list of welcome + files elements. + + Used in: web-app + + + + + + + + + The welcome-file element contains file name to use + as a default welcome file, such as index.html + + + + + + + + + + + + + The localeType defines valid locale defined by ISO-639-1 + and ISO-3166. + + + + + + + + + + + + + The encodingType defines IANA character sets. + + + + + + + + + + + + + + + + The locale-encoding-mapping-list contains one or more + locale-encoding-mapping(s). + + + + + + + + + + + + + + + + + The locale-encoding-mapping contains locale name and + encoding name. The locale name must be either "Language-code", + such as "ja", defined by ISO-639 or "Language-code_Country-code", + such as "ja_JP". "Country code" is defined by ISO-3166. + + + + + + + + + + + + + + + + + + This element indicates that the ordering sub-element in which + it was placed should take special action regarding the ordering + of this application resource relative to other application + configuration resources. + See section 8.2.2 of the specification for details. + + + + + + + + + + + + + + Please see section 8.2.2 of the specification for details. + + + + + + + + + + + + + + + + + Please see section 8.2.2 of the specification for details. + + + + + + + + + + + + + + + + + This element contains a sequence of "name" elements, each of + which + refers to an application configuration resource by the "name" + declared on its web.xml fragment. This element can also contain + a single "others" element which specifies that this document + comes + before or after other documents within the application. + See section 8.2.2 of the specification for details. + + + + + + + + + + + + + + + + + This element specifies configuration information related to the + handling of multipart/form-data requests. + + + + + + + + + The directory location where uploaded files will be stored + + + + + + + + + The maximum size limit of uploaded files + + + + + + + + + The maximum size limit of multipart/form-data requests + + + + + + + + + The size threshold after which an uploaded file will be + written to disk + + + + + + + + diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/xml.xsd b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/xml.xsd new file mode 100644 index 00000000000..aea7d0db0a4 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/xsd/xml.xsd @@ -0,0 +1,287 @@ + + + + + + +
+

About the XML namespace

+ +
+

+ This schema document describes the XML namespace, in a form + suitable for import by other schema documents. +

+

+ See + http://www.w3.org/XML/1998/namespace.html and + + http://www.w3.org/TR/REC-xml for information + about this namespace. +

+

+ Note that local names in this namespace are intended to be + defined only by the World Wide Web Consortium or its subgroups. + The names currently defined in this namespace are listed below. + They should not be used with conflicting semantics by any Working + Group, specification, or document instance. +

+

+ See further below in this document for more information about how to refer to this schema document from your own + XSD schema documents and about the + namespace-versioning policy governing this schema document. +

+
+
+
+
+ + + + +
+ +

lang (as an attribute name)

+

+ denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification.

+ +
+
+

Notes

+

+ Attempting to install the relevant ISO 2- and 3-letter + codes as the enumerated possible values is probably never + going to be a realistic possibility. +

+

+ See BCP 47 at + http://www.rfc-editor.org/rfc/bcp/bcp47.txt + and the IANA language subtag registry at + + http://www.iana.org/assignments/language-subtag-registry + for further information. +

+

+ The union allows for the 'un-declaration' of xml:lang with + the empty string. +

+
+
+
+ + + + + + + + + +
+ + + + +
+ +

space (as an attribute name)

+

+ denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification.

+ +
+
+
+ + + + + + +
+ + + +
+ +

base (as an attribute name)

+

+ denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification.

+ +

+ See http://www.w3.org/TR/xmlbase/ + for information about this attribute. +

+
+
+
+
+ + + + +
+ +

id (as an attribute name)

+

+ denotes an attribute whose value + should be interpreted as if declared to be of type ID. + This name is reserved by virtue of its definition in the + xml:id specification.

+ +

+ See http://www.w3.org/TR/xml-id/ + for information about this attribute. +

+
+
+
+
+ + + + + + + + + + +
+ +

Father (in any context at all)

+ +
+

+ denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: +

+
+

+ In appreciation for his vision, leadership and + dedication the W3C XML Plenary on this 10th day of + February, 2000, reserves for Jon Bosak in perpetuity + the XML name "xml:Father". +

+
+
+
+
+
+ + + +
+

About this schema document

+ +
+

+ This schema defines attributes and an attribute group suitable + for use by schemas wishing to allow xml:base, + xml:lang, xml:space or + xml:id attributes on elements they define. +

+

+ To enable this, such a schema must import this schema for + the XML namespace, e.g. as follows: +

+
+          <schema . . .>
+           . . .
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     
+

+ or +

+
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     
+

+ Subsequently, qualified reference to any of the attributes or the + group defined below will have the desired effect, e.g. +

+
+          <type . . .>
+           . . .
+           <attributeGroup ref="xml:specialAttrs"/>
+     
+

+ will define a type which will schema-validate an instance element + with any of those attributes. +

+
+
+
+
+ + + +
+

Versioning policy for this schema document

+
+

+ In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + + http://www.w3.org/2009/01/xml.xsd. +

+

+ At the date of issue it can also be found at + + http://www.w3.org/2001/xml.xsd. +

+

+ The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML + Schema itself, or with the XML namespace itself. In other words, + if the XML Schema or XML namespaces change, the version of this + document at + http://www.w3.org/2001/xml.xsd + + will change accordingly; the version at + + http://www.w3.org/2009/01/xml.xsd + + will not change. +

+

+ Previous dated (and unchanging) versions of this schema + document are at: +

+ +
+
+
+
+ +
+ diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/java/ca/uhn/fhir/jpa/cds/example/CdsExampleTests.java b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/java/ca/uhn/fhir/jpa/cds/example/CdsExampleTests.java new file mode 100644 index 00000000000..1cf434289ad --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/java/ca/uhn/fhir/jpa/cds/example/CdsExampleTests.java @@ -0,0 +1,260 @@ +package ca.uhn.fhir.jpa.cds.example; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.client.api.IGenericClient; +import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; +import ca.uhn.fhir.rest.server.IResourceProvider; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.webapp.WebAppContext; +import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.ServerSocket; +import java.net.URL; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Scanner; + +public class CdsExampleTests { + private static IGenericClient ourClient; + private static FhirContext ourCtx = FhirContext.forDstu3(); + + protected static int ourPort; + + private static Server ourServer; + private static String ourServerBase; + + private static Collection providers; + + @BeforeClass + public static void beforeClass() throws Exception { + + String path = Paths.get("").toAbsolutePath().toString(); + + ourPort = RandomServerPortProvider.findFreePort(); + ourServer = new Server(ourPort); + + WebAppContext webAppContext = new WebAppContext(); + webAppContext.setContextPath("/hapi-fhir-jpaserver-cds"); + webAppContext.setDescriptor(path + "/src/main/webapp/WEB-INF/web.xml"); + webAppContext.setResourceBase(path + "/target/hapi-fhir-jpaserver-cds"); + webAppContext.setParentLoaderPriority(true); + + ourServer.setHandler(webAppContext); + ourServer.start(); + + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); + ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver-cds/baseDstu3"; + ourClient = ourCtx.newRestfulGenericClient(ourServerBase); + ourClient.registerInterceptor(new LoggingInterceptor(true)); + + // Load test data + // Normally, I would use a transaction bundle, but issues with the random ports prevents that... + // So, doing it the old-fashioned way =) + + // General + putResource("general-practitioner.json", "Practitioner-12208"); + putResource("general-patient.json", "Patient-12214"); + putResource("general-fhirhelpers-3.json", "FHIRHelpers"); + } + + @AfterClass + public static void afterClass() throws Exception { + ourServer.stop(); + } + + private static void putResource(String resourceFileName, String id) { + InputStream is = CdsExampleTests.class.getResourceAsStream(resourceFileName); + Scanner scanner = new Scanner(is).useDelimiter("\\A"); + String json = scanner.hasNext() ? scanner.next() : ""; + IBaseResource resource = ourCtx.newJsonParser().parseResource(json); + ourClient.update(id, resource); + } + + @Test + public void MeasureProcessingTest() { + putResource("measure-processing-library.json", "col-logic"); + putResource("measure-processing-measure.json", "col"); + putResource("measure-processing-procedure.json", "Procedure-9"); + putResource("measure-processing-condition.json", "Condition-13"); + putResource("measure-processing-valueset-1.json", "2.16.840.1.113883.3.464.1003.108.11.1001"); + putResource("measure-processing-valueset-2.json", "2.16.840.1.113883.3.464.1003.198.12.1019"); + putResource("measure-processing-valueset-3.json", "2.16.840.1.113883.3.464.1003.108.12.1020"); + putResource("measure-processing-valueset-4.json", "2.16.840.1.113883.3.464.1003.198.12.1010"); + putResource("measure-processing-valueset-5.json", "2.16.840.1.113883.3.464.1003.198.12.1011"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("patient").setValue(new StringType("Patient-12214")); + inParams.addParameter().setName("startPeriod").setValue(new DateType("2001-01-01")); + inParams.addParameter().setName("endPeriod").setValue(new DateType("2015-03-01")); + + Parameters outParams = ourClient + .operation() + .onInstance(new IdDt("Measure", "col")) + .named("$evaluate") + .withParameters(inParams) + .useHttpGet() + .execute(); + + List response = outParams.getParameter(); + + Assert.assertTrue(!response.isEmpty()); + + Parameters.ParametersParameterComponent component = response.get(0); + + Assert.assertTrue(component.getResource() instanceof MeasureReport); + + MeasureReport report = (MeasureReport) component.getResource(); + + Assert.assertTrue(report.getEvaluatedResources() != null); + + for (MeasureReport.MeasureReportGroupComponent group : report.getGroup()) { + if (group.getIdentifier().getValue().equals("history-of-colorectal-cancer")) { + Assert.assertTrue(group.getPopulation().get(0).getCount() > 0); + } + + if (group.getIdentifier().getValue().equals("history-of-total-colectomy")) { + Assert.assertTrue(group.getPopulation().get(0).getCount() > 0); + } + } + } + + @Test + public void PlanDefinitionApplyTest() throws ClassNotFoundException { + putResource("plandefinition-apply-library.json", "plandefinitionApplyTest"); + putResource("plandefinition-apply.json", "apply-example"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("patient").setValue(new StringType("Patient-12214")); + + Parameters outParams = ourClient + .operation() + .onInstance(new IdDt("PlanDefinition", "apply-example")) + .named("$apply") + .withParameters(inParams) + .useHttpGet() + .execute(); + + List response = outParams.getParameter(); + + Assert.assertTrue(!response.isEmpty()); + + Resource resource = response.get(0).getResource(); + + Assert.assertTrue(resource instanceof CarePlan); + + CarePlan carePlan = (CarePlan) resource; + + Assert.assertTrue(carePlan.getTitle().equals("This is a dynamic definition!")); + } + + @Test + public void ActivityDefinitionApplyTest() { + putResource("activitydefinition-apply-library.json", "activityDefinitionApplyTest"); + putResource("activitydefinition-apply.json", "ad-apply-example"); + + Parameters inParams = new Parameters(); + inParams.addParameter().setName("patient").setValue(new StringType("Patient-12214")); + + Parameters outParams = ourClient + .operation() + .onInstance(new IdDt("ActivityDefinition", "ad-apply-example")) + .named("$apply") + .withParameters(inParams) + .useHttpGet() + .execute(); + + List response = outParams.getParameter(); + + Assert.assertTrue(!response.isEmpty()); + + Resource resource = response.get(0).getResource(); + + Assert.assertTrue(resource instanceof ProcedureRequest); + + ProcedureRequest procedureRequest = (ProcedureRequest) resource; + + Assert.assertTrue(procedureRequest.getDoNotPerform()); + } + + //@Test + public void CdsHooksPatientViewTest() throws IOException { + putResource("cds-bcs-library.json", "patient-view"); + putResource("cds-bcs-patient.json", "Patient-6532"); + putResource("cds-bcs-plandefinition.json", "bcs-decision-support"); + putResource("cds-bcs-activitydefinition.json", "mammogram-service-request"); + + // Get the CDS Hooks request + InputStream is = this.getClass().getResourceAsStream("cds-bcs-request.json"); + Scanner scanner = new Scanner(is).useDelimiter("\\A"); + String cdsHooksRequest = scanner.hasNext() ? scanner.next() : ""; + byte[] data = cdsHooksRequest.getBytes("UTF-8"); + + URL url = new URL("http://localhost:" + ourPort + "/hapi-fhir-jpaserver-cds/cds-services/bcs-decision-support"); + + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setRequestProperty("Content-Type", "application/json"); + conn.setRequestProperty("Content-Length", String.valueOf(data.length)); + conn.setDoOutput(true); + conn.getOutputStream().write(data); + + StringBuilder response = new StringBuilder(); + try(Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"))) + { + for (int i; (i = in.read()) >= 0;) { + response.append((char) i); + } + } + + String expected = "{\n" + + " \"cards\": [\n" + + " {\n" + + " \"summary\": \"High risk for opioid overdose - taper now\",\n" + + " \"indicator\": \"warning\",\n" + + " \"detail\": \"Total morphine milligram equivalent (MME) is 20200.700mg/d. Taper to less than 50.\"\n" + + " }\n" + + " ]\n" + + "}"; + + Assert.assertTrue( + response.toString().replaceAll("\\s+", "") + .equals(expected.replaceAll("\\s+", "")) + ); + } +} + +class RandomServerPortProvider { + + private static List ourPorts = new ArrayList<>(); + + static int findFreePort() { + ServerSocket server; + try { + server = new ServerSocket(0); + int port = server.getLocalPort(); + ourPorts.add(port); + server.close(); + Thread.sleep(500); + return port; + } catch (IOException | InterruptedException e) { + throw new Error(e); + } + } + + public static List list() { + return ourPorts; + } + +} diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/activitydefinition-apply-library.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/activitydefinition-apply-library.json new file mode 100644 index 00000000000..c116f8788b6 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/activitydefinition-apply-library.json @@ -0,0 +1,19 @@ +{ + "resourceType": "Library", + "id": "activityDefinitionApplyTest", + "version": "1.0", + "status": "draft", + "type": { + "coding": [ + { + "code": "logic-library" + } + ] + }, + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBhY3Rpdml0eURlZmluaXRpb25BcHBseVRlc3QgdmVyc2lvbiAnMS4wJw0KDQpkZWZpbmUgIkR5bmFtaWMgZG9Ob3RQZXJmb3JtIFNldHRpbmciOg0KICAgIHRydWU=" + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/activitydefinition-apply.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/activitydefinition-apply.json new file mode 100644 index 00000000000..0d5243f6375 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/activitydefinition-apply.json @@ -0,0 +1,32 @@ +{ + "resourceType": "ActivityDefinition", + "id": "ad-apply-example", + "text": { + "status": "generated", + "div": "
ActivityDefinition $apply operation example.
" + }, + "status": "draft", + "description": "This is a test.", + "library": [ + { + "reference": "Library/activityDefinitionApplyTest" + } + ], + "kind": "ProcedureRequest", + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "303653007", + "display": "Computed tomography of head" + } + ] + }, + "dynamicValue": [ + { + "description": "Set ProcedureRequest doNotPerform property", + "path": "doNotPerform", + "expression": "activityDefinitionApplyTest.\"Dynamic doNotPerform Setting\"" + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-activitydefinition.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-activitydefinition.json new file mode 100644 index 00000000000..bc74be1e27f --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-activitydefinition.json @@ -0,0 +1,37 @@ +{ + "resourceType": "ActivityDefinition", + "id": "mammogram-service-request", + "text": { + "status": "generated", + "div": "
Create ServiceRequest for Mammogrm Procedure
" + }, + "status": "draft", + "description": "Create ServiceRequest for Mammogram Procedure", + "kind": "ProcedureRequest", + "code": { + "coding": [ + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "77056", + "display": "Mammography; bilateral" + } + ] + }, + "timingTiming": { + "_event": [ + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/cqif-basic-cqlExpression", + "valueString": "Now()" + } + ] + } + ] + }, + "participant": [ + { + "type": "practitioner" + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-library.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-library.json new file mode 100644 index 00000000000..f2360ca89e1 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-library.json @@ -0,0 +1,166 @@ +{ + "resourceType": "Library", + "id": "patient-view", + "version": "1.0.0", + "status": "active", + "experimental": true, + "type": { + "coding": [ + { + "code": "logic-library" + } + ] + }, + "dataRequirement": [ + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Bilateral Mastectomy Value Set" + } + ] + }, + { + "type": "Condition", + "codeFilter": [ + { + "path": "code", + "valueSetString": "History of Bilateral Mastectomy Value Set" + } + ] + }, + { + "type": "Claim" + }, + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Unilateral Mastectomy Value Set" + } + ] + }, + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Bilateral Modifier Value Set" + } + ] + }, + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Unilateral Mastectomy Value Set" + } + ] + }, + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Unilateral Mastectomy Value Set" + } + ] + }, + { + "type": "Claim" + }, + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Unilateral Mastectomy Value Set" + } + ] + }, + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Left Modifier Value Set" + } + ] + }, + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Unilateral Mastectomy Left Value Set" + } + ] + }, + { + "type": "Observation", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Absence of Left Breast Value Set" + } + ] + }, + { + "type": "Claim" + }, + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Unilateral Mastectomy Value Set" + } + ] + }, + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Right Modifier Value Set" + } + ] + }, + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Unilateral Mastectomy Right Value Set" + } + ] + }, + { + "type": "Observation", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Absence of Right Breast Value Set" + } + ] + }, + { + "type": "Procedure", + "codeFilter": [ + { + "path": "code", + "valueSetString": "Mammography Value Set" + } + ] + } + ], + "content": [ + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPGxpYnJhcnkgeG1sbnM9InVybjpobDctb3JnOmVsbTpyMSIgeG1sbnM6dD0idXJuOmhsNy1vcmc6ZWxtLXR5cGVzOnIxIiB4bWxuczp4c2k9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIiB4bWxuczp4c2Q9Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hIiB4bWxuczpmaGlyPSJodHRwOi8vaGw3Lm9yZy9maGlyIiB4bWxuczphPSJ1cm46aGw3LW9yZzpjcWwtYW5ub3RhdGlvbnM6cjEiPgogICA8YW5ub3RhdGlvbiBzdGFydExpbmU9IjEwNSIgc3RhcnRDaGFyPSIzIiBlbmRMaW5lPSIxMDUiIGVuZENoYXI9IjQ3IiBtZXNzYWdlPSJDb3VsZCBub3QgcmVzb2x2ZSBtZW1iZXJzaGlwIG9wZXJhdG9yIGZvciB0ZXJtaW5vbG9neSB0YXJnZXQgb2YgdGhlIHJldHJpZXZlLiIgZXJyb3JUeXBlPSJzZW1hbnRpYyIgZXJyb3JTZXZlcml0eT0id2FybmluZyIgeHNpOnR5cGU9ImE6Q3FsVG9FbG1FcnJvciIvPgogICA8YW5ub3RhdGlvbiBzdGFydExpbmU9IjExMiIgc3RhcnRDaGFyPSIzIiBlbmRMaW5lPSIxMTIiIGVuZENoYXI9IjU4IiBtZXNzYWdlPSJDb3VsZCBub3QgcmVzb2x2ZSBtZW1iZXJzaGlwIG9wZXJhdG9yIGZvciB0ZXJtaW5vbG9neSB0YXJnZXQgb2YgdGhlIHJldHJpZXZlLiIgZXJyb3JUeXBlPSJzZW1hbnRpYyIgZXJyb3JTZXZlcml0eT0id2FybmluZyIgeHNpOnR5cGU9ImE6Q3FsVG9FbG1FcnJvciIvPgogICA8YW5ub3RhdGlvbiBzdGFydExpbmU9IjEyNCIgc3RhcnRDaGFyPSI3IiBlbmRMaW5lPSIxMjQiIGVuZENoYXI9IjUyIiBtZXNzYWdlPSJDb3VsZCBub3QgcmVzb2x2ZSBtZW1iZXJzaGlwIG9wZXJhdG9yIGZvciB0ZXJtaW5vbG9neSB0YXJnZXQgb2YgdGhlIHJldHJpZXZlLiIgZXJyb3JUeXBlPSJzZW1hbnRpYyIgZXJyb3JTZXZlcml0eT0id2FybmluZyIgeHNpOnR5cGU9ImE6Q3FsVG9FbG1FcnJvciIvPgogICA8YW5ub3RhdGlvbiBzdGFydExpbmU9IjEzMCIgc3RhcnRDaGFyPSI3MCIgZW5kTGluZT0iMTMwIiBlbmRDaGFyPSI3OSIgbWVzc2FnZT0iTGlzdC12YWx1ZWQgZXhwcmVzc2lvbiB3YXMgZGVtb3RlZCB0byBhIHNpbmdsZXRvbi4iIGVycm9yVHlwZT0ic2VtYW50aWMiIGVycm9yU2V2ZXJpdHk9Indhcm5pbmciIHhzaTp0eXBlPSJhOkNxbFRvRWxtRXJyb3IiLz4KICAgPGFubm90YXRpb24gc3RhcnRMaW5lPSIxMzMiIHN0YXJ0Q2hhcj0iNyIgZW5kTGluZT0iMTMzIiBlbmRDaGFyPSI0OSIgbWVzc2FnZT0iQ291bGQgbm90IHJlc29sdmUgbWVtYmVyc2hpcCBvcGVyYXRvciBmb3IgdGVybWlub2xvZ3kgdGFyZ2V0IG9mIHRoZSByZXRyaWV2ZS4iIGVycm9yVHlwZT0ic2VtYW50aWMiIGVycm9yU2V2ZXJpdHk9Indhcm5pbmciIHhzaTp0eXBlPSJhOkNxbFRvRWxtRXJyb3IiLz4KICAgPGFubm90YXRpb24gc3RhcnRMaW5lPSIxMzkiIHN0YXJ0Q2hhcj0iNzAiIGVuZExpbmU9IjEzOSIgZW5kQ2hhcj0iNzkiIG1lc3NhZ2U9Ikxpc3QtdmFsdWVkIGV4cHJlc3Npb24gd2FzIGRlbW90ZWQgdG8gYSBzaW5nbGV0b24uIiBlcnJvclR5cGU9InNlbWFudGljIiBlcnJvclNldmVyaXR5PSJ3YXJuaW5nIiB4c2k6dHlwZT0iYTpDcWxUb0VsbUVycm9yIi8+CiAgIDxhbm5vdGF0aW9uIHN0YXJ0TGluZT0iMTQ3IiBzdGFydENoYXI9IjQiIGVuZExpbmU9IjE0NyIgZW5kQ2hhcj0iNDkiIG1lc3NhZ2U9IkNvdWxkIG5vdCByZXNvbHZlIG1lbWJlcnNoaXAgb3BlcmF0b3IgZm9yIHRlcm1pbm9sb2d5IHRhcmdldCBvZiB0aGUgcmV0cmlldmUuIiBlcnJvclR5cGU9InNlbWFudGljIiBlcnJvclNldmVyaXR5PSJ3YXJuaW5nIiB4c2k6dHlwZT0iYTpDcWxUb0VsbUVycm9yIi8+CiAgIDxhbm5vdGF0aW9uIHN0YXJ0TGluZT0iMTUyIiBzdGFydENoYXI9IjUiIGVuZExpbmU9IjE1MiIgZW5kQ2hhcj0iNTAiIG1lc3NhZ2U9IkNvdWxkIG5vdCByZXNvbHZlIG1lbWJlcnNoaXAgb3BlcmF0b3IgZm9yIHRlcm1pbm9sb2d5IHRhcmdldCBvZiB0aGUgcmV0cmlldmUuIiBlcnJvclR5cGU9InNlbWFudGljIiBlcnJvclNldmVyaXR5PSJ3YXJuaW5nIiB4c2k6dHlwZT0iYTpDcWxUb0VsbUVycm9yIi8+CiAgIDxhbm5vdGF0aW9uIHN0YXJ0TGluZT0iMTY4IiBzdGFydENoYXI9IjciIGVuZExpbmU9IjE2OCIgZW5kQ2hhcj0iNTIiIG1lc3NhZ2U9IkNvdWxkIG5vdCByZXNvbHZlIG1lbWJlcnNoaXAgb3BlcmF0b3IgZm9yIHRlcm1pbm9sb2d5IHRhcmdldCBvZiB0aGUgcmV0cmlldmUuIiBlcnJvclR5cGU9InNlbWFudGljIiBlcnJvclNldmVyaXR5PSJ3YXJuaW5nIiB4c2k6dHlwZT0iYTpDcWxUb0VsbUVycm9yIi8+CiAgIDxhbm5vdGF0aW9uIHN0YXJ0TGluZT0iMTc0IiBzdGFydENoYXI9IjcwIiBlbmRMaW5lPSIxNzQiIGVuZENoYXI9Ijc5IiBtZXNzYWdlPSJMaXN0LXZhbHVlZCBleHByZXNzaW9uIHdhcyBkZW1vdGVkIHRvIGEgc2luZ2xldG9uLiIgZXJyb3JUeXBlPSJzZW1hbnRpYyIgZXJyb3JTZXZlcml0eT0id2FybmluZyIgeHNpOnR5cGU9ImE6Q3FsVG9FbG1FcnJvciIvPgogICA8YW5ub3RhdGlvbiBzdGFydExpbmU9IjE3NyIgc3RhcnRDaGFyPSI3IiBlbmRMaW5lPSIxNzciIGVuZENoYXI9IjQ0IiBtZXNzYWdlPSJDb3VsZCBub3QgcmVzb2x2ZSBtZW1iZXJzaGlwIG9wZXJhdG9yIGZvciB0ZXJtaW5vbG9neSB0YXJnZXQgb2YgdGhlIHJldHJpZXZlLiIgZXJyb3JUeXBlPSJzZW1hbnRpYyIgZXJyb3JTZXZlcml0eT0id2FybmluZyIgeHNpOnR5cGU9ImE6Q3FsVG9FbG1FcnJvciIvPgogICA8YW5ub3RhdGlvbiBzdGFydExpbmU9IjE4MyIgc3RhcnRDaGFyPSI3MCIgZW5kTGluZT0iMTgzIiBlbmRDaGFyPSI3OSIgbWVzc2FnZT0iTGlzdC12YWx1ZWQgZXhwcmVzc2lvbiB3YXMgZGVtb3RlZCB0byBhIHNpbmdsZXRvbi4iIGVycm9yVHlwZT0ic2VtYW50aWMiIGVycm9yU2V2ZXJpdHk9Indhcm5pbmciIHhzaTp0eXBlPSJhOkNxbFRvRWxtRXJyb3IiLz4KICAgPGFubm90YXRpb24gc3RhcnRMaW5lPSIxOTAiIHN0YXJ0Q2hhcj0iMyIgZW5kTGluZT0iMTkwIiBlbmRDaGFyPSI1MyIgbWVzc2FnZT0iQ291bGQgbm90IHJlc29sdmUgbWVtYmVyc2hpcCBvcGVyYXRvciBmb3IgdGVybWlub2xvZ3kgdGFyZ2V0IG9mIHRoZSByZXRyaWV2ZS4iIGVycm9yVHlwZT0ic2VtYW50aWMiIGVycm9yU2V2ZXJpdHk9Indhcm5pbmciIHhzaTp0eXBlPSJhOkNxbFRvRWxtRXJyb3IiLz4KICAgPGFubm90YXRpb24gc3RhcnRMaW5lPSIxOTciIHN0YXJ0Q2hhcj0iMyIgZW5kTGluZT0iMTk3IiBlbmRDaGFyPSI1MSIgbWVzc2FnZT0iQ291bGQgbm90IHJlc29sdmUgbWVtYmVyc2hpcCBvcGVyYXRvciBmb3IgdGVybWlub2xvZ3kgdGFyZ2V0IG9mIHRoZSByZXRyaWV2ZS4iIGVycm9yVHlwZT0ic2VtYW50aWMiIGVycm9yU2V2ZXJpdHk9Indhcm5pbmciIHhzaTp0eXBlPSJhOkNxbFRvRWxtRXJyb3IiLz4KICAgPGFubm90YXRpb24gc3RhcnRMaW5lPSIyMDkiIHN0YXJ0Q2hhcj0iNyIgZW5kTGluZT0iMjA5IiBlbmRDaGFyPSI1MiIgbWVzc2FnZT0iQ291bGQgbm90IHJlc29sdmUgbWVtYmVyc2hpcCBvcGVyYXRvciBmb3IgdGVybWlub2xvZ3kgdGFyZ2V0IG9mIHRoZSByZXRyaWV2ZS4iIGVycm9yVHlwZT0ic2VtYW50aWMiIGVycm9yU2V2ZXJpdHk9Indhcm5pbmciIHhzaTp0eXBlPSJhOkNxbFRvRWxtRXJyb3IiLz4KICAgPGFubm90YXRpb24gc3RhcnRMaW5lPSIyMTUiIHN0YXJ0Q2hhcj0iNzAiIGVuZExpbmU9IjIxNSIgZW5kQ2hhcj0iNzkiIG1lc3NhZ2U9Ikxpc3QtdmFsdWVkIGV4cHJlc3Npb24gd2FzIGRlbW90ZWQgdG8gYSBzaW5nbGV0b24uIiBlcnJvclR5cGU9InNlbWFudGljIiBlcnJvclNldmVyaXR5PSJ3YXJuaW5nIiB4c2k6dHlwZT0iYTpDcWxUb0VsbUVycm9yIi8+CiAgIDxhbm5vdGF0aW9uIHN0YXJ0TGluZT0iMjE4IiBzdGFydENoYXI9IjciIGVuZExpbmU9IjIxOCIgZW5kQ2hhcj0iNDUiIG1lc3NhZ2U9IkNvdWxkIG5vdCByZXNvbHZlIG1lbWJlcnNoaXAgb3BlcmF0b3IgZm9yIHRlcm1pbm9sb2d5IHRhcmdldCBvZiB0aGUgcmV0cmlldmUuIiBlcnJvclR5cGU9InNlbWFudGljIiBlcnJvclNldmVyaXR5PSJ3YXJuaW5nIiB4c2k6dHlwZT0iYTpDcWxUb0VsbUVycm9yIi8+CiAgIDxhbm5vdGF0aW9uIHN0YXJ0TGluZT0iMjI0IiBzdGFydENoYXI9IjcwIiBlbmRMaW5lPSIyMjQiIGVuZENoYXI9Ijc5IiBtZXNzYWdlPSJMaXN0LXZhbHVlZCBleHByZXNzaW9uIHdhcyBkZW1vdGVkIHRvIGEgc2luZ2xldG9uLiIgZXJyb3JUeXBlPSJzZW1hbnRpYyIgZXJyb3JTZXZlcml0eT0id2FybmluZyIgeHNpOnR5cGU9ImE6Q3FsVG9FbG1FcnJvciIvPgogICA8YW5ub3RhdGlvbiBzdGFydExpbmU9IjIzMSIgc3RhcnRDaGFyPSIzIiBlbmRMaW5lPSIyMzEiIGVuZENoYXI9IjU0IiBtZXNzYWdlPSJDb3VsZCBub3QgcmVzb2x2ZSBtZW1iZXJzaGlwIG9wZXJhdG9yIGZvciB0ZXJtaW5vbG9neSB0YXJnZXQgb2YgdGhlIHJldHJpZXZlLiIgZXJyb3JUeXBlPSJzZW1hbnRpYyIgZXJyb3JTZXZlcml0eT0id2FybmluZyIgeHNpOnR5cGU9ImE6Q3FsVG9FbG1FcnJvciIvPgogICA8YW5ub3RhdGlvbiBzdGFydExpbmU9IjIzOCIgc3RhcnRDaGFyPSIzIiBlbmRMaW5lPSIyMzgiIGVuZENoYXI9IjUyIiBtZXNzYWdlPSJDb3VsZCBub3QgcmVzb2x2ZSBtZW1iZXJzaGlwIG9wZXJhdG9yIGZvciB0ZXJtaW5vbG9neSB0YXJnZXQgb2YgdGhlIHJldHJpZXZlLiIgZXJyb3JUeXBlPSJzZW1hbnRpYyIgZXJyb3JTZXZlcml0eT0id2FybmluZyIgeHNpOnR5cGU9ImE6Q3FsVG9FbG1FcnJvciIvPgogICA8YW5ub3RhdGlvbiBzdGFydExpbmU9IjgyIiBzdGFydENoYXI9IjMiIGVuZExpbmU9IjgyIiBlbmRDaGFyPSIzOCIgbWVzc2FnZT0iQ291bGQgbm90IHJlc29sdmUgbWVtYmVyc2hpcCBvcGVyYXRvciBmb3IgdGVybWlub2xvZ3kgdGFyZ2V0IG9mIHRoZSByZXRyaWV2ZS4iIGVycm9yVHlwZT0ic2VtYW50aWMiIGVycm9yU2V2ZXJpdHk9Indhcm5pbmciIHhzaTp0eXBlPSJhOkNxbFRvRWxtRXJyb3IiLz4KICAgPGFubm90YXRpb24gc3RhcnRMaW5lPSI3OCIgc3RhcnRDaGFyPSIxOCIgZW5kTGluZT0iNzgiIGVuZENoYXI9IjQ5IiBtZXNzYWdlPSJCb29sZWFuLXZhbHVlZCBleHByZXNzaW9uIHdhcyBwcm9tb3RlZCB0byBhIGxpc3QuIiBlcnJvclR5cGU9InNlbWFudGljIiBlcnJvclNldmVyaXR5PSJ3YXJuaW5nIiB4c2k6dHlwZT0iYTpDcWxUb0VsbUVycm9yIi8+CiAgIDxpZGVudGlmaWVyIGlkPSJCQ1NfRkhJUiIgdmVyc2lvbj0iMS4wLjAiLz4KICAgPHNjaGVtYUlkZW50aWZpZXIgaWQ9InVybjpobDctb3JnOmVsbSIgdmVyc2lvbj0icjEiLz4KICAgPHVzaW5ncz4KICAgICAgPGRlZiBsb2NhbElkZW50aWZpZXI9IlN5c3RlbSIgdXJpPSJ1cm46aGw3LW9yZzplbG0tdHlwZXM6cjEiLz4KICAgICAgPGRlZiBsb2NhbElkPSIxIiBsb2NhbElkZW50aWZpZXI9IkZISVIiIHVyaT0iaHR0cDovL2hsNy5vcmcvZmhpciIgdmVyc2lvbj0iMy4wLjAiLz4KICAgPC91c2luZ3M+CiAgIDxwYXJhbWV0ZXJzPgogICAgICA8ZGVmIGxvY2FsSWQ9IjE0IiBuYW1lPSJQcm9kdWN0IExpbmUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8cGFyYW1ldGVyVHlwZVNwZWNpZmllciBsb2NhbElkPSIxMyIgbmFtZT0idDpTdHJpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYWxJZD0iMTciIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxwYXJhbWV0ZXJUeXBlU3BlY2lmaWVyIGxvY2FsSWQ9IjE2IiB4c2k6dHlwZT0iSW50ZXJ2YWxUeXBlU3BlY2lmaWVyIj4KICAgICAgICAgICAgPHBvaW50VHlwZSBsb2NhbElkPSIxNSIgbmFtZT0idDpEYXRlVGltZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICA8L3BhcmFtZXRlclR5cGVTcGVjaWZpZXI+CiAgICAgIDwvZGVmPgogICA8L3BhcmFtZXRlcnM+CiAgIDx2YWx1ZVNldHM+CiAgICAgIDxkZWYgbG9jYWxJZD0iMiIgbmFtZT0iQWJzZW5jZSBvZiBMZWZ0IEJyZWFzdCBWYWx1ZSBTZXQiIGlkPSIyLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDA0LjEzMjkuMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2FsSWQ9IjMiIG5hbWU9IkFic2VuY2Ugb2YgUmlnaHQgQnJlYXN0IFZhbHVlIFNldCIgaWQ9IjIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDQuMTMzMC4yIiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYWxJZD0iNCIgbmFtZT0iQmlsYXRlcmFsIE1hc3RlY3RvbXkgVmFsdWUgU2V0IiBpZD0iMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwNC4xMDQyIiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYWxJZD0iNSIgbmFtZT0iQmlsYXRlcmFsIE1vZGlmaWVyIFZhbHVlIFNldCIgaWQ9IjIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDQuMTA0My41IiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+CiAgICAgIDxkZWYgbG9jYWxJZD0iNiIgbmFtZT0iSGlzdG9yeSBvZiBCaWxhdGVyYWwgTWFzdGVjdG9teSBWYWx1ZSBTZXQiIGlkPSIyLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDA0LjEzMzEuNiIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2FsSWQ9IjciIG5hbWU9IkxlZnQgTW9kaWZpZXIgVmFsdWUgU2V0IiBpZD0iMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwNC4xMTQ4LjciIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgICAgPGRlZiBsb2NhbElkPSI4IiBuYW1lPSJNYW1tb2dyYXBoeSBWYWx1ZSBTZXQiIGlkPSIyLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDA0LjExNjgiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgICAgPGRlZiBsb2NhbElkPSI5IiBuYW1lPSJSaWdodCBNb2RpZmllciBWYWx1ZSBTZXQiIGlkPSIyLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDA0LjEyMzAuMTIiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgICAgPGRlZiBsb2NhbElkPSIxMCIgbmFtZT0iVW5pbGF0ZXJhbCBNYXN0ZWN0b215IFZhbHVlIFNldCIgaWQ9IjIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDQuMTI1NiIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICAgICA8ZGVmIGxvY2FsSWQ9IjExIiBuYW1lPSJVbmlsYXRlcmFsIE1hc3RlY3RvbXkgTGVmdCBWYWx1ZSBTZXQiIGlkPSIyLjE2Ljg0MC4xLjExMzg4My4zLjQ2NC4xMDA0LjEzMzQuMTUiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4KICAgICAgPGRlZiBsb2NhbElkPSIxMiIgbmFtZT0iVW5pbGF0ZXJhbCBNYXN0ZWN0b215IFJpZ2h0IFZhbHVlIFNldCIgaWQ9IjIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDQuMTMzNS4xNiIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPgogICA8L3ZhbHVlU2V0cz4KICAgPHN0YXRlbWVudHM+CiAgICAgIDxkZWYgbG9jYWxJZD0iMjUiIG5hbWU9IkZpcnN0IFByZWRlY2Vzc29yIFllYXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6cyByPSIyNSI+CiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0ZpcnN0IFByZWRlY2Vzc29yIFllYXImcXVvdDs6Cgk8L2E6cz4KICAgICAgICAgICAgICAgPGE6cyByPSIyNCI+CiAgICAgICAgICAgICAgICAgIDxhOnM+SW50ZXJ2YWxbPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjAiPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Ub2RheSgpPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPiAtIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4yIHllYXJzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cz4sIDwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjIzIj4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+VG9kYXkoKTwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4gLSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+MSB5ZWFyczwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMjQiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgIDxsb3cgbG9jYWxJZD0iMjAiIHhzaTp0eXBlPSJTdWJ0cmFjdCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4IiB4c2k6dHlwZT0iVG9kYXkiLz4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTkiIHZhbHVlPSIyIiB1bml0PSJ5ZWFycyIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+CiAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICA8aGlnaCBsb2NhbElkPSIyMyIgeHNpOnR5cGU9IlN1YnRyYWN0Ij4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjEiIHhzaTp0eXBlPSJUb2RheSIvPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMiIgdmFsdWU9IjEiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2FsSWQ9IjMzIiBuYW1lPSJTZWNvbmQgUHJlZGVjZXNzb3IgWWVhciIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTpzIHI9IjMzIj4KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7U2Vjb25kIFByZWRlY2Vzc29yIFllYXImcXVvdDs6Cgk8L2E6cz4KICAgICAgICAgICAgICAgPGE6cyByPSIzMiI+CiAgICAgICAgICAgICAgICAgIDxhOnM+SW50ZXJ2YWxbPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjgiPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Ub2RheSgpPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPiAtIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4zIHllYXJzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cz4sIDwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjMxIj4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyOSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+VG9kYXkoKTwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4gLSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+MiB5ZWFyczwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMzIiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgIDxsb3cgbG9jYWxJZD0iMjgiIHhzaTp0eXBlPSJTdWJ0cmFjdCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI2IiB4c2k6dHlwZT0iVG9kYXkiLz4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjciIHZhbHVlPSIzIiB1bml0PSJ5ZWFycyIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+CiAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICA8aGlnaCBsb2NhbElkPSIzMSIgeHNpOnR5cGU9IlN1YnRyYWN0Ij4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjkiIHhzaTp0eXBlPSJUb2RheSIvPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzMCIgdmFsdWU9IjIiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2FsSWQ9IjQzIiBuYW1lPSJUaGlyZCBQcmVkZWNlc3NvciBRdWFydGVyIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnMgcj0iNDMiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtUaGlyZCBQcmVkZWNlc3NvciBRdWFydGVyJnF1b3Q7OgoJPC9hOnM+CiAgICAgICAgICAgICAgIDxhOnMgcj0iNDIiPgogICAgICAgICAgICAgICAgICA8YTpzPkludGVydmFsWzwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjM4Ij4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlRvZGF5KCk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IC0gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPjMgeWVhcnM8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPiAtIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4zIG1vbnRoczwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnM+LCA8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MSI+CiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzkiPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlRvZGF5KCk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+IC0gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDAiPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPjMgeWVhcnM8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4KICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgIDwvYTpzPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjQyIiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9ImZhbHNlIiB4c2k6dHlwZT0iSW50ZXJ2YWwiPgogICAgICAgICAgICA8bG93IGxvY2FsSWQ9IjM4IiB4c2k6dHlwZT0iU3VidHJhY3QiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzNiIgeHNpOnR5cGU9IlN1YnRyYWN0Ij4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzQiIHhzaTp0eXBlPSJUb2RheSIvPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzNSIgdmFsdWU9IjMiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzNyIgdmFsdWU9IjMiIHVuaXQ9Im1vbnRocyIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+CiAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICA8aGlnaCBsb2NhbElkPSI0MSIgeHNpOnR5cGU9IlN1YnRyYWN0Ij4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzkiIHhzaTp0eXBlPSJUb2RheSIvPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0MCIgdmFsdWU9IjMiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgPC9oaWdoPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2FsSWQ9IjQ5IiBuYW1lPSJMb29rYmFjayBJbnRlcnZhbCAyNyBNb3JlIE1vbnRocyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTpzIHI9IjQ5Ij4KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7TG9va2JhY2sgSW50ZXJ2YWwgMjcgTW9yZSBNb250aHMmcXVvdDs6Cgk8L2E6cz4KICAgICAgICAgICAgICAgPGE6cyByPSI0OCI+CiAgICAgICAgICAgICAgICAgIDxhOnM+SW50ZXJ2YWxbPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDYiPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Ub2RheSgpPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPiAtIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4yNyBtb250aHM8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDciPgogICAgICAgICAgICAgICAgICAgICA8YTpzPlRvZGF5KCk8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iNDgiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0iZmFsc2UiIHhzaTp0eXBlPSJJbnRlcnZhbCI+CiAgICAgICAgICAgIDxsb3cgbG9jYWxJZD0iNDYiIHhzaTp0eXBlPSJTdWJ0cmFjdCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQ0IiB4c2k6dHlwZT0iVG9kYXkiLz4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDUiIHZhbHVlPSIyNyIgdW5pdD0ibW9udGhzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4KICAgICAgICAgICAgPC9sb3c+CiAgICAgICAgICAgIDxoaWdoIGxvY2FsSWQ9IjQ3IiB4c2k6dHlwZT0iVG9kYXkiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBuYW1lPSJQYXRpZW50IiBjb250ZXh0PSJQYXRpZW50Ij4KICAgICAgICAgPGV4cHJlc3Npb24geHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPgogICAgICAgICAgICA8b3BlcmFuZCBkYXRhVHlwZT0iZmhpcjpQYXRpZW50IiB4c2k6dHlwZT0iUmV0cmlldmUiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhbElkPSI1NSIgbmFtZT0iSXMgRmVtYWxlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnMgcj0iNTUiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtJcyBGZW1hbGUmcXVvdDs6Cgk8L2E6cz4KICAgICAgICAgICAgICAgPGE6cyByPSI1NCI+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTIiPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUxIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UGF0aWVudDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmdlbmRlcjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUyIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz52YWx1ZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnM+ID0gPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTMiPgogICAgICAgICAgICAgICAgICAgICA8YTpzPidmZW1hbGUnPC9hOnM+CiAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iNTQiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjUyIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI1MSIgcGF0aD0iZ2VuZGVyIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjUwIiBuYW1lPSJQYXRpZW50IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI1MyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImZlbWFsZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhbElkPSI2MyIgbmFtZT0iSXMgQWdlIDUyIHRvIDc0IGF0IEVuZCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTpzIHI9IjYzIj4KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7SXMgQWdlIDUyIHRvIDc0IGF0IEVuZCZxdW90OzoKCTwvYTpzPgogICAgICAgICAgICAgICA8YTpzIHI9IjYyIj4KICAgICAgICAgICAgICAgICAgPGE6cyByPSI1OSI+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+QWdlSW5ZZWFyc0F0KDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjU4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTciPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cz4gYmV0d2VlbiA1MiBhbmQgNzQ8L2E6cz4KICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgIDwvYTpzPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjYyIiB4c2k6dHlwZT0iQW5kIj4KICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkdyZWF0ZXJPckVxdWFsIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNTkiIHByZWNpc2lvbj0iWWVhciIgeHNpOnR5cGU9IkNhbGN1bGF0ZUFnZUF0Ij4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0iYmlydGhEYXRlLnZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIG5hbWU9IlBhdGllbnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNTgiIHhzaTp0eXBlPSJFbmQiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI1NyIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2MCIgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSI1MiIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iTGVzc09yRXF1YWwiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI1OSIgcHJlY2lzaW9uPSJZZWFyIiB4c2k6dHlwZT0iQ2FsY3VsYXRlQWdlQXQiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBwYXRoPSJiaXJ0aERhdGUudmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI1OCIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU3IiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjYxIiB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9Ijc0IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYWxJZD0iODMiIG5hbWU9IlBlcmlvZFRvSW50ZXJ2YWxPZkRUIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIiB4c2k6dHlwZT0iRnVuY3Rpb25EZWYiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6cyByPSI4MyI+CiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lIGZ1bmN0aW9uIFBlcmlvZFRvSW50ZXJ2YWxPZkRUKHZhbHVlIDwvYTpzPgogICAgICAgICAgICAgICA8YTpzIHI9Ijc1Ij4KICAgICAgICAgICAgICAgICAgPGE6cz5GSElSLlBlcmlvZDwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgPGE6cz4pOgoJPC9hOnM+CiAgICAgICAgICAgICAgIDxhOnMgcj0iODIiPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjgyIj4KICAgICAgICAgICAgICAgICAgICAgPGE6cz5JbnRlcnZhbFs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3OCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzciPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz52YWx1ZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O3N0YXJ0JnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4sIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjgxIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7ZW5kJnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+CiAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iODIiIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4KICAgICAgICAgICAgPGxvdyBsb2NhbElkPSI3OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iNzciIHBhdGg9InN0YXJ0IiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9Ijc2IiBuYW1lPSJ2YWx1ZSIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgIDwvbG93PgogICAgICAgICAgICA8aGlnaCBsb2NhbElkPSI4MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iODAiIHBhdGg9ImVuZCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI3OSIgbmFtZT0idmFsdWUiIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICA8L2hpZ2g+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0idmFsdWUiPgogICAgICAgICAgICA8b3BlcmFuZFR5cGVTcGVjaWZpZXIgbG9jYWxJZD0iNzUiIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYWxJZD0iOTIiIG5hbWU9IklzIEJpbGF0ZXJhbCBNYXN0ZWN0b215IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnMgcj0iOTIiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtJcyBCaWxhdGVyYWwgTWFzdGVjdG9teSZxdW90OzoKCTwvYTpzPgogICAgICAgICAgICAgICA8YTpzIHI9IjkxIj4KICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHM8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cyByPSI5MCI+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+KAoJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjkwIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI2NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNjYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjY2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtCaWxhdGVyYWwgTWFzdGVjdG9teSBWYWx1ZSBTZXQmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IFByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4OSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjcyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNjkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjY4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Qcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI2OSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3MSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2NvbXBsZXRlZCc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJYW5kIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4NSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kIG9mIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijg0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5QZXJpb2RUb0ludGVydmFsT2ZEVCg8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI3NCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNzMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijc0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBzYW1lIGRheSBvciBiZWZvcmUgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iODciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI4NiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkpPC9hOnM+CiAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iOTEiIHhzaTp0eXBlPSJFeGlzdHMiPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI5MCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI2NyIgYWxpYXM9IlByb2MiPgogICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI2NiIgZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+CiAgICAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJCaWxhdGVyYWwgTWFzdGVjdG9teSBWYWx1ZSBTZXQiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPgogICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iODkiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI3MiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNzAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjY5IiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJQcm9jIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI3MSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4OCIgcHJlY2lzaW9uPSJEYXkiIHhzaTp0eXBlPSJTYW1lT3JCZWZvcmUiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI4NSIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9Ijg0IiBuYW1lPSJQZXJpb2RUb0ludGVydmFsT2ZEVCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgYXNUeXBlPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNzQiIHBhdGg9InBlcmZvcm1lZCIgc2NvcGU9IlByb2MiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9Ijg3IiB4c2k6dHlwZT0iRW5kIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iODYiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2FsSWQ9IjExMCIgbmFtZT0iSXMgSGlzdG9yeSBPZiBCaWxhdGVyYWwgTWFzdGVjdG9teSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTpzIHI9IjExMCI+CiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0lzIEhpc3RvcnkgT2YgQmlsYXRlcmFsIE1hc3RlY3RvbXkmcXVvdDs6Cgk8L2E6cz4KICAgICAgICAgICAgICAgPGE6cyByPSIxMDkiPgogICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0czwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwOCI+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+KAoJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwOCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijk0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+W0NvbmRpdGlvbjogPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SGlzdG9yeSBvZiBCaWxhdGVyYWwgTWFzdGVjdG9teSBWYWx1ZSBTZXQmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IENvbmQ8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMDciPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTAwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5OCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9Ijk2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Db25kPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmVyaWZpY2F0aW9uU3RhdHVzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iOTgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI5OSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2NvbmZpcm1lZCc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJYW5kIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTAzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMDIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Q29uZDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTAyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5hc3NlcnRlZERhdGU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMDMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gc2FtZSBkYXkgb3IgYmVmb3JlIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kIG9mIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEwNCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkpPC9hOnM+CiAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTA5IiB4c2k6dHlwZT0iRXhpc3RzIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTA4IiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9Ijk1IiBhbGlhcz0iQ29uZCI+CiAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9Ijk0IiBkYXRhVHlwZT0iZmhpcjpDb25kaXRpb24iIGNvZGVQcm9wZXJ0eT0iY29kZSIgeHNpOnR5cGU9IlJldHJpZXZlIj4KICAgICAgICAgICAgICAgICAgICAgPGNvZGVzIG5hbWU9Ikhpc3Rvcnkgb2YgQmlsYXRlcmFsIE1hc3RlY3RvbXkgVmFsdWUgU2V0IiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjEwNyIgeHNpOnR5cGU9IkFuZCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEwMCIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9Ijk3IiBwYXRoPSJ2ZXJpZmljYXRpb25TdGF0dXMiIHNjb3BlPSJDb25kIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI5OSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbmZpcm1lZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMDYiIHByZWNpc2lvbj0iRGF5IiB4c2k6dHlwZT0iU2FtZU9yQmVmb3JlIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTAzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxMDIiIHBhdGg9ImFzc2VydGVkRGF0ZSIgc2NvcGU9IkNvbmQiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEwNSIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEwNCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYWxJZD0iMTkxIiBuYW1lPSJJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgV2l0aCBCaWxhdGVyYWwgTW9kaWZpZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6cyByPSIxOTEiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgV2l0aCBCaWxhdGVyYWwgTW9kaWZpZXImcXVvdDs6Cgk8L2E6cz4KICAgICAgICAgICAgICAgPGE6cyByPSIxOTAiPgogICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0czwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4OSI+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+KAoJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4OSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExMyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+W0NsYWltXTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IENsYWltPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTg4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz53aGVyZSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxODgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExOSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExNSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Q2xhaW08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjExNiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz52YWx1ZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ID0gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTE4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nYWN0aXZlJzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQlhbmQgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTg3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHM8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxODYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPigKCQkJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4NiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTIxIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNsYWltLnByb2NlZHVyZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IEJiRWxlbTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTUzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz53aXRoIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzOCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTM3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4oCgkJCQkJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzNyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTIzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyMiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+W1Byb2NlZHVyZTogPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7VW5pbGF0ZXJhbCBNYXN0ZWN0b215IFZhbHVlIFNldCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gUHJvYzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzNiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyNSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTI0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Qcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXR1czwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyNiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEyNyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2NvbXBsZXRlZCc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCQkJCWFuZCA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMzUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzMiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kIG9mIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UGVyaW9kVG9JbnRlcnZhbE9mRFQoPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTMwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMjkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzMCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cGVyZm9ybWVkPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gc2FtZSBkYXkgb3IgYmVmb3JlIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzNCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kIG9mIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjEzMyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJKTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gUHJvYzE8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJCXN1Y2ggdGhhdAoJCQkJCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTUyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4oPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTUyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5pZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTM5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5CYkVsZW08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cHJvY2VkdXJlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaXMgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTQxIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GSElSLlJlZmVyZW5jZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJCQkJdGhlbiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTQ2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4oPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTQ2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0MyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QmJFbGVtPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnByb2NlZHVyZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IGFzIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0NSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+RkhJUi5SZWZlcmVuY2U8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE0NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+aWRlbnRpZmllcjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IH4gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTQ5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlByb2MxPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNDkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmlkZW50aWZpZXI8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkJCQllbHNlIGZhbHNlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4NSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2l0aCA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE2OSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KAoJCQkJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTU0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltQcm9jZWR1cmU6IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0JpbGF0ZXJhbCBNb2RpZmllciBWYWx1ZSBTZXQmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IFByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz53aGVyZSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE2MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTU4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE1NiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UHJvYzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTU3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNTkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidjb21wbGV0ZWQnPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkJCQlhbmQgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTY3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlBlcmlvZFRvSW50ZXJ2YWxPZkRUKDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE2MiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTYxIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Qcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnBlcmZvcm1lZDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IHNhbWUgZGF5IG9yIGJlZm9yZSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNjUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCSk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IFByb2MyPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCQlzdWNoIHRoYXQKCQkJCQkJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4NCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4NCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+aWYgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3MSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QmJFbGVtPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnByb2NlZHVyZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IGlzIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3MyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+RkhJUi5SZWZlcmVuY2U8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCQkJCXRoZW4gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3OCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE3OCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkJiRWxlbTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTc2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wcm9jZWR1cmU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBhcyA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZISVIuUmVmZXJlbmNlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxNzkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmlkZW50aWZpZXI8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiB+IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE4MSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Qcm9jMjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTgxIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5pZGVudGlmaWVyPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJCQkJZWxzZSBmYWxzZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCSk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPgoJKTwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgIDwvYTpzPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjE5MCIgeHNpOnR5cGU9IkV4aXN0cyI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE4OSIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxMTQiIGFsaWFzPSJDbGFpbSI+CiAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjExMyIgZGF0YVR5cGU9ImZoaXI6Q2xhaW0iIHhzaTp0eXBlPSJSZXRyaWV2ZSIvPgogICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjE4OCIgeHNpOnR5cGU9IkFuZCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExOSIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTE3IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxMTYiIHBhdGg9InN0YXR1cyIgc2NvcGU9IkNsYWltIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMTgiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJhY3RpdmUiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTg3IiB4c2k6dHlwZT0iRXhpc3RzIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTg2IiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjEyMSIgYWxpYXM9IkJiRWxlbSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjEyMCIgcGF0aD0icHJvY2VkdXJlIiBzY29wZT0iQ2xhaW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgPHJlbGF0aW9uc2hpcCBsb2NhbElkPSIxNTMiIGFsaWFzPSJQcm9jMSIgeHNpOnR5cGU9IldpdGgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxMzciIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTIzIiBhbGlhcz0iUHJvYyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjEyMiIgZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJVbmlsYXRlcmFsIE1hc3RlY3RvbXkgVmFsdWUgU2V0IiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjEzNiIgeHNpOnR5cGU9IkFuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEyOCIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTI2IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIxMjUiIHBhdGg9InN0YXR1cyIgc2NvcGU9IlByb2MiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjEyNyIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzUiIHByZWNpc2lvbj0iRGF5IiB4c2k6dHlwZT0iU2FtZU9yQmVmb3JlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTMyIiB4c2k6dHlwZT0iRW5kIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTMxIiBuYW1lPSJQZXJpb2RUb0ludGVydmFsT2ZEVCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgYXNUeXBlPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTMwIiBwYXRoPSJwZXJmb3JtZWQiIHNjb3BlPSJQcm9jIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzQiIHhzaTp0eXBlPSJFbmQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMzMiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8c3VjaFRoYXQgbG9jYWxJZD0iMTUyIiB4c2k6dHlwZT0iSWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y29uZGl0aW9uIGFzVHlwZT0idDpCb29sZWFuIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNDIiIHhzaTp0eXBlPSJJcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE0MCIgcGF0aD0icHJvY2VkdXJlIiBzY29wZT0iQmJFbGVtIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGlzVHlwZVNwZWNpZmllciBsb2NhbElkPSIxNDEiIG5hbWU9ImZoaXI6UmVmZXJlbmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBuYW1lPSJ0OkJvb2xlYW4iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx0aGVuIGxvY2FsSWQ9IjE1MCIgeHNpOnR5cGU9IkVxdWl2YWxlbnQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNDciIHBhdGg9ImlkZW50aWZpZXIiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTQ2IiBzdHJpY3Q9ImZhbHNlIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNDQiIHBhdGg9InByb2NlZHVyZSIgc2NvcGU9IkJiRWxlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbG9jYWxJZD0iMTQ1IiBuYW1lPSJmaGlyOlJlZmVyZW5jZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE0OSIgcGF0aD0iaWRlbnRpZmllciIgc2NvcGU9IlByb2MxIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3RoZW4+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxlbHNlIGxvY2FsSWQ9IjE1MSIgdmFsdWVUeXBlPSJ0OkJvb2xlYW4iIHZhbHVlPSJmYWxzZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zdWNoVGhhdD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9yZWxhdGlvbnNoaXA+CiAgICAgICAgICAgICAgICAgICAgICAgIDxyZWxhdGlvbnNoaXAgbG9jYWxJZD0iMTg1IiBhbGlhcz0iUHJvYzIiIHhzaTp0eXBlPSJXaXRoIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMTY5IiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjE1NSIgYWxpYXM9IlByb2MiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxNTQiIGRhdGFUeXBlPSJmaGlyOlByb2NlZHVyZSIgY29kZVByb3BlcnR5PSJjb2RlIiB4c2k6dHlwZT0iUmV0cmlldmUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y29kZXMgbmFtZT0iQmlsYXRlcmFsIE1vZGlmaWVyIFZhbHVlIFNldCIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhbElkPSIxNjgiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNjAiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE1OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTU3IiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJQcm9jIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNTkiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTY3IiBwcmVjaXNpb249IkRheSIgeHNpOnR5cGU9IlNhbWVPckJlZm9yZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2NCIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MyIgbmFtZT0iUGVyaW9kVG9JbnRlcnZhbE9mRFQiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE2MiIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iUHJvYyIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTY2IiB4c2k6dHlwZT0iRW5kIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTY1IiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvd2hlcmU+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHN1Y2hUaGF0IGxvY2FsSWQ9IjE4NCIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNvbmRpdGlvbiBhc1R5cGU9InQ6Qm9vbGVhbiIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc0IiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxNzIiIHBhdGg9InByb2NlZHVyZSIgc2NvcGU9IkJiRWxlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYWxJZD0iMTczIiBuYW1lPSJmaGlyOlJlZmVyZW5jZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbmFtZT0idDpCb29sZWFuIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGhlbiBsb2NhbElkPSIxODIiIHhzaTp0eXBlPSJFcXVpdmFsZW50Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc5IiBwYXRoPSJpZGVudGlmaWVyIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjE3OCIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTc2IiBwYXRoPSJwcm9jZWR1cmUiIHNjb3BlPSJCYkVsZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2FsSWQ9IjE3NyIgbmFtZT0iZmhpcjpSZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxODEiIHBhdGg9ImlkZW50aWZpZXIiIHNjb3BlPSJQcm9jMiIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZWxzZSBsb2NhbElkPSIxODMiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0iZmFsc2UiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc3VjaFRoYXQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvcmVsYXRpb25zaGlwPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2FsSWQ9IjI0MiIgbmFtZT0iSXMgVW5pbGF0ZXJhbCBNYXN0ZWN0b215IFR3aWNlIFNwcmVhZCBUd28gV2Vla3MiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6cyByPSIyNDIiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgVHdpY2UgU3ByZWFkIFR3byBXZWVrcyZxdW90OzoKCTwvYTpzPgogICAgICAgICAgICAgICA8YTpzIHI9IjI0MSI+CiAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjQwIj4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4oCgkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjQwIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIwNSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KAoJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5NSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTk0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltQcm9jZWR1cmU6IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1VuaWxhdGVyYWwgTWFzdGVjdG9teSBWYWx1ZSBTZXQmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IFByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjAwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz53aGVyZSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5OCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTk3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxOTYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTk4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz52YWx1ZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ID0gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTk5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29tcGxldGVkJzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5yZXR1cm4gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjAzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5QZXJpb2RUb0ludGVydmFsT2ZEVCg8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIwMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UHJvYzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjAyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkpPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBXaGVuVU0xPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjM5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz53aXRoIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxOSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4oCgkJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMTgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIwOCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMDciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltQcm9jZWR1cmU6IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1VuaWxhdGVyYWwgTWFzdGVjdG9teSBWYWx1ZSBTZXQmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IFByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxMyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjEzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMTEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxMCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjA5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Qcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMTAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXR1czwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxMiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2NvbXBsZXRlZCc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMTciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnJldHVybiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMTYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlBlcmlvZFRvSW50ZXJ2YWxPZkRUKDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIxNSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjE0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Qcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMTUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnBlcmZvcm1lZDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkpPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBXaGVuVU0yPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJc3VjaCB0aGF0IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIzOCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIzOCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjMyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPig8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyNCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyNCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZGlmZmVyZW5jZSBpbiBkYXlzIGJldHdlZW4gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjIxIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGFydCBvZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPldoZW5VTTE8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBhbmQgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjIzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGFydCBvZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPldoZW5VTTI8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA+PSAxNDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCWFuZCA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMzEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyOCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kIG9mIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjIyNyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+V2hlblVNMTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IHNhbWUgZGF5IG9yIGJlZm9yZSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMzAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMjkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQlhbmQgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjM3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMzQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyMzMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPldoZW5VTTI8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBzYW1lIGRheSBvciBiZWZvcmUgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjM2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjM1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPgoJKTwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgIDwvYTpzPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjI0MSIgeHNpOnR5cGU9IkV4aXN0cyI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI0MCIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIyMDYiIGFsaWFzPSJXaGVuVU0xIj4KICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMjA1IiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjE5NSIgYWxpYXM9IlByb2MiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIxOTQiIGRhdGFUeXBlPSJmaGlyOlByb2NlZHVyZSIgY29kZVByb3BlcnR5PSJjb2RlIiB4c2k6dHlwZT0iUmV0cmlldmUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8Y29kZXMgbmFtZT0iVW5pbGF0ZXJhbCBNYXN0ZWN0b215IFZhbHVlIFNldCIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhbElkPSIyMDAiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjE5OCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMTk3IiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJQcm9jIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTkiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvd2hlcmU+CiAgICAgICAgICAgICAgICAgICAgIDxyZXR1cm4gbG9jYWxJZD0iMjA0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMjAzIiBuYW1lPSJQZXJpb2RUb0ludGVydmFsT2ZEVCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgYXNUeXBlPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjAyIiBwYXRoPSJwZXJmb3JtZWQiIHNjb3BlPSJQcm9jIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgPC9yZXR1cm4+CiAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDxyZWxhdGlvbnNoaXAgbG9jYWxJZD0iMjM5IiBhbGlhcz0iV2hlblVNMiIgeHNpOnR5cGU9IldpdGgiPgogICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIyMTgiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMjA4IiBhbGlhcz0iUHJvYyI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjIwNyIgZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJVbmlsYXRlcmFsIE1hc3RlY3RvbXkgVmFsdWUgU2V0IiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjIxMyIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjExIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIyMTAiIHBhdGg9InN0YXR1cyIgc2NvcGU9IlByb2MiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIxMiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgICAgICAgICAgPHJldHVybiBsb2NhbElkPSIyMTciPgogICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIyMTYiIG5hbWU9IlBlcmlvZFRvSW50ZXJ2YWxPZkRUIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBhc1R5cGU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMTUiIHBhdGg9InBlcmZvcm1lZCIgc2NvcGU9IlByb2MiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICAgICA8L3JldHVybj4KICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICAgICA8c3VjaFRoYXQgbG9jYWxJZD0iMjM4IiB4c2k6dHlwZT0iQW5kIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjMyIiB4c2k6dHlwZT0iQW5kIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjI2IiB4c2k6dHlwZT0iR3JlYXRlck9yRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMjQiIHByZWNpc2lvbj0iRGF5IiB4c2k6dHlwZT0iRGlmZmVyZW5jZUJldHdlZW4iPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMjEiIHhzaTp0eXBlPSJTdGFydCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIyMCIgbmFtZT0iV2hlblVNMSIgeHNpOnR5cGU9IkFsaWFzUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjIzIiB4c2k6dHlwZT0iU3RhcnQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMjIiIG5hbWU9IldoZW5VTTIiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjI1IiB2YWx1ZVR5cGU9InQ6SW50ZWdlciIgdmFsdWU9IjE0IiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIzMSIgcHJlY2lzaW9uPSJEYXkiIHhzaTp0eXBlPSJTYW1lT3JCZWZvcmUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMjgiIHhzaTp0eXBlPSJFbmQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMjciIG5hbWU9IldoZW5VTTEiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIzMCIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIyOSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIzNyIgcHJlY2lzaW9uPSJEYXkiIHhzaTp0eXBlPSJTYW1lT3JCZWZvcmUiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMzQiIHhzaTp0eXBlPSJFbmQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyMzMiIG5hbWU9IldoZW5VTTIiIHhzaTp0eXBlPSJBbGlhc1JlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIzNiIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjIzNSIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L3N1Y2hUaGF0PgogICAgICAgICAgICAgICA8L3JlbGF0aW9uc2hpcD4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2FsSWQ9IjI0NSIgbmFtZT0iSXMgTGFja2luZyBCb3RoIEJyZWFzdHMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6cyByPSIyNDUiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtJcyBMYWNraW5nIEJvdGggQnJlYXN0cyZxdW90OzoKCTwvYTpzPgogICAgICAgICAgICAgICA8YTpzIHI9IjI0NCI+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMTkzIj4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTIiPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjkzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtJcyBCaWxhdGVyYWwgTWFzdGVjdG9teSZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQlvciA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIxMTEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0lzIEhpc3RvcnkgT2YgQmlsYXRlcmFsIE1hc3RlY3RvbXkmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCW9yIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjE5MiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SXMgVW5pbGF0ZXJhbCBNYXN0ZWN0b215IFdpdGggQmlsYXRlcmFsIE1vZGlmaWVyJnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cz4KCQlvciA8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNDMiPgogICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0lzIFVuaWxhdGVyYWwgTWFzdGVjdG9teSBUd2ljZSBTcHJlYWQgVHdvIFdlZWtzJnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMjQ0IiB4c2k6dHlwZT0iT3IiPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxOTMiIHhzaTp0eXBlPSJPciI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjExMiIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iOTMiIG5hbWU9IklzIEJpbGF0ZXJhbCBNYXN0ZWN0b215IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIxMTEiIG5hbWU9IklzIEhpc3RvcnkgT2YgQmlsYXRlcmFsIE1hc3RlY3RvbXkiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMTkyIiBuYW1lPSJJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgV2l0aCBCaWxhdGVyYWwgTW9kaWZpZXIiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjQzIiBuYW1lPSJJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgVHdpY2UgU3ByZWFkIFR3byBXZWVrcyIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhbElkPSIzMjUiIG5hbWU9IklzIFVuaWxhdGVyYWwgTWFzdGVjdG9teSBXaXRoIExlZnQgTW9kaWZpZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6cyByPSIzMjUiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgV2l0aCBMZWZ0IE1vZGlmaWVyJnF1b3Q7OgoJPC9hOnM+CiAgICAgICAgICAgICAgIDxhOnMgcj0iMzI0Ij4KICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHM8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMjMiPgogICAgICAgICAgICAgICAgICAgICA8YTpzPigKCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMjMiPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI0OCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjQ3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNDciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltDbGFpbV08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBDbGFpbTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMyMiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzIyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNTMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI1MSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjUwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNDkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkNsYWltPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNTAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXR1czwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI1MSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI1MiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2FjdGl2ZSc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJYW5kIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMyMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzIwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4oCgkJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMjAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI1NSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjU0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5DbGFpbS5wcm9jZWR1cmU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBCYkVsZW08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI4NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2l0aCA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNzIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI3MSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KAoJCQkJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNzEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI1NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjU2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNTYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltQcm9jZWR1cmU6IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1VuaWxhdGVyYWwgTWFzdGVjdG9teSBWYWx1ZSBTZXQmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IFByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjcwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz53aGVyZSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNzAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI2MiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjYwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNTkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI1OCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UHJvYzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjU5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNjAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNjEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidjb21wbGV0ZWQnPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkJCQlhbmQgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjY5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNjYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNjUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlBlcmlvZFRvSW50ZXJ2YWxPZkRUKDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI2NCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjYzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Qcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNjQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnBlcmZvcm1lZDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IHNhbWUgZGF5IG9yIGJlZm9yZSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNjgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNjciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCSk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IFByb2MxPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCQlzdWNoIHRoYXQKCQkJCQkJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI4NiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI4NiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+aWYgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjc2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNzQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI3MyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QmJFbGVtPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNzQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnByb2NlZHVyZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IGlzIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI3NSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+RkhJUi5SZWZlcmVuY2U8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCQkJCXRoZW4gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjg0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyODEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI4MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI4MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjc4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNzciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkJiRWxlbTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjc4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wcm9jZWR1cmU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBhcyA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyNzkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZISVIuUmVmZXJlbmNlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyODEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmlkZW50aWZpZXI8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiB+IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI4MyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjgyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Qcm9jMTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjgzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5pZGVudGlmaWVyPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJCQkJZWxzZSBmYWxzZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMTkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPndpdGggPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzA0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMDMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPigKCQkJCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzAzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyODkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI4OCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjg4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtMZWZ0IE1vZGlmaWVyIFZhbHVlIFNldCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gUHJvYzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMDIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMwMiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjk0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyOTIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI5MSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjkwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Qcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyOTEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXR1czwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI5MiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI5MyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2NvbXBsZXRlZCc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCQkJCWFuZCA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMDEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI5OCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kIG9mIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI5NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UGVyaW9kVG9JbnRlcnZhbE9mRFQoPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjk2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIyOTUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI5NiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cGVyZm9ybWVkPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gc2FtZSBkYXkgb3IgYmVmb3JlIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMwMCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZW5kIG9mIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjI5OSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWVhc3VyZW1lbnQgUGVyaW9kJnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJKTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gUHJvYzI8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJCXN1Y2ggdGhhdAoJCQkJCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzE4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4oPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzE4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5pZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMDgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMwNiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzA1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5CYkVsZW08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMwNiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cHJvY2VkdXJlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaXMgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzA3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GSElSLlJlZmVyZW5jZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJCQkJdGhlbiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMTYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMxMyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzEyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4oPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzEyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMTAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMwOSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+QmJFbGVtPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMTAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnByb2NlZHVyZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IGFzIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMxMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+RkhJUi5SZWZlcmVuY2U8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMxMyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+aWRlbnRpZmllcjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IH4gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzE1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMTQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlByb2MyPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMTUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmlkZW50aWZpZXI8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkJCQllbHNlIGZhbHNlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJKTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkpPC9hOnM+CiAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMzI0IiB4c2k6dHlwZT0iRXhpc3RzIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzIzIiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjI0OCIgYWxpYXM9IkNsYWltIj4KICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMjQ3IiBkYXRhVHlwZT0iZmhpcjpDbGFpbSIgeHNpOnR5cGU9IlJldHJpZXZlIi8+CiAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMzIyIiB4c2k6dHlwZT0iQW5kIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjUzIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyNTEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjI1MCIgcGF0aD0ic3RhdHVzIiBzY29wZT0iQ2xhaW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI1MiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImFjdGl2ZSIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzMjEiIHhzaTp0eXBlPSJFeGlzdHMiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzMjAiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMjU1IiBhbGlhcz0iQmJFbGVtIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMjU0IiBwYXRoPSJwcm9jZWR1cmUiIHNjb3BlPSJDbGFpbSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICA8cmVsYXRpb25zaGlwIGxvY2FsSWQ9IjI4NyIgYWxpYXM9IlByb2MxIiB4c2k6dHlwZT0iV2l0aCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjI3MSIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIyNTciIGFsaWFzPSJQcm9jIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMjU2IiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgeHNpOnR5cGU9IlJldHJpZXZlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNvZGVzIG5hbWU9IlVuaWxhdGVyYWwgTWFzdGVjdG9teSBWYWx1ZSBTZXQiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMjcwIiB4c2k6dHlwZT0iQW5kIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjYyIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyNjAiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjI1OSIgcGF0aD0ic3RhdHVzIiBzY29wZT0iUHJvYyIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjYxIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iY29tcGxldGVkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI2OSIgcHJlY2lzaW9uPSJEYXkiIHhzaTp0eXBlPSJTYW1lT3JCZWZvcmUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyNjYiIHhzaTp0eXBlPSJFbmQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyNjUiIG5hbWU9IlBlcmlvZFRvSW50ZXJ2YWxPZkRUIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBhc1R5cGU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyNjQiIHBhdGg9InBlcmZvcm1lZCIgc2NvcGU9IlByb2MiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI2OCIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI2NyIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzdWNoVGhhdCBsb2NhbElkPSIyODYiIHhzaTp0eXBlPSJJZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxjb25kaXRpb24gYXNUeXBlPSJ0OkJvb2xlYW4iIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI3NiIgeHNpOnR5cGU9IklzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjc0IiBwYXRoPSJwcm9jZWR1cmUiIHNjb3BlPSJCYkVsZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8aXNUeXBlU3BlY2lmaWVyIGxvY2FsSWQ9IjI3NSIgbmFtZT0iZmhpcjpSZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIG5hbWU9InQ6Qm9vbGVhbiIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2NvbmRpdGlvbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHRoZW4gbG9jYWxJZD0iMjg0IiB4c2k6dHlwZT0iRXF1aXZhbGVudCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI4MSIgcGF0aD0iaWRlbnRpZmllciIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIyODAiIHN0cmljdD0iZmFsc2UiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI3OCIgcGF0aD0icHJvY2VkdXJlIiBzY29wZT0iQmJFbGVtIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBsb2NhbElkPSIyNzkiIG5hbWU9ImZoaXI6UmVmZXJlbmNlIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJTaW5nbGV0b25Gcm9tIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjgzIiBwYXRoPSJpZGVudGlmaWVyIiBzY29wZT0iUHJvYzEiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvdGhlbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGVsc2UgbG9jYWxJZD0iMjg1IiB2YWx1ZVR5cGU9InQ6Qm9vbGVhbiIgdmFsdWU9ImZhbHNlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L3N1Y2hUaGF0PgogICAgICAgICAgICAgICAgICAgICAgICA8L3JlbGF0aW9uc2hpcD4KICAgICAgICAgICAgICAgICAgICAgICAgPHJlbGF0aW9uc2hpcCBsb2NhbElkPSIzMTkiIGFsaWFzPSJQcm9jMiIgeHNpOnR5cGU9IldpdGgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIzMDMiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMjg5IiBhbGlhcz0iUHJvYyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjI4OCIgZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJMZWZ0IE1vZGlmaWVyIFZhbHVlIFNldCIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhbElkPSIzMDIiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyOTQiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI5MiIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMjkxIiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJQcm9jIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIyOTMiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzAxIiBwcmVjaXNpb249IkRheSIgeHNpOnR5cGU9IlNhbWVPckJlZm9yZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI5OCIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI5NyIgbmFtZT0iUGVyaW9kVG9JbnRlcnZhbE9mRFQiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjI5NiIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iUHJvYyIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzAwIiB4c2k6dHlwZT0iRW5kIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjk5IiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvd2hlcmU+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHN1Y2hUaGF0IGxvY2FsSWQ9IjMxOCIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNvbmRpdGlvbiBhc1R5cGU9InQ6Qm9vbGVhbiIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzA4IiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzMDYiIHBhdGg9InByb2NlZHVyZSIgc2NvcGU9IkJiRWxlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYWxJZD0iMzA3IiBuYW1lPSJmaGlyOlJlZmVyZW5jZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbmFtZT0idDpCb29sZWFuIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGhlbiBsb2NhbElkPSIzMTYiIHhzaTp0eXBlPSJFcXVpdmFsZW50Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzEzIiBwYXRoPSJpZGVudGlmaWVyIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjMxMiIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzEwIiBwYXRoPSJwcm9jZWR1cmUiIHNjb3BlPSJCYkVsZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2FsSWQ9IjMxMSIgbmFtZT0iZmhpcjpSZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzMTUiIHBhdGg9ImlkZW50aWZpZXIiIHNjb3BlPSJQcm9jMiIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZWxzZSBsb2NhbElkPSIzMTciIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0iZmFsc2UiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc3VjaFRoYXQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvcmVsYXRpb25zaGlwPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2FsSWQ9IjM0NCIgbmFtZT0iSXMgVW5pbGF0ZXJhbCBNYXN0ZWN0b215IExlZnQiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6cyByPSIzNDQiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgTGVmdCZxdW90OzoKCTwvYTpzPgogICAgICAgICAgICAgICA8YTpzIHI9IjM0MyI+CiAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzQyIj4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4oCgkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzQyIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMjgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMyNyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzI3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtVbmlsYXRlcmFsIE1hc3RlY3RvbXkgTGVmdCBWYWx1ZSBTZXQmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IFByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNDEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM0MSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzMzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMzEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMzMCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzI5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Qcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMzAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXR1czwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMzMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMzMiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2NvbXBsZXRlZCc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJYW5kIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM0MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzM3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzM2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5QZXJpb2RUb0ludGVydmFsT2ZEVCg8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzMzUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjMzNCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UHJvYzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzM1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBzYW1lIGRheSBvciBiZWZvcmUgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzM5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzM4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCSk8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICA8L2E6cz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIzNDMiIHhzaTp0eXBlPSJFeGlzdHMiPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzNDIiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMzI4IiBhbGlhcz0iUHJvYyI+CiAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjMyNyIgZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+CiAgICAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJVbmlsYXRlcmFsIE1hc3RlY3RvbXkgTGVmdCBWYWx1ZSBTZXQiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPgogICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iMzQxIiB4c2k6dHlwZT0iQW5kIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzMzIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzMzEiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjMzMCIgcGF0aD0ic3RhdHVzIiBzY29wZT0iUHJvYyIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzMyIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iY29tcGxldGVkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjM0MCIgcHJlY2lzaW9uPSJEYXkiIHhzaTp0eXBlPSJTYW1lT3JCZWZvcmUiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzMzciIHhzaTp0eXBlPSJFbmQiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzMzYiIG5hbWU9IlBlcmlvZFRvSW50ZXJ2YWxPZkRUIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBhc1R5cGU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzMzUiIHBhdGg9InBlcmZvcm1lZCIgc2NvcGU9IlByb2MiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjMzOSIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjMzOCIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYWxJZD0iMzY1IiBuYW1lPSJJcyBBYnNlbmNlIE9mIExlZnQgQnJlYXN0IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnMgcj0iMzY1Ij4KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7SXMgQWJzZW5jZSBPZiBMZWZ0IEJyZWFzdCZxdW90OzoKCTwvYTpzPgogICAgICAgICAgICAgICA8YTpzIHI9IjM2NCI+CiAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzYzIj4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4oCgkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzYzIj4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNDgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM0NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzQ3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bT2JzZXJ2YXRpb246IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0Fic2VuY2Ugb2YgTGVmdCBCcmVhc3QgVmFsdWUgU2V0JnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBPYnM8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNjIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM2MiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzU1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNTEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM1MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzQ5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5PYnM8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM1MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzUxIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz52YWx1ZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IGluIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM1NCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+eyA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNTIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidmaW5hbCc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LCA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNTMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidhbWVuZGVkJzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gfTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQlhbmQgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzYxIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNTgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM1NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzU2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5PYnM8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM1NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZWZmZWN0aXZlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzU4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz52YWx1ZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IHNhbWUgZGF5IG9yIGJlZm9yZSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNjAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNTkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPgoJKTwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgIDwvYTpzPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjM2NCIgeHNpOnR5cGU9IkV4aXN0cyI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjM2MyIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIzNDgiIGFsaWFzPSJPYnMiPgogICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIzNDciIGRhdGFUeXBlPSJmaGlyOk9ic2VydmF0aW9uIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+CiAgICAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJBYnNlbmNlIG9mIExlZnQgQnJlYXN0IFZhbHVlIFNldCIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhbElkPSIzNjIiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzNTUiIHhzaTp0eXBlPSJJbiI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjM1MSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMzUwIiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJPYnMiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjM1NCIgeHNpOnR5cGU9Ikxpc3QiPgogICAgICAgICAgICAgICAgICAgICAgICA8ZWxlbWVudCBsb2NhbElkPSIzNTIiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJmaW5hbCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbG9jYWxJZD0iMzUzIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iYW1lbmRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjM2MSIgcHJlY2lzaW9uPSJEYXkiIHhzaTp0eXBlPSJTYW1lT3JCZWZvcmUiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzNTgiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjM1NyIgcGF0aD0iZWZmZWN0aXZlIiBzY29wZT0iT2JzIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzNjAiIHhzaTp0eXBlPSJFbmQiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzNTkiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2FsSWQ9IjM2OCIgbmFtZT0iSXMgTGFja2luZyBMZWZ0IEJyZWFzdCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTpzIHI9IjM2OCI+CiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0lzIExhY2tpbmcgTGVmdCBCcmVhc3QmcXVvdDs6Cgk8L2E6cz4KICAgICAgICAgICAgICAgPGE6cyByPSIzNjciPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjM0NiI+CiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzI2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgV2l0aCBMZWZ0IE1vZGlmaWVyJnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCW9yIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM0NSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SXMgVW5pbGF0ZXJhbCBNYXN0ZWN0b215IExlZnQmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzPgoJCW9yIDwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjM2NiI+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SXMgQWJzZW5jZSBPZiBMZWZ0IEJyZWFzdCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgIDwvYTpzPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjM2NyIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzQ2IiB4c2k6dHlwZT0iT3IiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzMjYiIG5hbWU9IklzIFVuaWxhdGVyYWwgTWFzdGVjdG9teSBXaXRoIExlZnQgTW9kaWZpZXIiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjM0NSIgbmFtZT0iSXMgVW5pbGF0ZXJhbCBNYXN0ZWN0b215IExlZnQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzY2IiBuYW1lPSJJcyBBYnNlbmNlIE9mIExlZnQgQnJlYXN0IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2FsSWQ9IjQ0OCIgbmFtZT0iSXMgVW5pbGF0ZXJhbCBNYXN0ZWN0b215IFdpdGggUmlnaHQgTW9kaWZpZXIiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6cyByPSI0NDgiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgV2l0aCBSaWdodCBNb2RpZmllciZxdW90OzoKCTwvYTpzPgogICAgICAgICAgICAgICA8YTpzIHI9IjQ0NyI+CiAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDQ2Ij4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4oCgkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDQ2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNzEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM3MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzcwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bQ2xhaW1dPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gQ2xhaW08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NDUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ0NSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzc2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNzQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM3MyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzcyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5DbGFpbTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzczIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNzQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNzUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidhY3RpdmUnPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCWFuZCA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NDQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0czwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ0MyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KAoJCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDQzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzNzgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM3NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Q2xhaW0ucHJvY2VkdXJlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gQmJFbGVtPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MTAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPndpdGggPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzk1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzOTQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPigKCQkJCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzk0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzODAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM3OSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzc5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bUHJvY2VkdXJlOiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtVbmlsYXRlcmFsIE1hc3RlY3RvbXkgVmFsdWUgU2V0JnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBQcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM5MyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzkzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzODUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM4MyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzgyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzODEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM4MiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzgzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz52YWx1ZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ID0gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzg0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29tcGxldGVkJzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJCQkJYW5kIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM5MiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzg5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzg4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5QZXJpb2RUb0ludGVydmFsT2ZEVCg8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzODciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM4NiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UHJvYzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzg3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBzYW1lIGRheSBvciBiZWZvcmUgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzkxIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzkwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkpPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBQcm9jMTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkJc3VjaCB0aGF0CgkJCQkJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MDkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPig8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MDkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmlmIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM5OSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzk3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzOTYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkJiRWxlbTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMzk3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wcm9jZWR1cmU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBpcyA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSIzOTgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZISVIuUmVmZXJlbmNlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkJCQl0aGVuIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQwNyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDA0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MDMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPig8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MDMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQwMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDAwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5CYkVsZW08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQwMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cHJvY2VkdXJlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gYXMgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDAyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GSElSLlJlZmVyZW5jZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDA0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5pZGVudGlmaWVyPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gfiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MDYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQwNSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UHJvYzE8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQwNiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+aWRlbnRpZmllcjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCQkJCWVsc2UgZmFsc2U8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDQyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz53aXRoIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQyNyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDI2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4oCgkJCQkJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQyNiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDEyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MTEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQxMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+W1Byb2NlZHVyZTogPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7UmlnaHQgTW9kaWZpZXIgVmFsdWUgU2V0JnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBQcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQyNSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+d2hlcmUgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDI1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MTciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQxNSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDE0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MTMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQxNCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3RhdHVzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDE1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz52YWx1ZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ID0gPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDE2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nY29tcGxldGVkJzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJCQkJCQkJYW5kIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQyNCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDIxIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDIwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5QZXJpb2RUb0ludGVydmFsT2ZEVCg8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MTkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQxOCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UHJvYzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDE5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBzYW1lIGRheSBvciBiZWZvcmUgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDIzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDIyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkpPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBQcm9jMjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkJc3VjaCB0aGF0CgkJCQkJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NDEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPig8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NDEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmlmIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQzMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDI5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MjgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkJiRWxlbTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDI5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wcm9jZWR1cmU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBpcyA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MzAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPkZISVIuUmVmZXJlbmNlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCQkJCQl0aGVuIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQzOSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDM2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MzUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPig8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MzUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQzMyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDMyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5CYkVsZW08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQzMyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cHJvY2VkdXJlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gYXMgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDM0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5GSElSLlJlZmVyZW5jZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDM2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5pZGVudGlmaWVyPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gfiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0MzgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQzNyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UHJvYzI8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQzOCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+aWRlbnRpZmllcjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJCQkJCWVsc2UgZmFsc2U8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkpPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCSk8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICA8L2E6cz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI0NDciIHhzaTp0eXBlPSJFeGlzdHMiPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0NDYiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMzcxIiBhbGlhcz0iQ2xhaW0iPgogICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIzNzAiIGRhdGFUeXBlPSJmaGlyOkNsYWltIiB4c2k6dHlwZT0iUmV0cmlldmUiLz4KICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhbElkPSI0NDUiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzNzYiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjM3NCIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMzczIiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJDbGFpbSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzc1IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iYWN0aXZlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQ0NCIgeHNpOnR5cGU9IkV4aXN0cyI+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQ0MyIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSIzNzgiIGFsaWFzPSJCYkVsZW0iPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIzNzciIHBhdGg9InByb2NlZHVyZSIgc2NvcGU9IkNsYWltIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgIDxyZWxhdGlvbnNoaXAgbG9jYWxJZD0iNDEwIiBhbGlhcz0iUHJvYzEiIHhzaTp0eXBlPSJXaXRoIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iMzk0IiB4c2k6dHlwZT0iUXVlcnkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjM4MCIgYWxpYXM9IlByb2MiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSIzNzkiIGRhdGFUeXBlPSJmaGlyOlByb2NlZHVyZSIgY29kZVByb3BlcnR5PSJjb2RlIiB4c2k6dHlwZT0iUmV0cmlldmUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Y29kZXMgbmFtZT0iVW5pbGF0ZXJhbCBNYXN0ZWN0b215IFZhbHVlIFNldCIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhbElkPSIzOTMiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzODUiIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjM4MyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iMzgyIiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJQcm9jIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzODQiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzkyIiBwcmVjaXNpb249IkRheSIgeHNpOnR5cGU9IlNhbWVPckJlZm9yZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjM4OSIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjM4OCIgbmFtZT0iUGVyaW9kVG9JbnRlcnZhbE9mRFQiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjM4NyIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iUHJvYyIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzkxIiB4c2k6dHlwZT0iRW5kIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzkwIiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvd2hlcmU+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHN1Y2hUaGF0IGxvY2FsSWQ9IjQwOSIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNvbmRpdGlvbiBhc1R5cGU9InQ6Qm9vbGVhbiIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzk5IiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSIzOTciIHBhdGg9InByb2NlZHVyZSIgc2NvcGU9IkJiRWxlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYWxJZD0iMzk4IiBuYW1lPSJmaGlyOlJlZmVyZW5jZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbmFtZT0idDpCb29sZWFuIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGhlbiBsb2NhbElkPSI0MDciIHhzaTp0eXBlPSJFcXVpdmFsZW50Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDA0IiBwYXRoPSJpZGVudGlmaWVyIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjQwMyIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDAxIiBwYXRoPSJwcm9jZWR1cmUiIHNjb3BlPSJCYkVsZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2FsSWQ9IjQwMiIgbmFtZT0iZmhpcjpSZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0MDYiIHBhdGg9ImlkZW50aWZpZXIiIHNjb3BlPSJQcm9jMSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZWxzZSBsb2NhbElkPSI0MDgiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0iZmFsc2UiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc3VjaFRoYXQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvcmVsYXRpb25zaGlwPgogICAgICAgICAgICAgICAgICAgICAgICA8cmVsYXRpb25zaGlwIGxvY2FsSWQ9IjQ0MiIgYWxpYXM9IlByb2MyIiB4c2k6dHlwZT0iV2l0aCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjQyNiIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI0MTIiIGFsaWFzPSJQcm9jIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iNDExIiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgeHNpOnR5cGU9IlJldHJpZXZlIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNvZGVzIG5hbWU9IlJpZ2h0IE1vZGlmaWVyIFZhbHVlIFNldCIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDx3aGVyZSBsb2NhbElkPSI0MjUiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0MTciIHhzaTp0eXBlPSJFcXVhbCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQxNSIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iNDE0IiBwYXRoPSJzdGF0dXMiIHNjb3BlPSJQcm9jIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0MTYiIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDI0IiBwcmVjaXNpb249IkRheSIgeHNpOnR5cGU9IlNhbWVPckJlZm9yZSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQyMSIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQyMCIgbmFtZT0iUGVyaW9kVG9JbnRlcnZhbE9mRFQiIHhzaTp0eXBlPSJGdW5jdGlvblJlZiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGFzVHlwZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJBcyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQxOSIgcGF0aD0icGVyZm9ybWVkIiBzY29wZT0iUHJvYyIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbmFtZT0iZmhpcjpQZXJpb2QiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDIzIiB4c2k6dHlwZT0iRW5kIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDIyIiBuYW1lPSJNZWFzdXJlbWVudCBQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvd2hlcmU+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPHN1Y2hUaGF0IGxvY2FsSWQ9IjQ0MSIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGNvbmRpdGlvbiBhc1R5cGU9InQ6Qm9vbGVhbiIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDMxIiB4c2k6dHlwZT0iSXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0MjkiIHBhdGg9InByb2NlZHVyZSIgc2NvcGU9IkJiRWxlbSIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpc1R5cGVTcGVjaWZpZXIgbG9jYWxJZD0iNDMwIiBuYW1lPSJmaGlyOlJlZmVyZW5jZSIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhc1R5cGVTcGVjaWZpZXIgbmFtZT0idDpCb29sZWFuIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvY29uZGl0aW9uPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8dGhlbiBsb2NhbElkPSI0MzkiIHhzaTp0eXBlPSJFcXVpdmFsZW50Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDM2IiBwYXRoPSJpZGVudGlmaWVyIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjQzNSIgc3RyaWN0PSJmYWxzZSIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDMzIiBwYXRoPSJwcm9jZWR1cmUiIHNjb3BlPSJCYkVsZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIGxvY2FsSWQ9IjQzNCIgbmFtZT0iZmhpcjpSZWZlcmVuY2UiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IlNpbmdsZXRvbkZyb20iPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0MzgiIHBhdGg9ImlkZW50aWZpZXIiIHNjb3BlPSJQcm9jMiIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC90aGVuPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZWxzZSBsb2NhbElkPSI0NDAiIHZhbHVlVHlwZT0idDpCb29sZWFuIiB2YWx1ZT0iZmFsc2UiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvc3VjaFRoYXQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvcmVsYXRpb25zaGlwPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2FsSWQ9IjQ2NyIgbmFtZT0iSXMgVW5pbGF0ZXJhbCBNYXN0ZWN0b215IFJpZ2h0IiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4KICAgICAgICAgPGFubm90YXRpb24geHNpOnR5cGU9ImE6QW5ub3RhdGlvbiI+CiAgICAgICAgICAgIDxhOnMgcj0iNDY3Ij4KICAgICAgICAgICAgICAgPGE6cz5kZWZpbmUgJnF1b3Q7SXMgVW5pbGF0ZXJhbCBNYXN0ZWN0b215IFJpZ2h0JnF1b3Q7OgoJPC9hOnM+CiAgICAgICAgICAgICAgIDxhOnMgcj0iNDY2Ij4KICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHM8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NjUiPgogICAgICAgICAgICAgICAgICAgICA8YTpzPigKCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NjUiPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ1MSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDUwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NTAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPltQcm9jZWR1cmU6IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O1VuaWxhdGVyYWwgTWFzdGVjdG9teSBSaWdodCBWYWx1ZSBTZXQmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+XTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IFByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NjQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPndoZXJlIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ2NCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDU2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NTQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ1MyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDUyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5Qcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NTMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXR1czwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ1NCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiA9IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ1NSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+J2NvbXBsZXRlZCc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJYW5kIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ2MyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDYwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDU5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5QZXJpb2RUb0ludGVydmFsT2ZEVCg8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NTgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ1NyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UHJvYzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDU4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5wZXJmb3JtZWQ8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPik8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBzYW1lIGRheSBvciBiZWZvcmUgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDYyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDYxIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtNZWFzdXJlbWVudCBQZXJpb2QmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCSk8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICA8L2E6cz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI0NjYiIHhzaTp0eXBlPSJFeGlzdHMiPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0NjUiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iNDUxIiBhbGlhcz0iUHJvYyI+CiAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjQ1MCIgZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+CiAgICAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJVbmlsYXRlcmFsIE1hc3RlY3RvbXkgUmlnaHQgVmFsdWUgU2V0IiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICAgICAgICAgICA8L3NvdXJjZT4KICAgICAgICAgICAgICAgPHdoZXJlIGxvY2FsSWQ9IjQ2NCIgeHNpOnR5cGU9IkFuZCI+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQ1NiIgeHNpOnR5cGU9IkVxdWFsIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDU0IiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI0NTMiIHBhdGg9InN0YXR1cyIgc2NvcGU9IlByb2MiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQ1NSIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0NjMiIHByZWNpc2lvbj0iRGF5IiB4c2k6dHlwZT0iU2FtZU9yQmVmb3JlIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDYwIiB4c2k6dHlwZT0iRW5kIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDU5IiBuYW1lPSJQZXJpb2RUb0ludGVydmFsT2ZEVCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgYXNUeXBlPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDU4IiBwYXRoPSJwZXJmb3JtZWQiIHNjb3BlPSJQcm9jIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBuYW1lPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9Ik5hbWVkVHlwZVNwZWNpZmllciIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0NjIiIHhzaTp0eXBlPSJFbmQiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0NjEiIG5hbWU9Ik1lYXN1cmVtZW50IFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC93aGVyZT4KICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgIDwvZGVmPgogICAgICA8ZGVmIGxvY2FsSWQ9IjQ4OSIgbmFtZT0iSXMgQWJzZW5jZSBPZiBSaWdodCBCcmVhc3QiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6cyByPSI0ODkiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtJcyBBYnNlbmNlIE9mIFJpZ2h0IEJyZWFzdCZxdW90OzoKCTwvYTpzPgogICAgICAgICAgICAgICA8YTpzIHI9IjQ4OCI+CiAgICAgICAgICAgICAgICAgIDxhOnM+ZXhpc3RzPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDg3Ij4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4oCgkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDg3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NzEiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ3MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDcwIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5bT2JzZXJ2YXRpb246IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0Fic2VuY2Ugb2YgUmlnaHQgQnJlYXN0IFZhbHVlIFNldCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5dPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gT2JzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDg2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz53aGVyZSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0ODYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ3OCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDc0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NzMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ3MiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+T2JzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NzMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnN0YXR1czwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ3NCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBpbiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NzciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnsgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDc1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nZmluYWwnPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiwgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDc2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4nYW1lbmRlZCc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IH08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCQkJYW5kIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ4NSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDgyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5lbmQgb2YgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDgxIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5QZXJpb2RUb0ludGVydmFsT2ZEVCg8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0ODAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ3OSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+T2JzPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0ODAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVmZmVjdGl2ZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+IHNhbWUgZGF5IG9yIGJlZm9yZSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0ODQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmVuZCBvZiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0ODMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O01lYXN1cmVtZW50IFBlcmlvZCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPgoJKTwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgIDwvYTpzPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjQ4OCIgeHNpOnR5cGU9IkV4aXN0cyI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQ4NyIgeHNpOnR5cGU9IlF1ZXJ5Ij4KICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI0NzEiIGFsaWFzPSJPYnMiPgogICAgICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI0NzAiIGRhdGFUeXBlPSJmaGlyOk9ic2VydmF0aW9uIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+CiAgICAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJBYnNlbmNlIG9mIFJpZ2h0IEJyZWFzdCBWYWx1ZSBTZXQiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPgogICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iNDg2IiB4c2k6dHlwZT0iQW5kIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDc4IiB4c2k6dHlwZT0iSW4iPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0NzQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjQ3MyIgcGF0aD0ic3RhdHVzIiBzY29wZT0iT2JzIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0NzciIHhzaTp0eXBlPSJMaXN0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGVsZW1lbnQgbG9jYWxJZD0iNDc1IiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iZmluYWwiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDxlbGVtZW50IGxvY2FsSWQ9IjQ3NiIgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImFtZW5kZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0ODUiIHByZWNpc2lvbj0iRGF5IiB4c2k6dHlwZT0iU2FtZU9yQmVmb3JlIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDgyIiB4c2k6dHlwZT0iRW5kIj4KICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDgxIiBuYW1lPSJQZXJpb2RUb0ludGVydmFsT2ZEVCIgeHNpOnR5cGU9IkZ1bmN0aW9uUmVmIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgYXNUeXBlPSJmaGlyOlBlcmlvZCIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDgwIiBwYXRoPSJlZmZlY3RpdmUiIHNjb3BlPSJPYnMiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQ4NCIgeHNpOnR5cGU9IkVuZCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQ4MyIgbmFtZT0iTWVhc3VyZW1lbnQgUGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICA8L3doZXJlPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYWxJZD0iNDkyIiBuYW1lPSJJcyBMYWNraW5nIFJpZ2h0IEJyZWFzdCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTpzIHI9IjQ5MiI+CiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0lzIExhY2tpbmcgUmlnaHQgQnJlYXN0JnF1b3Q7OgoJPC9hOnM+CiAgICAgICAgICAgICAgIDxhOnMgcj0iNDkxIj4KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0NjkiPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ0OSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SXMgVW5pbGF0ZXJhbCBNYXN0ZWN0b215IFdpdGggUmlnaHQgTW9kaWZpZXImcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+CgkJb3IgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDY4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgUmlnaHQmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzPgoJCW9yIDwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjQ5MCI+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SXMgQWJzZW5jZSBPZiBSaWdodCBCcmVhc3QmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICA8L2E6cz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI0OTEiIHhzaTp0eXBlPSJPciI+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjQ2OSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDQ5IiBuYW1lPSJJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgV2l0aCBSaWdodCBNb2RpZmllciIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDY4IiBuYW1lPSJJcyBVbmlsYXRlcmFsIE1hc3RlY3RvbXkgUmlnaHQiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDkwIiBuYW1lPSJJcyBBYnNlbmNlIE9mIFJpZ2h0IEJyZWFzdCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhbElkPSI0OTYiIG5hbWU9IkhhcyBEb3VibGUgTWFzdGVjdG9teSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTpzIHI9IjQ5NiI+CiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O0hhcyBEb3VibGUgTWFzdGVjdG9teSZxdW90OzoKICAgIDwvYTpzPgogICAgICAgICAgICAgICA8YTpzIHI9IjQ5NSI+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iMjQ2Ij4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtJcyBMYWNraW5nIEJvdGggQnJlYXN0cyZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICBvciA8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cyByPSI0OTQiPgogICAgICAgICAgICAgICAgICAgICA8YTpzPig8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0OTQiPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjM2OSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SXMgTGFja2luZyBMZWZ0IEJyZWFzdCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICBhbmQgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDkzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtJcyBMYWNraW5nIFJpZ2h0IEJyZWFzdCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+KTwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgIDwvYTpzPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjQ5NSIgeHNpOnR5cGU9Ik9yIj4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMjQ2IiBuYW1lPSJJcyBMYWNraW5nIEJvdGggQnJlYXN0cyIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDk0IiB4c2k6dHlwZT0iQW5kIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iMzY5IiBuYW1lPSJJcyBMYWNraW5nIExlZnQgQnJlYXN0IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI0OTMiIG5hbWU9IklzIExhY2tpbmcgUmlnaHQgQnJlYXN0IiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYWxJZD0iNTA0IiBuYW1lPSJEb2VzIFBhdGllbnQgUXVhbGlmeT8iIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6cyByPSI1MDQiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtEb2VzIFBhdGllbnQgUXVhbGlmeT8mcXVvdDs6Cgk8L2E6cz4KICAgICAgICAgICAgICAgPGE6cyByPSI1MDMiPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjY1Ij4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1NiI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SXMgRmVtYWxlJnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPgoJCWFuZCA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI2NCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SXMgQWdlIDUyIHRvIDc0IGF0IEVuZCZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnM+CgkJYW5kIDwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjUwMiI+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+aWYgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNDk4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI0OTciPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0hhcyBEb3VibGUgTWFzdGVjdG9teSZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaXMgbnVsbDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkgICAgICAgIHRoZW4gdHJ1ZQoJCSAgICBlbHNlIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUwMSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+bm90IDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUwMCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7SGFzIERvdWJsZSBNYXN0ZWN0b215JnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICA8L2E6cz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI1MDMiIHhzaTp0eXBlPSJBbmQiPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI2NSIgeHNpOnR5cGU9IkFuZCI+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjU2IiBuYW1lPSJJcyBGZW1hbGUiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjY0IiBuYW1lPSJJcyBBZ2UgNTIgdG8gNzQgYXQgRW5kIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjUwMiIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgICAgPGNvbmRpdGlvbiBhc1R5cGU9InQ6Qm9vbGVhbiIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDk4IiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNDk3IiBuYW1lPSJIYXMgRG91YmxlIE1hc3RlY3RvbXkiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBuYW1lPSJ0OkJvb2xlYW4iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgICAgIDx0aGVuIGxvY2FsSWQ9IjQ5OSIgdmFsdWVUeXBlPSJ0OkJvb2xlYW4iIHZhbHVlPSJ0cnVlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICA8ZWxzZSBsb2NhbElkPSI1MDEiIHhzaTp0eXBlPSJOb3QiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI1MDAiIG5hbWU9IkhhcyBEb3VibGUgTWFzdGVjdG9teSIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4KICAgICAgICAgICAgICAgPC9lbHNlPgogICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYWxJZD0iNTIwIiBuYW1lPSJJcyBNYW1tb2dyYW0gSW4gTGFzdCAzOSBNb250aHMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPgogICAgICAgICA8YW5ub3RhdGlvbiB4c2k6dHlwZT0iYTpBbm5vdGF0aW9uIj4KICAgICAgICAgICAgPGE6cyByPSI1MjAiPgogICAgICAgICAgICAgICA8YTpzPmRlZmluZSAmcXVvdDtJcyBNYW1tb2dyYW0gSW4gTGFzdCAzOSBNb250aHMmcXVvdDs6Cgk8L2E6cz4KICAgICAgICAgICAgICAgPGE6cyByPSI1MTkiPgogICAgICAgICAgICAgICAgICA8YTpzPmV4aXN0czwvYTpzPgogICAgICAgICAgICAgICAgICA8YTpzIHI9IjUxOCI+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+KAoJCTwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUxOCI+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTA2Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MDUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUwNSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+W1Byb2NlZHVyZTogPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+JnF1b3Q7TWFtbW9ncmFwaHkgVmFsdWUgU2V0JnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPl08L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBQcm9jPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTE3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz53aGVyZSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MTciPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUxMSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTA5Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MDgiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUwNyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UHJvYzwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTA4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5zdGF0dXM8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MDkiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4gPSA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MTAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPidjb21wbGV0ZWQnPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCQkJCWFuZCA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MTYiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUxNCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+UGVyaW9kVG9JbnRlcnZhbE9mRFQoPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTEzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MTIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPlByb2M8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUxMyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+cGVyZm9ybWVkPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4pPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiBpbmNsdWRlZCBpbiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MTUiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0xvb2tiYWNrIEludGVydmFsIDI3IE1vcmUgTW9udGhzJnF1b3Q7PC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4KCSk8L2E6cz4KICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICA8L2E6cz4KICAgICAgICAgPC9hbm5vdGF0aW9uPgogICAgICAgICA8ZXhwcmVzc2lvbiBsb2NhbElkPSI1MTkiIHhzaTp0eXBlPSJFeGlzdHMiPgogICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI1MTgiIHhzaTp0eXBlPSJRdWVyeSI+CiAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iNTA2IiBhbGlhcz0iUHJvYyI+CiAgICAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjUwNSIgZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+CiAgICAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJNYW1tb2dyYXBoeSBWYWx1ZSBTZXQiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPgogICAgICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+CiAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8d2hlcmUgbG9jYWxJZD0iNTE3IiB4c2k6dHlwZT0iQW5kIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNTExIiB4c2k6dHlwZT0iRXF1YWwiPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI1MDkiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjUwOCIgcGF0aD0ic3RhdHVzIiBzY29wZT0iUHJvYyIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+CiAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNTEwIiB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iY29tcGxldGVkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIGxvY2FsSWQ9IjUxNiIgeHNpOnR5cGU9IkluY2x1ZGVkSW4iPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI1MTQiIG5hbWU9IlBlcmlvZFRvSW50ZXJ2YWxPZkRUIiB4c2k6dHlwZT0iRnVuY3Rpb25SZWYiPgogICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBhc1R5cGU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iQXMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI1MTMiIHBhdGg9InBlcmZvcm1lZCIgc2NvcGU9IlByb2MiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YXNUeXBlU3BlY2lmaWVyIG5hbWU9ImZoaXI6UGVyaW9kIiB4c2k6dHlwZT0iTmFtZWRUeXBlU3BlY2lmaWVyIi8+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI1MTUiIG5hbWU9Ikxvb2tiYWNrIEludGVydmFsIDI3IE1vcmUgTW9udGhzIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPgogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+CiAgICAgICAgICAgICAgIDwvd2hlcmU+CiAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgPC9leHByZXNzaW9uPgogICAgICA8L2RlZj4KICAgICAgPGRlZiBsb2NhbElkPSI1MjgiIG5hbWU9Ik5lZWRzIE1hbW1vZ3JhbSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTpzIHI9IjUyOCI+CiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lICZxdW90O05lZWRzIE1hbW1vZ3JhbSZxdW90OzoKCTwvYTpzPgogICAgICAgICAgICAgICA8YTpzIHI9IjUyNyI+CiAgICAgICAgICAgICAgICAgIDxhOnM+aWYgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTIyIj4KICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MjEiPgogICAgICAgICAgICAgICAgICAgICAgICA8YTpzPiZxdW90O0lzIE1hbW1vZ3JhbSBJbiBMYXN0IDM5IE1vbnRocyZxdW90OzwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4gaXMgbnVsbDwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgPGE6cz4KCSAgICB0aGVuIHRydWUKCWVsc2UgPC9hOnM+CiAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTI2Ij4KICAgICAgICAgICAgICAgICAgICAgPGE6cz5ub3QgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTI1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5leGlzdHMgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTI0Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4mcXVvdDtJcyBNYW1tb2dyYW0gSW4gTGFzdCAzOSBNb250aHMmcXVvdDs8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgIDwvYTpzPgogICAgICAgICA8L2Fubm90YXRpb24+CiAgICAgICAgIDxleHByZXNzaW9uIGxvY2FsSWQ9IjUyNyIgeHNpOnR5cGU9IklmIj4KICAgICAgICAgICAgPGNvbmRpdGlvbiBhc1R5cGU9InQ6Qm9vbGVhbiIgeHNpOnR5cGU9IkFzIj4KICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNTIyIiB4c2k6dHlwZT0iSXNOdWxsIj4KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNTIxIiBuYW1lPSJJcyBNYW1tb2dyYW0gSW4gTGFzdCAzOSBNb250aHMiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPGFzVHlwZVNwZWNpZmllciBuYW1lPSJ0OkJvb2xlYW4iIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgICAgPC9jb25kaXRpb24+CiAgICAgICAgICAgIDx0aGVuIGxvY2FsSWQ9IjUyMyIgdmFsdWVUeXBlPSJ0OkJvb2xlYW4iIHZhbHVlPSJ0cnVlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPgogICAgICAgICAgICA8ZWxzZSBsb2NhbElkPSI1MjYiIHhzaTp0eXBlPSJOb3QiPgogICAgICAgICAgICAgICA8b3BlcmFuZCBsb2NhbElkPSI1MjUiIHhzaTp0eXBlPSJFeGlzdHMiPgogICAgICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iVG9MaXN0Ij4KICAgICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbG9jYWxJZD0iNTI0IiBuYW1lPSJJcyBNYW1tb2dyYW0gSW4gTGFzdCAzOSBNb250aHMiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4KICAgICAgICAgICAgICAgPC9vcGVyYW5kPgogICAgICAgICAgICA8L2Vsc2U+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgPC9kZWY+CiAgICAgIDxkZWYgbG9jYWxJZD0iNTQ1IiBuYW1lPSJDb2RpbmdUb0NvZGUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiIHhzaTp0eXBlPSJGdW5jdGlvbkRlZiI+CiAgICAgICAgIDxhbm5vdGF0aW9uIHhzaTp0eXBlPSJhOkFubm90YXRpb24iPgogICAgICAgICAgICA8YTpzIHI9IjU0NSI+CiAgICAgICAgICAgICAgIDxhOnM+ZGVmaW5lIGZ1bmN0aW9uIENvZGluZ1RvQ29kZShjb2RpbmcgPC9hOnM+CiAgICAgICAgICAgICAgIDxhOnMgcj0iNTMxIj4KICAgICAgICAgICAgICAgICAgPGE6cz5GSElSLkNvZGluZzwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgPGE6cz4pOgoJPC9hOnM+CiAgICAgICAgICAgICAgIDxhOnMgcj0iNTQ0Ij4KICAgICAgICAgICAgICAgICAgPGE6cyByPSI1NDQiPgogICAgICAgICAgICAgICAgICAgICA8YTpzPlN5c3RlbS5Db2RlIHsKCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5jb2RlOiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MzQiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUzMyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTMyIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5jb2Rpbmc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUzMyI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Y29kZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUzNCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4sCgkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3lzdGVtOiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1MzciPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUzNiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTM1Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5jb2Rpbmc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUzNiI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+c3lzdGVtPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz4uPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTM3Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz52YWx1ZTwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8YTpzPiwKCQk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz52ZXJzaW9uOiA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1NDAiPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUzOSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTM4Ij4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cz5jb2Rpbmc8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjUzOSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmVyc2lvbjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+LjwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjU0MCI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+dmFsdWU8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPGE6cz4sCgkJPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+ZGlzcGxheTogPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgIDxhOnMgcj0iNTQzIj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1NDIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzIHI9IjU0MSI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhOnM+Y29kaW5nPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1NDIiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPmRpc3BsYXk8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPi48L2E6cz4KICAgICAgICAgICAgICAgICAgICAgICAgICAgPGE6cyByPSI1NDMiPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YTpzPnZhbHVlPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgICAgICAgICAgICAgIDxhOnM+Cgl9PC9hOnM+CiAgICAgICAgICAgICAgICAgIDwvYTpzPgogICAgICAgICAgICAgICA8L2E6cz4KICAgICAgICAgICAgPC9hOnM+CiAgICAgICAgIDwvYW5ub3RhdGlvbj4KICAgICAgICAgPGV4cHJlc3Npb24gbG9jYWxJZD0iNTQ0IiBjbGFzc1R5cGU9InQ6Q29kZSIgeHNpOnR5cGU9Ikluc3RhbmNlIj4KICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0iY29kZSI+CiAgICAgICAgICAgICAgIDx2YWx1ZSBsb2NhbElkPSI1MzQiIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPgogICAgICAgICAgICAgICAgICA8c291cmNlIGxvY2FsSWQ9IjUzMyIgcGF0aD0iY29kZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI1MzIiIG5hbWU9ImNvZGluZyIgeHNpOnR5cGU9Ik9wZXJhbmRSZWYiLz4KICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+CiAgICAgICAgICAgICAgIDwvdmFsdWU+CiAgICAgICAgICAgIDwvZWxlbWVudD4KICAgICAgICAgICAgPGVsZW1lbnQgbmFtZT0ic3lzdGVtIj4KICAgICAgICAgICAgICAgPHZhbHVlIGxvY2FsSWQ9IjUzNyIgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iNTM2IiBwYXRoPSJzeXN0ZW0iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iNTM1IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9InZlcnNpb24iPgogICAgICAgICAgICAgICA8dmFsdWUgbG9jYWxJZD0iNTQwIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI1MzkiIHBhdGg9InZlcnNpb24iIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iNTM4IiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgICAgIDxlbGVtZW50IG5hbWU9ImRpc3BsYXkiPgogICAgICAgICAgICAgICA8dmFsdWUgbG9jYWxJZD0iNTQzIiBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4KICAgICAgICAgICAgICAgICAgPHNvdXJjZSBsb2NhbElkPSI1NDIiIHBhdGg9ImRpc3BsYXkiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+CiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbG9jYWxJZD0iNTQxIiBuYW1lPSJjb2RpbmciIHhzaTp0eXBlPSJPcGVyYW5kUmVmIi8+CiAgICAgICAgICAgICAgICAgIDwvc291cmNlPgogICAgICAgICAgICAgICA8L3ZhbHVlPgogICAgICAgICAgICA8L2VsZW1lbnQ+CiAgICAgICAgIDwvZXhwcmVzc2lvbj4KICAgICAgICAgPG9wZXJhbmQgbmFtZT0iY29kaW5nIj4KICAgICAgICAgICAgPG9wZXJhbmRUeXBlU3BlY2lmaWVyIGxvY2FsSWQ9IjUzMSIgbmFtZT0iZmhpcjpDb2RpbmciIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4KICAgICAgICAgPC9vcGVyYW5kPgogICAgICA8L2RlZj4KICAgPC9zdGF0ZW1lbnRzPgo8L2xpYnJhcnk+Cg==" + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-patient.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-patient.json new file mode 100644 index 00000000000..1749d4395d4 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-patient.json @@ -0,0 +1,96 @@ +{ + "resourceType": "Patient", + "id": "Patient-6532", + "extension": [ + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/Race", + "code": "2106-3", + "display": "White" + } + ] + } + }, + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/Ethnicity", + "code": "2186-5", + "display": "Not Hispanic or Latino" + } + ] + } + }, + { + "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-religion", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ReligiousAffiliation", + "code": "1041", + "display": "Roman Catholic Church" + } + ] + } + } + ], + "identifier": [ + { + "use": "official", + "type": { + "coding": [ + { + "system": "http://hl7.org/fhir/identifier-type", + "code": "SB", + "display": "Social Beneficiary Identifier" + } + ], + "text": "US Social Security Number" + }, + "system": "http://hl7.org/fhir/sid/us-ssn", + "value": "000006532" + } + ], + "active": true, + "name": [ + { + "family": "Brandt", + "given": [ + "Edith", + "Elaine" + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "616-555-1082", + "use": "home" + }, + { + "system": "phone", + "value": "616-555-1211", + "use": "mobile" + } + ], + "gender": "female", + "birthDate": "1987-07-16", + "address": [ + { + "use": "home", + "type": "postal", + "line": [ + "893 N Elm Drive" + ], + "city": "Grand Rapids", + "district": "Kent County", + "state": "MI", + "postalCode": "49504" + } + ] +} diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-plandefinition.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-plandefinition.json new file mode 100644 index 00000000000..a95c264a80c --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-plandefinition.json @@ -0,0 +1,33 @@ +{ + "resourceType": "PlanDefinition", + "id": "bcs-decision-support", + "status": "draft", + "library": { + "reference": "Library/patient-view" + }, + "action": [ + { + "condition": [ + { + "kind": "applicability", + "language": "text/cql", + "expression": "Does Patient Qualify?" + } + ], + "action": [ + { + "condition": [ + { + "kind": "applicability", + "language": "text/cql", + "expression": "Needs Mammogram" + } + ], + "definition": { + "reference": "ActivityDefinition/mammogram-service-request" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-request.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-request.json new file mode 100644 index 00000000000..8f3e1c228ae --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/cds-bcs-request.json @@ -0,0 +1,9 @@ +{ + "hookInstance": "d1577c69-dfbe-44ad-ba6d-3e05e953b2ea", + "fhirServer": "https://sb-fhir-dstu2.smarthealthit.org/smartdstu2/open", + "hook": "patient-view", + "user": "Practitioner/example", + "context": [], + "patient": "Patient/Patient-6535", + "prefetch": {} +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/general-fhirhelpers-3.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/general-fhirhelpers-3.json new file mode 100644 index 00000000000..13dd36df40c --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/general-fhirhelpers-3.json @@ -0,0 +1,19 @@ +{ + "resourceType": "Library", + "id": "FHIRHelpers", + "version": "3.0.0", + "type": { + "coding": [ + { + "code": "logic-library" + } + ] + }, + "status": "draft", + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBGSElSSGVscGVycyB2ZXJzaW9uICczLjAuMCcNCg0KdXNpbmcgRkhJUiB2ZXJzaW9uICczLjAuMCcNCg0KZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZXJ2YWwocGVyaW9kIEZISVIuUGVyaW9kKToNCiAgICBJbnRlcnZhbFtwZXJpb2QuInN0YXJ0Ii52YWx1ZSwgcGVyaW9kLiJlbmQiLnZhbHVlXQ0KDQpkZWZpbmUgZnVuY3Rpb24gVG9RdWFudGl0eShxdWFudGl0eSBGSElSLlF1YW50aXR5KToNCiAgICBTeXN0ZW0uUXVhbnRpdHkgeyB2YWx1ZTogcXVhbnRpdHkudmFsdWUudmFsdWUsIHVuaXQ6IHF1YW50aXR5LnVuaXQudmFsdWUgfQ0KDQpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlcnZhbChyYW5nZSBGSElSLlJhbmdlKToNCiAgICBJbnRlcnZhbFtUb1F1YW50aXR5KHJhbmdlLmxvdyksIFRvUXVhbnRpdHkocmFuZ2UuaGlnaCldDQoNCmRlZmluZSBmdW5jdGlvbiBUb0NvZGUoY29kaW5nIEZISVIuQ29kaW5nKToNCiAgICBTeXN0ZW0uQ29kZSB7DQogICAgICBjb2RlOiBjb2RpbmcuY29kZS52YWx1ZSwNCiAgICAgIHN5c3RlbTogY29kaW5nLnN5c3RlbS52YWx1ZSwNCiAgICAgIHZlcnNpb246IGNvZGluZy52ZXJzaW9uLnZhbHVlLA0KICAgICAgZGlzcGxheTogY29kaW5nLmRpc3BsYXkudmFsdWUNCiAgICB9DQoNCmRlZmluZSBmdW5jdGlvbiBUb0NvbmNlcHQoY29uY2VwdCBGSElSLkNvZGVhYmxlQ29uY2VwdCk6DQogICAgU3lzdGVtLkNvbmNlcHQgew0KICAgICAgICBjb2RlczogY29uY2VwdC5jb2RpbmcgQyByZXR1cm4gVG9Db2RlKEMpLA0KICAgICAgICBkaXNwbGF5OiBjb25jZXB0LnRleHQudmFsdWUNCiAgICB9DQoNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLnV1aWQpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuVGVzdFNjcmlwdFJlcXVlc3RNZXRob2RDb2RlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlByb3ZlbmFuY2VFbnRpdHlSb2xlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlVuaXRzT2ZUaW1lKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkFkZHJlc3NUeXBlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkFsbGVyZ3lJbnRvbGVyYW5jZUNhdGVnb3J5KTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlNwZWNpbWVuU3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlJlc3RmdWxDYXBhYmlsaXR5TW9kZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5EZXRlY3RlZElzc3VlU2V2ZXJpdHkpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuSXNzdWVTZXZlcml0eSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5DYXJlVGVhbVN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5EYXRhRWxlbWVudFN0cmluZ2VuY3kpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuVmlzaW9uRXllcyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5FbmNvdW50ZXJTdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuU3RydWN0dXJlRGVmaW5pdGlvbktpbmQpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuUHVibGljYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQ29uc2VudERhdGFNZWFuaW5nKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlN0cnVjdHVyZU1hcFNvdXJjZUxpc3RNb2RlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlJlcXVlc3RTdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuUXVlc3Rpb25uYWlyZVJlc3BvbnNlU3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlNlYXJjaENvbXBhcmF0b3IpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQ2hhcmdlSXRlbVN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5BY3Rpb25QYXJ0aWNpcGFudFR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQWxsZXJneUludG9sZXJhbmNlVHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Eb2N1bWVudFJlbGF0aW9uc2hpcFR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQWxsZXJneUludG9sZXJhbmNlQ2xpbmljYWxTdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQ2FyZVBsYW5BY3Rpdml0eVN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5BY3Rpb25MaXN0KTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlBhcnRpY2lwYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuVGVzdFJlcG9ydFJlc3VsdCk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Db25jZXB0TWFwR3JvdXBVbm1hcHBlZE1vZGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvRGF0ZVRpbWUodmFsdWUgRkhJUi5pbnN0YW50KTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb0RhdGVUaW1lKHZhbHVlIEZISVIuZGF0ZVRpbWUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvRGF0ZVRpbWUodmFsdWUgRkhJUi5kYXRlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkRvY3VtZW50TW9kZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Bc3NlcnRpb25PcGVyYXRvclR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuRGF5c09mV2Vlayk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Jc3N1ZVR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQ29udGVudFR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuU3RydWN0dXJlTWFwQ29udGV4dFR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuRmFtaWx5SGlzdG9yeVN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9JbnRlZ2VyKHZhbHVlIEZISVIucG9zaXRpdmVJbnQpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQ2xpbmljYWxJbXByZXNzaW9uU3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkFzc2VydGlvblJlc3BvbnNlVHlwZXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuUmVxdWVzdEludGVudCk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5OYXJyYXRpdmVTdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuTWVhc21udFByaW5jaXBsZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Db25zZW50RXhjZXB0VHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5zdHJpbmcpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQWN0aW9uUmVxdWlyZWRCZWhhdmlvcik6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5FbmRwb2ludFN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5HdWlkZVBhZ2VLaW5kKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkd1aWRlRGVwZW5kZW5jeVR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuUmVzb3VyY2VWZXJzaW9uUG9saWN5KTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLk1lZGljYXRpb25SZXF1ZXN0U3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLk1lZGljYXRpb25BZG1pbmlzdHJhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5BY3Rpb25DYXJkaW5hbGl0eUJlaGF2aW9yKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLk1lZGljYXRpb25SZXF1ZXN0SW50ZW50KTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLk5hbWluZ1N5c3RlbUlkZW50aWZpZXJUeXBlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkltbXVuaXphdGlvblN0YXR1c0NvZGVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkFjY291bnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuTWVkaWNhdGlvbkRpc3BlbnNlU3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkNvbmZpZGVudGlhbGl0eUNsYXNzaWZpY2F0aW9uKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLklkZW50aWZpZXJVc2UpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuRGlnaXRhbE1lZGlhVHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5TdHJ1Y3R1cmVNYXBUYXJnZXRMaXN0TW9kZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5UZXN0UmVwb3J0UGFydGljaXBhbnRUeXBlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkJpbmRpbmdTdHJlbmd0aCk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5SZXF1ZXN0UHJpb3JpdHkpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuUGFydGljaXBhbnRSZXF1aXJlZCk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5EaXNjcmltaW5hdG9yVHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5YUGF0aFVzYWdlVHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5TdHJ1Y3R1cmVNYXBJbnB1dE1vZGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuSW5zdGFuY2VBdmFpbGFiaWxpdHkpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuaWQpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuTGlua2FnZVR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuUmVmZXJlbmNlSGFuZGxpbmdQb2xpY3kpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuTWVkaWNhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5GaWx0ZXJPcGVyYXRvcik6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5OYW1pbmdTeXN0ZW1UeXBlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlJlc2VhcmNoU3R1ZHlTdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuRXh0ZW5zaW9uQ29udGV4dCk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5GSElSRGVmaW5lZFR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQXVkaXRFdmVudE91dGNvbWUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQWN0aW9uUmVsYXRpb25zaGlwVHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Db25zdHJhaW50U2V2ZXJpdHkpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuRXZlbnRDYXBhYmlsaXR5TW9kZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Db250cmFjdFJlc291cmNlU3RhdHVzQ29kZXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuUmVzZWFyY2hTdWJqZWN0U3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlVESUVudHJ5VHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5EZXZpY2VNZXRyaWNDYXRlZ29yeSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5UZXN0UmVwb3J0QWN0aW9uUmVzdWx0KTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlN0cnVjdHVyZU1hcFRyYW5zZm9ybSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5SZXNwb25zZVR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvRGVjaW1hbCh2YWx1ZSBGSElSLmRlY2ltYWwpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQWdncmVnYXRpb25Nb2RlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkNhcGFiaWxpdHlTdGF0ZW1lbnRLaW5kKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkFsbGVyZ3lJbnRvbGVyYW5jZVZlcmlmaWNhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5FdmVudFRpbWluZyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Hb2FsU3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlNlYXJjaFBhcmFtVHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5TeXN0ZW1SZXN0ZnVsSW50ZXJhY3Rpb24pOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQWN0aW9uR3JvdXBpbmdCZWhhdmlvcik6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5TdHJ1Y3R1cmVNYXBNb2RlbE1vZGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuVGFza1N0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5BZHZlcnNlRXZlbnRDYXVzYWxpdHkpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuU3Vic2NyaXB0aW9uQ2hhbm5lbFR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuR3JhcGhDb21wYXJ0bWVudFJ1bGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQXNzZXJ0aW9uRGlyZWN0aW9uVHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5DYXJlUGxhbkludGVudCk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5TbGljaW5nUnVsZXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuRXhwbGFuYXRpb25PZkJlbmVmaXRTdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQ29uc2VudFN0YXRlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkFkdmVyc2VFdmVudENhdGVnb3J5KTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkxpbmtUeXBlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkFsbGVyZ3lJbnRvbGVyYW5jZUNyaXRpY2FsaXR5KTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLk1lZGljYXRpb25SZXF1ZXN0UHJpb3JpdHkpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQ29uY2VwdE1hcEVxdWl2YWxlbmNlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkZISVJBbGxUeXBlcyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Qcm9wZXJ0eVJlcHJlc2VudGF0aW9uKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkF1ZGl0RXZlbnRBY3Rpb24pOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuVHJpZ2dlclR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuU2VhcmNoTW9kaWZpZXJDb2RlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkNvbXBvc2l0aW9uU3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkFwcG9pbnRtZW50U3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLk1lc3NhZ2VTaWduaWZpY2FuY2VDYXRlZ29yeSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5FdmVudFN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5PcGVyYXRpb25QYXJhbWV0ZXJVc2UpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuTGlzdE1vZGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQWN0aW9uQ29uZGl0aW9uS2luZCk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5PYnNlcnZhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5xdWFsaXR5VHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5BZG1pbmlzdHJhdGl2ZUdlbmRlcik6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5SZXNvdXJjZVR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuUXVlc3Rpb25uYWlyZUl0ZW1UeXBlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb0Jvb2xlYW4odmFsdWUgRkhJUi5ib29sZWFuKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlN0cnVjdHVyZU1hcEdyb3VwVHlwZU1vZGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuRGV2aWNlTWV0cmljQ2FsaWJyYXRpb25UeXBlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLmNvZGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuU3VwcGx5UmVxdWVzdFN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5BY3Rpb25TZWxlY3Rpb25CZWhhdmlvcik6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5FbmNvdW50ZXJMb2NhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5TdXBwbHlEZWxpdmVyeVN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5EaWFnbm9zdGljUmVwb3J0U3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkZsYWdTdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQ2FyZVBsYW5TdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQ29uZGl0aW9uQ2xpbmljYWxTdGF0dXNDb2Rlcyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5MaXN0U3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb2Jhc2U2NEJpbmFyeSh2YWx1ZSBGSElSLmJhc2U2NEJpbmFyeSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5EZXZpY2VVc2VTdGF0ZW1lbnRTdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQXVkaXRFdmVudEFnZW50TmV0d29ya1R5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQWRkcmVzc1VzZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Db25kaXRpb25hbERlbGV0ZVN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Db250YWN0UG9pbnRVc2UpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuRGV2aWNlTWV0cmljT3BlcmF0aW9uYWxTdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuTnV0cml0aW9uT3JkZXJTdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIudXJpKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkNvbnRyaWJ1dG9yVHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5SZWZlcmVuY2VWZXJzaW9uUnVsZXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuVXNlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLklkZW50aXR5QXNzdXJhbmNlTGV2ZWwpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuTWVhc3VyZVJlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5EZXZpY2VNZXRyaWNDb2xvcik6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5TZWFyY2hFbnRyeU1vZGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvVGltZSh2YWx1ZSBGSElSLnRpbWUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQ29uZGl0aW9uYWxSZWFkU3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkNvbmRpdGlvblZlcmlmaWNhdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5BbGxlcmd5SW50b2xlcmFuY2VTZXZlcml0eSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5GaW5hbmNpYWxSZXNvdXJjZVN0YXR1c0NvZGVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLk9wZXJhdGlvbktpbmQpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuT2JzZXJ2YXRpb25SZWxhdGlvbnNoaXBUeXBlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb0ludGVnZXIodmFsdWUgRkhJUi51bnNpZ25lZEludCk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5OYW1lVXNlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlN1YnNjcmlwdGlvblN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Eb2N1bWVudFJlZmVyZW5jZVN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Mb2NhdGlvbk1vZGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvSW50ZWdlcih2YWx1ZSBGSElSLmludGVnZXIpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIucmVwb3NpdG9yeVR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuTG9jYXRpb25TdGF0dXMpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuRkhJUlN1YnN0YW5jZVN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Vbmtub3duQ29udGVudENvZGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuTm90ZVR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuVGVzdFJlcG9ydFN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5IVFRQVmVyYik6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Db2RlU3lzdGVtQ29udGVudE1vZGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuRXBpc29kZU9mQ2FyZVN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5SZW1pdHRhbmNlT3V0Y29tZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5GSElSRGV2aWNlU3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkNvbnRhY3RQb2ludFN5c3RlbSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5TbG90U3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlByb3BlcnR5VHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5tYXJrZG93bik6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5UeXBlRGVyaXZhdGlvblJ1bGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuTWVkaWNhdGlvblN0YXRlbWVudFN0YXR1cyk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5HdWlkYW5jZVJlc3BvbnNlU3RhdHVzKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlF1YW50aXR5Q29tcGFyYXRvcik6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5SZWxhdGVkQXJ0aWZhY3RUeXBlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLm9pZCk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5NZWFzdXJlUmVwb3J0VHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5BY3Rpb25QcmVjaGVja0JlaGF2aW9yKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlNhbXBsZWREYXRhRGF0YVR5cGUpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuTWVkaWNhdGlvblN0YXRlbWVudFRha2VuKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkNvbXBhcnRtZW50VHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5Db21wb3NpdGlvbkF0dGVzdGF0aW9uTW9kZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5EZXZpY2VNZXRyaWNDYWxpYnJhdGlvblN0YXRlKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkdyb3VwVHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5UeXBlUmVzdGZ1bEludGVyYWN0aW9uKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLkNvZGVTeXN0ZW1IaWVyYXJjaHlNZWFuaW5nKTogdmFsdWUudmFsdWUNCmRlZmluZSBmdW5jdGlvbiBUb1N0cmluZyh2YWx1ZSBGSElSLlZpc2lvbkJhc2UpOiB2YWx1ZS52YWx1ZQ0KZGVmaW5lIGZ1bmN0aW9uIFRvU3RyaW5nKHZhbHVlIEZISVIuQnVuZGxlVHlwZSk6IHZhbHVlLnZhbHVlDQpkZWZpbmUgZnVuY3Rpb24gVG9TdHJpbmcodmFsdWUgRkhJUi5TeXN0ZW1WZXJzaW9uUHJvY2Vzc2luZ01vZGUpOiB2YWx1ZS52YWx1ZQ==" + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/general-patient.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/general-patient.json new file mode 100644 index 00000000000..3a8e647c005 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/general-patient.json @@ -0,0 +1,114 @@ +{ + "resourceType": "Patient", + "id": "Patient-12214", + "meta": { + "versionId": "1", + "lastUpdated": "2017-07-17T16:34:10.814+00:00" + }, + "text": { + "status": "generated", + "div": "
2 N GERIATRIC Jr
Identifier7f3672feb3b54789953e012d8aef5246
Address202 Burlington Rd.
Bedford MA
Date of birth07 May 1946
" + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/us-core-race", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/Race", + "code": "2106-3", + "display": "White" + } + ] + } + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/us-core-ethnicity", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/Ethnicity", + "code": "2186-5", + "display": "Not Hispanic or Latino" + } + ] + } + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/us-core-religion", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/ReligiousAffiliation", + "code": "1007", + "display": "Atheism" + } + ] + } + } + ], + "identifier": [ + { + "use": "official", + "type": { + "coding": [ + { + "system": "http://hl7.org/fhir/identifier-type", + "code": "SB", + "display": "Social Beneficiary Identifier" + } + ], + "text": "Michigan Common Key Service Identifier" + }, + "system": "http://mihin.org/fhir/cks", + "value": "7f3672feb3b54789953e012d8aef5246" + } + ], + "active": false, + "name": [ + { + "family": "N Geriatric", + "given": [ + "2" + ], + "suffix": [ + "Jr" + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "586-555-7576", + "use": "home" + }, + { + "system": "phone", + "value": "586-555-0297", + "use": "work" + }, + { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/us-core-direct", + "valueBoolean": true + } + ], + "system": "email", + "value": "2.N.Geriatric@direct.mihintest.org", + "use": "home" + } + ], + "gender": "male", + "birthDate": "1946-05-07", + "address": [ + { + "line": [ + "202 Burlington Rd." + ], + "city": "Bedford", + "state": "MA", + "postalCode": "01730" + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/general-practitioner.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/general-practitioner.json new file mode 100644 index 00000000000..2665f30cb3a --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/general-practitioner.json @@ -0,0 +1,173 @@ +{ + "resourceType": "Practitioner", + "id": "Practitioner-12208", + "meta": { + "versionId": "1", + "lastUpdated": "2017-07-17T16:34:10.814+00:00" + }, + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/us-core-race", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/Race", + "code": "2056-0", + "display": "Black" + } + ] + } + }, + { + "url": "http://hl7.org/fhir/StructureDefinition/us-core-ethnicity", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://hl7.org/fhir/v3/Ethnicity", + "code": "2186-5", + "display": "Not Hispanic or Latino" + } + ] + } + }, + { + "url": "http://gov.onc.fhir.extension.taxonomy", + "valueCodeableConcept": { + "coding": [ + { + "system": "http://org.nucc.taxonomy", + "code": "208D00000X", + "display": "General Practice" + } + ] + } + }, + { + "url": "http://org.mihin.fhir.extension.electronic-service", + "valueReference": { + "reference": "ElectronicService/ElectronicService-2415", + "display": "Jay.M.Sawyer@direct.mihintest.org" + } + } + ], + "identifier": [ + { + "use": "official", + "type": { + "coding": [ + { + "system": "http://hl7.org/fhir/identifier-type", + "code": "SB", + "display": "Social Beneficiary Identifier" + } + ], + "text": "US Social Security Number" + }, + "system": "http://hl7.org/fhir/sid/us-ssn", + "value": "000012208" + }, + { + "use": "official", + "type": { + "coding": [ + { + "system": "http://hl7.org/fhir/v2/0203", + "code": "PRN", + "display": "Provider number" + } + ], + "text": "US National Provider Identifier" + }, + "system": "http://hl7.org/fhir/sid/us-npi", + "value": "9999912208" + }, + { + "use": "official", + "type": { + "coding": [ + { + "system": "http://hl7.org/fhir/identifier-type", + "code": "SB", + "display": "Social Beneficiary Identifier" + } + ], + "text": "Michigan Common Key Service Identifier" + }, + "system": "http://mihin.org/fhir/cks", + "value": "c6cc1bbaf5ea41c5a0d267e3a655def1" + } + ], + "name": [ + { + "family": "Sawyer", + "given": [ + "Jay", + "McCann" + ], + "suffix": [ + "MD" + ] + } + ], + "telecom": [ + { + "system": "phone", + "value": "989-555-8443", + "use": "home" + }, + { + "system": "phone", + "value": "989-555-5764", + "use": "work" + } + ], + "address": [ + { + "line": [ + "77 S Pine Place" + ], + "city": "Beaverton", + "state": "MI", + "postalCode": "48612" + } + ], + "gender": "male", + "birthDate": "1970-08-07", + "qualification": [ + { + "identifier": [ + { + "use": "official", + "type": { + "coding": [ + { + "system": "http://hl7.org/fhir/v2/0203", + "code": "MD", + "display": "Medical License number" + } + ], + "text": "Michigan Medical License" + }, + "system": "http://michigan.gov/fhir/medical-license", + "value": "LARA-12208", + "assigner": { + "display": "State of Michigan" + } + } + ], + "code": { + "coding": [ + { + "system": "http://michigan.gov/lara/license-type", + "code": "4305", + "display": "Medical Doctor" + } + ] + }, + "issuer": { + "reference": "Organization/Organization-2000", + "display": "Michigan Department of Licensing and Regulatory Affairs" + } + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/library-col.elm.xml b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/library-col.elm.xml new file mode 100644 index 00000000000..e078fa6dc57 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/library-col.elm.xml @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-col.xml b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-col.xml new file mode 100644 index 00000000000..4d538f19fbf --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-col.xml @@ -0,0 +1,138 @@ + + + + + +
+ Cohort definition for Colorectal Cancer Screening. +
+
+ + + + + + + + <status value="active"/> + <experimental value="true"/> + <description value="Colorectal Cancer Screening. Cohort Definition"/> + <topic> + <coding> + <system value="http://hl7.org/fhir/c80-doc-typecodes"/> + <code value="57024-2"/> + </coding> + </topic> + <library> + <reference value="Library/col-logic"/> + </library> + <scoring value="cohort"/> + <group> + <identifier> + <value value="in-demographic"/> + </identifier> + <population> + <type value="initial-population"/> + <identifier> + <value value="in-demographic"/> + </identifier> + <criteria value="In Demographic"/> + </population> + </group> + <group> + <identifier> + <value value="history-of-colorectal-cancer"/> + </identifier> + <population> + <type value="initial-population"/> + <identifier> + <value value="history-of-colorectal-cancer"/> + </identifier> + <criteria value="Hx Colorectal Cancer"/> + </population> + </group> + <group> + <identifier> + <value value="history-of-total-colectomy"/> + </identifier> + <population> + <type value="initial-population"/> + <identifier> + <value value="history-of-total-colectomy"/> + </identifier> + <criteria value="Hx Total Colectomy"/> + </population> + </group> + <group> + <identifier> + <value value="colonoscopy-performed"/> + </identifier> + <population> + <type value="initial-population"/> + <identifier> + <value value="colonoscopy-performed"/> + </identifier> + <criteria value="Colonoscopy Performed"/> + </population> + </group> + <group> + <identifier> + <value value="colonoscopy-results"/> + </identifier> + <population> + <type value="initial-population"/> + <identifier> + <value value="colonoscopy-results"/> + </identifier> + <criteria value="Colonoscopy Results"/> + </population> + </group> + <group> + <identifier> + <value value="sigmoidoscopy-procedure"/> + </identifier> + <population> + <type value="initial-population"/> + <identifier> + <value value="sigmoidoscopy-procedure"/> + </identifier> + <criteria value="Sigmoidoscopy Procedure"/> + </population> + </group> + <group> + <identifier> + <value value="sigmoidoscopy-observation"/> + </identifier> + <population> + <type value="initial-population"/> + <identifier> + <value value="sigmoidoscopy-observation"/> + </identifier> + <criteria value="Sigmoidoscopy Observation"/> + </population> + </group> + <group> + <identifier> + <value value="fobt-procedure"/> + </identifier> + <population> + <type value="initial-population"/> + <identifier> + <value value="fobt-procedure"/> + </identifier> + <criteria value="FOBT Procedure"/> + </population> + </group> + <group> + <identifier> + <value value="fobt-observation"/> + </identifier> + <population> + <type value="initial-population"/> + <identifier> + <value value="fobt-observation"/> + </identifier> + <criteria value="FOBT Observation"/> + </population> + </group> +</Measure> diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-condition.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-condition.json new file mode 100644 index 00000000000..6cbca50c2b5 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-condition.json @@ -0,0 +1,49 @@ +{ + "resourceType": "Condition", + "id": "Condition-13", + "meta": { + "versionId": "1", + "lastUpdated": "2017-09-09T21:52:17.035-06:00" + }, + "extension": [ + { + "url": "http://mihin.org/fhir/templateId", + "valueString": "2.16.840.1.113883.10.20.22.4.3" + }, + { + "url": "http://mihin.org/fhir/templateId", + "valueString": "2.16.840.1.113883.10.20.24.3.137" + } + ], + "clinicalStatus": "active", + "verificationStatus": "confirmed", + "category": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/condition-category", + "code": "diagnosis", + "display": "Diagnosis" + } + ], + "text": "This is a judgment made by a healthcare provider that the patient has a particular disease or condition" + } + ], + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "363414004" + } + ], + "text": "Diagnosis: Malignant Neoplasm Of Colon" + }, + "subject": { + "reference": "Patient/Patient-12214", + "display": "2 N Geriatric Jr" + }, + "asserter": { + "reference": "Practitioner/Practitioner-12208", + "display": "Jay McCann Sawyer MD" + } +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-library.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-library.json new file mode 100644 index 00000000000..f5ccd262162 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-library.json @@ -0,0 +1,22 @@ +{ + "resourceType": "Library", + "id": "col-logic", + "meta": { + "versionId": "1", + "lastUpdated": "2017-09-09T21:25:51.679-06:00" + }, + "status": "draft", + "type": { + "coding": [ + { + "code": "logic-library" + } + ] + }, + "content": [ + { + "contentType": "application/elm+xml", + "data": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4NCjxsaWJyYXJ5IHhtbG5zPSJ1cm46aGw3LW9yZzplbG06cjEiIHhtbG5zOnQ9InVybjpobDctb3JnOmVsbS10eXBlczpyMSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeG1sbnM6eHNkPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6Zmhpcj0iaHR0cDovL2hsNy5vcmcvZmhpciIgeG1sbnM6YT0idXJuOmhsNy1vcmc6Y3FsLWFubm90YXRpb25zOnIxIj4NCiAgIDxpZGVudGlmaWVyIGlkPSJDT0wiIHZlcnNpb249IjEiLz4NCiAgIDxzY2hlbWFJZGVudGlmaWVyIGlkPSJ1cm46aGw3LW9yZzplbG0iIHZlcnNpb249InIxIi8+DQogICA8dXNpbmdzPg0KICAgICAgPGRlZiBsb2NhbElkZW50aWZpZXI9IlN5c3RlbSIgdXJpPSJ1cm46aGw3LW9yZzplbG0tdHlwZXM6cjEiLz4NCiAgICAgIDxkZWYgbG9jYWxJZGVudGlmaWVyPSJGSElSIiB1cmk9Imh0dHA6Ly9obDcub3JnL2ZoaXIiIHZlcnNpb249IjEuNiIvPg0KICAgPC91c2luZ3M+DQogICA8cGFyYW1ldGVycz4NCiAgICAgIDxkZWYgbmFtZT0iTWVhc3VyZW1lbnRQZXJpb2QiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPHBhcmFtZXRlclR5cGVTcGVjaWZpZXIgeHNpOnR5cGU9IkludGVydmFsVHlwZVNwZWNpZmllciI+DQogICAgICAgICAgICA8cG9pbnRUeXBlIG5hbWU9InQ6RGF0ZVRpbWUiIHhzaTp0eXBlPSJOYW1lZFR5cGVTcGVjaWZpZXIiLz4NCiAgICAgICAgIDwvcGFyYW1ldGVyVHlwZVNwZWNpZmllcj4NCiAgICAgIDwvZGVmPg0KICAgPC9wYXJhbWV0ZXJzPg0KICAgPGNvZGVTeXN0ZW1zPg0KICAgICAgPGRlZiBuYW1lPSJDUFQiIGlkPSJ1cm46b2lkOjIuMTYuODQwLjEuMTEzODgzLjYuMTIiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4NCiAgICAgIDxkZWYgbmFtZT0iU05PTUVELUNUIiBpZD0idXJuOm9pZDoyLjE2Ljg0MC4xLjExMzg4My42Ljk2IiBhY2Nlc3NMZXZlbD0iUHVibGljIi8+DQogICAgICA8ZGVmIG5hbWU9IkxPSU5DIiBpZD0iaHR0cDovL2xvaW5jLm9yZyIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPg0KICAgPC9jb2RlU3lzdGVtcz4NCiAgIDx2YWx1ZVNldHM+DQogICAgICA8ZGVmIG5hbWU9Ik1hbGlnbmFudCBOZW9wbGFzbSBvZiBDb2xvbiIgaWQ9IjIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjExLjEwMDEiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4NCiAgICAgIDxkZWYgbmFtZT0iVG90YWwgQ29sZWN0b215IiBpZD0iMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxOSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPg0KICAgICAgPGRlZiBuYW1lPSJDb2xvbm9zY29weSIgaWQ9IjIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTA4LjEyLjEwMjAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4NCiAgICAgIDxkZWYgbmFtZT0iRmxleGlibGUgU2lnbW9pZG9zY29weSIgaWQ9IjIuMTYuODQwLjEuMTEzODgzLjMuNDY0LjEwMDMuMTk4LjEyLjEwMTAiIGFjY2Vzc0xldmVsPSJQdWJsaWMiLz4NCiAgICAgIDxkZWYgbmFtZT0iRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIiBpZD0iMi4xNi44NDAuMS4xMTM4ODMuMy40NjQuMTAwMy4xOTguMTIuMTAxMSIgYWNjZXNzTGV2ZWw9IlB1YmxpYyIvPg0KICAgPC92YWx1ZVNldHM+DQogICA8c3RhdGVtZW50cz4NCiAgICAgIDxkZWYgbmFtZT0iUGF0aWVudCIgY29udGV4dD0iUGF0aWVudCI+DQogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iU2luZ2xldG9uRnJvbSI+DQogICAgICAgICAgICA8b3BlcmFuZCBkYXRhVHlwZT0iZmhpcjpQYXRpZW50IiB4c2k6dHlwZT0iUmV0cmlldmUiLz4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBuYW1lPSJMb29rYmFjayBJbnRlcnZhbCBPbmUgWWVhciIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+DQogICAgICAgICAgICA8bG93IHhzaTp0eXBlPSJTdWJ0cmFjdCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iU3RhcnQiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iTWVhc3VyZW1lbnRQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlPSIxIiB1bml0PSJ5ZWFycyIgeHNpOnR5cGU9IlF1YW50aXR5Ii8+DQogICAgICAgICAgICA8L2xvdz4NCiAgICAgICAgICAgIDxoaWdoIHhzaTp0eXBlPSJFbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iTWVhc3VyZW1lbnRQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgIDwvaGlnaD4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBuYW1lPSJMb29rYmFjayBJbnRlcnZhbCBGaXZlIFllYXJzIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxleHByZXNzaW9uIGxvd0Nsb3NlZD0idHJ1ZSIgaGlnaENsb3NlZD0idHJ1ZSIgeHNpOnR5cGU9IkludGVydmFsIj4NCiAgICAgICAgICAgIDxsb3cgeHNpOnR5cGU9IlN1YnRyYWN0Ij4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJTdGFydCI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJNZWFzdXJlbWVudFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWU9IjUiIHVuaXQ9InllYXJzIiB4c2k6dHlwZT0iUXVhbnRpdHkiLz4NCiAgICAgICAgICAgIDwvbG93Pg0KICAgICAgICAgICAgPGhpZ2ggeHNpOnR5cGU9IkVuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJNZWFzdXJlbWVudFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgPC9oaWdoPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIG5hbWU9Ikxvb2tiYWNrIEludGVydmFsIFRlbiBZZWFycyIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8ZXhwcmVzc2lvbiBsb3dDbG9zZWQ9InRydWUiIGhpZ2hDbG9zZWQ9InRydWUiIHhzaTp0eXBlPSJJbnRlcnZhbCI+DQogICAgICAgICAgICA8bG93IHhzaTp0eXBlPSJTdWJ0cmFjdCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iU3RhcnQiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iTWVhc3VyZW1lbnRQZXJpb2QiIHhzaTp0eXBlPSJQYXJhbWV0ZXJSZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlPSIxMCIgdW5pdD0ieWVhcnMiIHhzaTp0eXBlPSJRdWFudGl0eSIvPg0KICAgICAgICAgICAgPC9sb3c+DQogICAgICAgICAgICA8aGlnaCB4c2k6dHlwZT0iRW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9Ik1lYXN1cmVtZW50UGVyaW9kIiB4c2k6dHlwZT0iUGFyYW1ldGVyUmVmIi8+DQogICAgICAgICAgICA8L2hpZ2g+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbmFtZT0iSW4gRGVtb2dyYXBoaWMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGV4cHJlc3Npb24geHNpOnR5cGU9IkdyZWF0ZXJPckVxdWFsIj4NCiAgICAgICAgICAgIDxvcGVyYW5kIHByZWNpc2lvbj0iWWVhciIgeHNpOnR5cGU9IkNhbGN1bGF0ZUFnZUF0Ij4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9ImJpcnRoRGF0ZS52YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgIDxzb3VyY2UgbmFtZT0iUGF0aWVudCIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJTdGFydCI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJNZWFzdXJlbWVudFBlcmlvZCIgeHNpOnR5cGU9IlBhcmFtZXRlclJlZiIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OkludGVnZXIiIHZhbHVlPSI1MCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBuYW1lPSJIeCBDb2xvcmVjdGFsIENhbmNlciIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBhbGlhcz0iQyI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBkYXRhVHlwZT0iZmhpcjpDb25kaXRpb24iIGNvZGVQcm9wZXJ0eT0iY29kZSIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJNYWxpZ25hbnQgTmVvcGxhc20gb2YgQ29sb24iIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+DQogICAgICAgICAgICAgICAgICAgICA8c291cmNlIHBhdGg9ImNsaW5pY2FsU3RhdHVzIiBzY29wZT0iQyIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iYWN0aXZlIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPg0KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBwYXRoPSJ2ZXJpZmljYXRpb25TdGF0dXMiIHNjb3BlPSJDIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb25maXJtZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIG5hbWU9Ikh4IFRvdGFsIENvbGVjdG9teSIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBhbGlhcz0iVCI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJUb3RhbCBDb2xlY3RvbXkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPg0KICAgICAgICAgICAgICAgICAgPHNvdXJjZSBwYXRoPSJzdGF0dXMiIHNjb3BlPSJUIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIG5hbWU9IkNvbG9ub3Njb3B5IFBlcmZvcm1lZCIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBhbGlhcz0iQyI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBkYXRhVHlwZT0iZmhpcjpQcm9jZWR1cmUiIGNvZGVQcm9wZXJ0eT0iY29kZSIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJDb2xvbm9zY29weSIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+DQogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICAgICAgICA8L3NvdXJjZT4NCiAgICAgICAgICAgIDx3aGVyZSB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJFcXVhbCI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgcGF0aD0ic3RhdHVzIiBzY29wZT0iQyIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iY29tcGxldGVkIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkluIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPg0KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBwYXRoPSJlbmQiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+DQogICAgICAgICAgICAgICAgICAgICAgICA8c291cmNlIHBhdGg9InBlcmZvcm1lZFBlcmlvZCIgc2NvcGU9IkMiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJMb29rYmFjayBJbnRlcnZhbCBUZW4gWWVhcnMiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIG5hbWU9IkNvbG9ub3Njb3B5IFJlc3VsdHMiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGV4cHJlc3Npb24geHNpOnR5cGU9IlF1ZXJ5Ij4NCiAgICAgICAgICAgIDxzb3VyY2UgYWxpYXM9IkMiPg0KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gZGF0YVR5cGU9ImZoaXI6T2JzZXJ2YXRpb24iIGNvZGVQcm9wZXJ0eT0iY29kZSIgeHNpOnR5cGU9IlJldHJpZXZlIj4NCiAgICAgICAgICAgICAgICAgIDxjb2RlcyBuYW1lPSJDb2xvbm9zY29weSIgeHNpOnR5cGU9IlZhbHVlU2V0UmVmIi8+DQogICAgICAgICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICAgICAgICA8L3NvdXJjZT4NCiAgICAgICAgICAgIDx3aGVyZSB4c2k6dHlwZT0iQW5kIj4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJFcXVhbCI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgcGF0aD0ic3RhdHVzIiBzY29wZT0iQyIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCB2YWx1ZVR5cGU9InQ6U3RyaW5nIiB2YWx1ZT0iZmluYWwiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+DQogICAgICAgICAgICAgICAgICAgICA8c291cmNlIHBhdGg9ImVmZmVjdGl2ZURhdGVUaW1lIiBzY29wZT0iQyIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBuYW1lPSJMb29rYmFjayBJbnRlcnZhbCBUZW4gWWVhcnMiIHhzaTp0eXBlPSJFeHByZXNzaW9uUmVmIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICA8L3doZXJlPg0KICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgPC9kZWY+DQogICAgICA8ZGVmIG5hbWU9IlNpZ21vaWRvc2NvcHkgUHJvY2VkdXJlIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxleHByZXNzaW9uIHhzaTp0eXBlPSJRdWVyeSI+DQogICAgICAgICAgICA8c291cmNlIGFsaWFzPSJTIj4NCiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGRhdGFUeXBlPSJmaGlyOlByb2NlZHVyZSIgY29kZVByb3BlcnR5PSJjb2RlIiB4c2k6dHlwZT0iUmV0cmlldmUiPg0KICAgICAgICAgICAgICAgICAgPGNvZGVzIG5hbWU9IkZsZXhpYmxlIFNpZ21vaWRvc2NvcHkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+DQogICAgICAgICAgICAgICAgICAgICA8c291cmNlIHBhdGg9InN0YXR1cyIgc2NvcGU9IlMiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJJbiI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgcGF0aD0iZW5kIiB4c2k6dHlwZT0iUHJvcGVydHkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBwYXRoPSJwZXJmb3JtZWRQZXJpb2QiIHNjb3BlPSJTIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iTG9va2JhY2sgSW50ZXJ2YWwgRml2ZSBZZWFycyIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvd2hlcmU+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbmFtZT0iU2lnbW9pZG9zY29weSBPYnNlcnZhdGlvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBhbGlhcz0iTyI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBkYXRhVHlwZT0iZmhpcjpPYnNlcnZhdGlvbiIgY29kZVByb3BlcnR5PSJjb2RlIiB4c2k6dHlwZT0iUmV0cmlldmUiPg0KICAgICAgICAgICAgICAgICAgPGNvZGVzIG5hbWU9IkZsZXhpYmxlIFNpZ21vaWRvc2NvcHkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+DQogICAgICAgICAgICAgICAgICAgICA8c291cmNlIHBhdGg9InN0YXR1cyIgc2NvcGU9Ik8iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImZpbmFsIiB4c2k6dHlwZT0iTGl0ZXJhbCIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkluIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPg0KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBwYXRoPSJlZmZlY3RpdmVEYXRlVGltZSIgc2NvcGU9Ik8iIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iTG9va2JhY2sgSW50ZXJ2YWwgRml2ZSBZZWFycyIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvd2hlcmU+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgICAgIDxkZWYgbmFtZT0iRk9CVCBQcm9jZWR1cmUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGV4cHJlc3Npb24geHNpOnR5cGU9IlF1ZXJ5Ij4NCiAgICAgICAgICAgIDxzb3VyY2UgYWxpYXM9IkYiPg0KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+DQogICAgICAgICAgICAgICAgICA8Y29kZXMgbmFtZT0iRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4NCiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgPHdoZXJlIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPg0KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBwYXRoPSJzdGF0dXMiIHNjb3BlPSJGIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJjb21wbGV0ZWQiIHhzaTp0eXBlPSJMaXRlcmFsIi8+DQogICAgICAgICAgICAgICA8L29wZXJhbmQ+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iSW4iPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+DQogICAgICAgICAgICAgICAgICAgICA8c291cmNlIHBhdGg9ImVuZCIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgcGF0aD0icGVyZm9ybWVkUGVyaW9kIiBzY29wZT0iRiIgeHNpOnR5cGU9IlByb3BlcnR5Ii8+DQogICAgICAgICAgICAgICAgICAgICA8L3NvdXJjZT4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9Ikxvb2tiYWNrIEludGVydmFsIE9uZSBZZWFyIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBuYW1lPSJGT0JUIE9ic2VydmF0aW9uIiBjb250ZXh0PSJQYXRpZW50IiBhY2Nlc3NMZXZlbD0iUHVibGljIj4NCiAgICAgICAgIDxleHByZXNzaW9uIHhzaTp0eXBlPSJRdWVyeSI+DQogICAgICAgICAgICA8c291cmNlIGFsaWFzPSJPIj4NCiAgICAgICAgICAgICAgIDxleHByZXNzaW9uIGRhdGFUeXBlPSJmaGlyOk9ic2VydmF0aW9uIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+DQogICAgICAgICAgICAgICAgICA8Y29kZXMgbmFtZT0iRmVjYWwgT2NjdWx0IEJsb29kIFRlc3QgKEZPQlQpIiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4NCiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgPHdoZXJlIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPg0KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBwYXRoPSJzdGF0dXMiIHNjb3BlPSJPIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJmaW5hbCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJJbiI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgcGF0aD0iZWZmZWN0aXZlRGF0ZVRpbWUiIHNjb3BlPSJPIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9Ikxvb2tiYWNrIEludGVydmFsIE9uZSBZZWFyIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBuYW1lPSJDb2xvbm9zY29weSBQcm9jZWR1cmUiIGNvbnRleHQ9IlBhdGllbnQiIGFjY2Vzc0xldmVsPSJQdWJsaWMiPg0KICAgICAgICAgPGV4cHJlc3Npb24geHNpOnR5cGU9IlF1ZXJ5Ij4NCiAgICAgICAgICAgIDxzb3VyY2UgYWxpYXM9IkMiPg0KICAgICAgICAgICAgICAgPGV4cHJlc3Npb24gZGF0YVR5cGU9ImZoaXI6UHJvY2VkdXJlIiBjb2RlUHJvcGVydHk9ImNvZGUiIHhzaTp0eXBlPSJSZXRyaWV2ZSI+DQogICAgICAgICAgICAgICAgICA8Y29kZXMgbmFtZT0iQ29sb25vc2NvcHkiIHhzaTp0eXBlPSJWYWx1ZVNldFJlZiIvPg0KICAgICAgICAgICAgICAgPC9leHByZXNzaW9uPg0KICAgICAgICAgICAgPC9zb3VyY2U+DQogICAgICAgICAgICA8d2hlcmUgeHNpOnR5cGU9IkFuZCI+DQogICAgICAgICAgICAgICA8b3BlcmFuZCB4c2k6dHlwZT0iRXF1YWwiPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgcGF0aD0idmFsdWUiIHhzaTp0eXBlPSJQcm9wZXJ0eSI+DQogICAgICAgICAgICAgICAgICAgICA8c291cmNlIHBhdGg9InN0YXR1cyIgc2NvcGU9IkMiIHhzaTp0eXBlPSJQcm9wZXJ0eSIvPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgdmFsdWVUeXBlPSJ0OlN0cmluZyIgdmFsdWU9ImNvbXBsZXRlZCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJJbiI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgcGF0aD0iZW5kIiB4c2k6dHlwZT0iUHJvcGVydHkiPg0KICAgICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBwYXRoPSJwZXJmb3JtZWRQZXJpb2QiIHNjb3BlPSJDIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgICAgICAgPG9wZXJhbmQgbmFtZT0iTG9va2JhY2sgSW50ZXJ2YWwgVGVuIFllYXJzIiB4c2k6dHlwZT0iRXhwcmVzc2lvblJlZiIvPg0KICAgICAgICAgICAgICAgPC9vcGVyYW5kPg0KICAgICAgICAgICAgPC93aGVyZT4NCiAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgIDwvZGVmPg0KICAgICAgPGRlZiBuYW1lPSJDb2xvbm9zY29weSBPYnNlcnZhdGlvbiIgY29udGV4dD0iUGF0aWVudCIgYWNjZXNzTGV2ZWw9IlB1YmxpYyI+DQogICAgICAgICA8ZXhwcmVzc2lvbiB4c2k6dHlwZT0iUXVlcnkiPg0KICAgICAgICAgICAgPHNvdXJjZSBhbGlhcz0iTyI+DQogICAgICAgICAgICAgICA8ZXhwcmVzc2lvbiBkYXRhVHlwZT0iZmhpcjpPYnNlcnZhdGlvbiIgY29kZVByb3BlcnR5PSJjb2RlIiB4c2k6dHlwZT0iUmV0cmlldmUiPg0KICAgICAgICAgICAgICAgICAgPGNvZGVzIG5hbWU9IkNvbG9ub3Njb3B5IiB4c2k6dHlwZT0iVmFsdWVTZXRSZWYiLz4NCiAgICAgICAgICAgICAgIDwvZXhwcmVzc2lvbj4NCiAgICAgICAgICAgIDwvc291cmNlPg0KICAgICAgICAgICAgPHdoZXJlIHhzaTp0eXBlPSJBbmQiPg0KICAgICAgICAgICAgICAgPG9wZXJhbmQgeHNpOnR5cGU9IkVxdWFsIj4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHBhdGg9InZhbHVlIiB4c2k6dHlwZT0iUHJvcGVydHkiPg0KICAgICAgICAgICAgICAgICAgICAgPHNvdXJjZSBwYXRoPSJzdGF0dXMiIHNjb3BlPSJPIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIHZhbHVlVHlwZT0idDpTdHJpbmciIHZhbHVlPSJmaW5hbCIgeHNpOnR5cGU9IkxpdGVyYWwiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgIDxvcGVyYW5kIHhzaTp0eXBlPSJJbiI+DQogICAgICAgICAgICAgICAgICA8b3BlcmFuZCBwYXRoPSJ2YWx1ZSIgeHNpOnR5cGU9IlByb3BlcnR5Ij4NCiAgICAgICAgICAgICAgICAgICAgIDxzb3VyY2UgcGF0aD0iZWZmZWN0aXZlRGF0ZVRpbWUiIHNjb3BlPSJPIiB4c2k6dHlwZT0iUHJvcGVydHkiLz4NCiAgICAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgICAgICAgIDxvcGVyYW5kIG5hbWU9Ikxvb2tiYWNrIEludGVydmFsIFRlbiBZZWFycyIgeHNpOnR5cGU9IkV4cHJlc3Npb25SZWYiLz4NCiAgICAgICAgICAgICAgIDwvb3BlcmFuZD4NCiAgICAgICAgICAgIDwvd2hlcmU+DQogICAgICAgICA8L2V4cHJlc3Npb24+DQogICAgICA8L2RlZj4NCiAgIDwvc3RhdGVtZW50cz4NCjwvbGlicmFyeT4NCg==" + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-measure.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-measure.json new file mode 100644 index 00000000000..ecb7851c71e --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-measure.json @@ -0,0 +1,158 @@ +{ + "resourceType": "Measure", + "id": "col", + "meta": { + "versionId": "1", + "lastUpdated": "2017-09-09T21:26:03.890-06:00" + }, + "text": { + "status": "additional", + "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">\n Cohort definition for Colorectal Cancer Screening.\n </div>" + }, + "identifier": [ + { + "use": "official", + "system": "http://hl7.org/fhir/cqi/ecqm/Measure/Identifier/payer-extract", + "value": "COL" + } + ], + "version": "1.0.0", + "title": "Colorectal Cancer Screening. Cohort Definition", + "status": "active", + "experimental": true, + "description": "Colorectal Cancer Screening. Cohort Definition", + "topic": [ + { + "coding": [ + { + "system": "http://hl7.org/fhir/c80-doc-typecodes", + "code": "57024-2" + } + ] + } + ], + "library": [ + { + "reference": "Library/col-logic" + } + ], + "group": [ + { + "identifier": { + "value": "in-demographic" + }, + "population": [ + { + "identifier": { + "value": "in-demographic" + }, + "criteria": "In Demographic" + } + ] + }, + { + "identifier": { + "value": "history-of-colorectal-cancer" + }, + "population": [ + { + "identifier": { + "value": "history-of-colorectal-cancer" + }, + "criteria": "Hx Colorectal Cancer" + } + ] + }, + { + "identifier": { + "value": "history-of-total-colectomy" + }, + "population": [ + { + "identifier": { + "value": "history-of-total-colectomy" + }, + "criteria": "Hx Total Colectomy" + } + ] + }, + { + "identifier": { + "value": "colonoscopy-performed" + }, + "population": [ + { + "identifier": { + "value": "colonoscopy-performed" + }, + "criteria": "Colonoscopy Performed" + } + ] + }, + { + "identifier": { + "value": "colonoscopy-results" + }, + "population": [ + { + "identifier": { + "value": "colonoscopy-results" + }, + "criteria": "Colonoscopy Results" + } + ] + }, + { + "identifier": { + "value": "sigmoidoscopy-procedure" + }, + "population": [ + { + "identifier": { + "value": "sigmoidoscopy-procedure" + }, + "criteria": "Sigmoidoscopy Procedure" + } + ] + }, + { + "identifier": { + "value": "sigmoidoscopy-observation" + }, + "population": [ + { + "identifier": { + "value": "sigmoidoscopy-observation" + }, + "criteria": "Sigmoidoscopy Observation" + } + ] + }, + { + "identifier": { + "value": "fobt-procedure" + }, + "population": [ + { + "identifier": { + "value": "fobt-procedure" + }, + "criteria": "FOBT Procedure" + } + ] + }, + { + "identifier": { + "value": "fobt-observation" + }, + "population": [ + { + "identifier": { + "value": "fobt-observation" + }, + "criteria": "FOBT Observation" + } + ] + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-procedure.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-procedure.json new file mode 100644 index 00000000000..b9bac60b14d --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-procedure.json @@ -0,0 +1,68 @@ +{ + "resourceType": "Procedure", + "id": "Procedure-9", + "meta": { + "versionId": "1", + "lastUpdated": "2017-09-09T21:52:35.933-06:00" + }, + "extension": [ + { + "url": "http://mihin.org/fhir/templateId", + "valueString": "2.16.840.1.113883.10.20.24.3.64" + }, + { + "url": "http://mihin.org/fhir/templateId", + "valueString": "2.16.840.1.113883.10.20.22.4.14" + } + ], + "identifier": [ + { + "system": "http://hl7.org/fhir/identifier", + "value": "1.3.6.1.4.1.115:579f4eb5aeac500a550c5c7b" + } + ], + "status": "completed", + "category": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "387713003", + "display": "Surgical Procedure" + } + ] + }, + "code": { + "coding": [ + { + "system": "http://snomed.info/sct", + "code": "36192008" + } + ], + "text": "Procedure, Performed: Total Colectomy" + }, + "subject": { + "reference": "Patient/Patient-12214", + "display": "2 N Geriatric Jr" + }, + "performedPeriod": { + "start": "2010-10-12T06:00:00-04:00", + "end": "2010-10-12T08:15:00-04:00" + }, + "performer": [ + { + "role": { + "coding": [ + { + "system": "http://hl7.org/fhir/ValueSet/performer-role", + "code": "112247003", + "display": "Medical doctor (occupation)" + } + ] + }, + "actor": { + "reference": "Practitioner/Practitioner-12208", + "display": "Jay McCann Sawyer MD" + } + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-1.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-1.json new file mode 100644 index 00000000000..19a6f47dcd1 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-1.json @@ -0,0 +1,416 @@ +{ + "resourceType": "ValueSet", + "id": "2.16.840.1.113883.3.464.1003.108.11.1001", + "meta": { + "versionId": "3", + "lastUpdated": "2017-07-25T09:54:33.579+00:00" + }, + "url": "http://measure.eval.kanvix.com/cqf-ruler/baseDstu3/Valueset/2.16.840.1.113883.3.464.1003.108.11.1001", + "name": "Malignant Neoplasm of Colon (SNOMED CT) eCQM", + "status": "active", + "compose": { + "include": [ + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "concept": [ + { + "code": "187758006" + }, + { + "code": "109838007" + }, + { + "code": "1701000119104" + }, + { + "code": "187757001" + }, + { + "code": "269533000" + }, + { + "code": "269544008" + }, + { + "code": "285312008" + }, + { + "code": "285611007" + }, + { + "code": "301756000" + }, + { + "code": "312111009" + }, + { + "code": "312112002" + }, + { + "code": "312113007" + }, + { + "code": "312114001" + }, + { + "code": "312115000" + }, + { + "code": "314965007" + }, + { + "code": "315058005" + }, + { + "code": "363406005" + }, + { + "code": "363407001" + }, + { + "code": "363408006" + }, + { + "code": "363409003" + }, + { + "code": "363410008" + }, + { + "code": "363412000" + }, + { + "code": "363413005" + }, + { + "code": "363414004" + }, + { + "code": "363510005" + }, + { + "code": "425178004" + }, + { + "code": "449218003" + }, + { + "code": "93683002" + }, + { + "code": "93761005" + }, + { + "code": "93771007" + }, + { + "code": "93826009" + }, + { + "code": "93980002" + }, + { + "code": "94006002" + }, + { + "code": "94072004" + }, + { + "code": "94105000" + }, + { + "code": "94179005" + }, + { + "code": "94260004" + }, + { + "code": "94271003" + }, + { + "code": "94328005" + }, + { + "code": "94509004" + }, + { + "code": "94538001" + }, + { + "code": "94604000" + }, + { + "code": "94643001" + } + ] + } + ] + }, + "expansion": { + "identifier": "http://open-api2.hspconsortium.org/payerextract/data/ValueSet/2.16.840.1.113883.3.464.1003.108.11.1001", + "timestamp": "2016-09-19T14:05:21.939-04:00", + "total": 43, + "offset": 0, + "contains": [ + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "425178004", + "display": "Adenocarcinoma of rectosigmoid junction" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "301756000", + "display": "Adenocarcinoma of sigmoid colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "312111009", + "display": "Carcinoma of ascending colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "269533000", + "display": "Carcinoma of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "312113007", + "display": "Carcinoma of descending colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "312114001", + "display": "Carcinoma of hepatic flexure" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "285312008", + "display": "Carcinoma of sigmoid colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "312115000", + "display": "Carcinoma of splenic flexure" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "269544008", + "display": "Carcinoma of the rectosigmoid junction" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "312112002", + "display": "Carcinoma of transverse colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "315058005", + "display": "Lynch syndrome" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "314965007", + "display": "Local recurrence of malignant tumor of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "449218003", + "display": "Lymphoma of sigmoid colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "187758006", + "display": "Malignant neoplasm of other specified sites of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "187757001", + "display": "Malignant neoplasm, overlapping lesion of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "363412000", + "display": "Malignant tumor of ascending colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "363406005", + "display": "Malignant tumor of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "363409003", + "display": "Malignant tumor of descending colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "363407001", + "display": "Malignant tumor of hepatic flexure" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "363510005", + "display": "Malignant tumor of large intestine" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "363414004", + "display": "Malignant tumor of rectosigmoid junction" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "363410008", + "display": "Malignant tumor of sigmoid colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "363413005", + "display": "Malignant tumor of splenic flexure" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "363408006", + "display": "Malignant tumor of transverse colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "285611007", + "display": "Metastasis to colon of unknown primary" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "109838007", + "display": "Overlapping malignant neoplasm of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "1701000119104", + "display": "Primary adenocarcinoma of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "93683002", + "display": "Primary malignant neoplasm of ascending colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "93761005", + "display": "Primary malignant neoplasm of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "93771007", + "display": "Primary malignant neoplasm of descending colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "93826009", + "display": "Primary malignant neoplasm of hepatic flexure of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "93980002", + "display": "Primary malignant neoplasm of rectosigmoid junction" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "94006002", + "display": "Primary malignant neoplasm of sigmoid colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "94072004", + "display": "Primary malignant neoplasm of splenic flexure of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "94105000", + "display": "Primary malignant neoplasm of transverse colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "94179005", + "display": "Secondary malignant neoplasm of ascending colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "94260004", + "display": "Secondary malignant neoplasm of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "94271003", + "display": "Secondary malignant neoplasm of descending colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "94328005", + "display": "Secondary malignant neoplasm of hepatic flexure of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "94509004", + "display": "Secondary malignant neoplasm of rectosigmoid junction" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "94538001", + "display": "Secondary malignant neoplasm of sigmoid colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "94604000", + "display": "Secondary malignant neoplasm of splenic flexure of colon" + }, + { + "system": "http://snomed.info/sct", + "version": "2015.03.14AB", + "code": "94643001", + "display": "Secondary malignant neoplasm of transverse colon" + } + ] + } +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-2.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-2.json new file mode 100644 index 00000000000..1d09b2d79af --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-2.json @@ -0,0 +1,181 @@ +{ + "resourceType": "ValueSet", + "id": "2.16.840.1.113883.3.464.1003.198.12.1019", + "meta": { + "versionId": "3", + "lastUpdated": "2017-07-25T09:54:33.579+00:00" + }, + "url": "http://measure.eval.kanvix.com/cql-measure-processor/baseDstu3/Valueset/2.16.840.1.113883.3.464.1003.198.12.1019 ", + "name": "Total Colectomy eMeasure", + "compose": { + "include": [ + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2016.1.15AA", + "concept": [ + { + "code": "44156" + }, + { + "code": "44158" + }, + { + "code": "44157" + }, + { + "code": "44155" + }, + { + "code": "44151" + }, + { + "code": "44150" + }, + { + "code": "44211" + }, + { + "code": "44212" + }, + { + "code": "44210" + }, + { + "code": "44153" + }, + { + "code": "44152" + } + ] + }, + { + "system": "http://snomed.info/sct", + "version": "2015.09.15AA", + "filter": [ + { + "property": "concept", + "op": "is-a", + "value": "26390003" + } + ] + } + ] + }, + "expansion": { + "timestamp": "2016-09-20T12:32:19.296-04:00", + "total": 22, + "offset": 0, + "contains": [ + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44156", + "display": "Colectomy, total, abdominal, with proctectomy; with continent ileostomy" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44158", + "display": "Colectomy, total, abdominal, with proctectomy; with ileoanal anastomosis, creation of ileal reservoir (S or J), includes loop ileostomy, and rectal mucosectomy, when performed" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44157", + "display": "Colectomy, total, abdominal, with proctectomy; with ileoanal anastomosis, includes loop ileostomy, and rectal mucosectomy, when performed" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44155", + "display": "Colectomy, total, abdominal, with proctectomy; with ileostomy" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44151", + "display": "Colectomy, total, abdominal, without proctectomy; with continent ileostomy" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44150", + "display": "Colectomy, total, abdominal, without proctectomy; with ileostomy or ileoproctostomy" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44153", + "display": "Colectomy, total, abdominal, without proctectomy; with rectal mucosectomy, ileoanal anastomosis, creation of ileal reservoir (S or J), with or without loop ileostomy" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44152", + "display": "Colectomy, total, abdominal, without proctectomy; with rectal mucosectomy, ileoanal anastomosis, with or without loop ileostomy" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44211", + "display": "Laparoscopy, surgical; colectomy, total, abdominal, with proctectomy, with ileoanal anastomosis, creation of ileal reservoir (S or J), with loop ileostomy, includes rectal mucosectomy, when performed" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44212", + "display": "Laparoscopy, surgical; colectomy, total, abdominal, with proctectomy, with ileostomy" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44210", + "display": "Laparoscopy, surgical; colectomy, total, abdominal, without proctectomy, with ileostomy or ileoproctostomy" + }, + { + "system": "http://snomed.info/sct", + "code": "303401008", + "display": "Parks panproctocolectomy, anastomosis of ileum to anus and creation of pouch" + }, + { + "system": "http://snomed.info/sct", + "code": "235331003", + "display": "Restorative proctocolectomy" + }, + { + "system": "http://snomed.info/sct", + "code": "36192008", + "display": "Total abdominal colectomy with ileoproctostomy" + }, + { + "system": "http://snomed.info/sct", + "code": "456004", + "display": "Total abdominal colectomy with ileostomy" + }, + { + "system": "http://snomed.info/sct", + "code": "44751009", + "display": "Total abdominal colectomy with proctectomy and continent ileostomy" + }, + { + "system": "http://snomed.info/sct", + "code": "31130001", + "display": "Total abdominal colectomy with proctectomy and ileostomy" + }, + { + "system": "http://snomed.info/sct", + "code": "80294005", + "display": "Total abdominal colectomy with rectal mucosectomy and ileoanal anastomosis" + }, + { + "system": "http://snomed.info/sct", + "code": "26390003", + "display": "Total colectomy" + }, + { + "system": "http://snomed.info/sct", + "code": "307666008", + "display": "Total colectomy and ileostomy" + }, + { + "system": "http://snomed.info/sct", + "code": "307669001", + "display": "Total colectomy, ileostomy and closure of rectal stump" + }, + { + "system": "http://snomed.info/sct", + "code": "307667004", + "display": "Total colectomy, ileostomy and rectal mucous fistula" + } + ] + } +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-3.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-3.json new file mode 100644 index 00000000000..87882baf088 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-3.json @@ -0,0 +1,421 @@ +{ + "resourceType": "ValueSet", + "id": "2.16.840.1.113883.3.464.1003.108.12.1020", + "meta": { + "versionId": "3", + "lastUpdated": "2017-07-25T09:54:33.579+00:00" + }, + "url": "http://measure.eval.kanvix.com/cql-measure-processor/baseDstu3/Valueset/2.16.840.1.113883.3.464.1003.108.12.1020", + "name": "Colonoscopy eMeasure", + "compose": { + "include": [ + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2015.1.14AB", + "concept": [ + { + "code": "44388" + }, + { + "code": "44393" + }, + { + "code": "44389" + }, + { + "code": "44391" + }, + { + "code": "44390" + }, + { + "code": "44392" + }, + { + "code": "44394" + }, + { + "code": "44397" + }, + { + "code": "45378" + }, + { + "code": "45383" + }, + { + "code": "45380" + }, + { + "code": "45382" + }, + { + "code": "45386" + }, + { + "code": "45381" + }, + { + "code": "45391" + }, + { + "code": "45379" + }, + { + "code": "45384" + }, + { + "code": "45385" + }, + { + "code": "45387" + }, + { + "code": "45392" + }, + { + "code": "45355" + }, + { + "code": "44401" + }, + { + "code": "44402" + }, + { + "code": "44403" + }, + { + "code": "44404" + }, + { + "code": "44405" + }, + { + "code": "44406" + }, + { + "code": "44407" + }, + { + "code": "44408" + }, + { + "code": "45388" + }, + { + "code": "45389" + }, + { + "code": "45390" + }, + { + "code": "45393" + }, + { + "code": "45398" + } + ] + }, + { + "system": "http://snomed.info/sct", + "version": "2014.07.14AA", + "filter": [ + { + "property": "concept", + "op": "is-a", + "value": "73761001" + } + ] + }, + { + "system": "http://snomed.info/sct", + "version": "2014.07.14AA", + "filter": [ + { + "property": "concept", + "op": "is-a", + "value": "174184006" + } + ] + } + ] + }, + "expansion": { + "timestamp": "2016-09-20T13:07:55.271-04:00", + "total": 54, + "offset": 0, + "contains": [ + { + "system": "http://snomed.info/sct", + "code": "310634005", + "display": "Check colonoscopy" + }, + { + "system": "http://snomed.info/sct", + "code": "73761001", + "display": "Colonoscopy" + }, + { + "system": "http://snomed.info/sct", + "code": "446745002", + "display": "Colonoscopy and biopsy of colon" + }, + { + "system": "http://snomed.info/sct", + "code": "446521004", + "display": "Colonoscopy and excision of mucosa of colon" + }, + { + "system": "http://snomed.info/sct", + "code": "447021001", + "display": "Colonoscopy and tattooing" + }, + { + "system": "http://snomed.info/sct", + "code": "443998000", + "display": "Colonoscopy through colostomy with endoscopic biopsy of colon" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44388", + "display": "Colonoscopy through stoma; diagnostic, including collection of specimen(s) by brushing or washing, when performed (separate procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44401", + "display": "Colonoscopy through stoma; with ablation of tumor(s), polyp(s), or other lesion(s) (includes pre-and post-dilation and guide wire passage, when performed)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44393", + "display": "Colonoscopy through stoma; with ablation of tumor(s), polyp(s), or other lesion(s) not amenable to removal by hot biopsy forceps, bipolar cautery or snare technique" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44389", + "display": "Colonoscopy through stoma; with biopsy, single or multiple" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44391", + "display": "Colonoscopy through stoma; with control of bleeding, any method" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44408", + "display": "Colonoscopy through stoma; with decompression (for pathologic distention) (eg, volvulus, megacolon), including placement of decompression tube, when performed" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44404", + "display": "Colonoscopy through stoma; with directed submucosal injection(s), any substance" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44403", + "display": "Colonoscopy through stoma; with endoscopic mucosal resection" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44402", + "display": "Colonoscopy through stoma; with endoscopic stent placement (including pre- and post-dilation and guide wire passage, when performed)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44406", + "display": "Colonoscopy through stoma; with endoscopic ultrasound examination, limited to the sigmoid, descending, transverse, or ascending colon and cecum and adjacent structures" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44390", + "display": "Colonoscopy through stoma; with removal of foreign body(s)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44392", + "display": "Colonoscopy through stoma; with removal of tumor(s), polyp(s), or other lesion(s) by hot biopsy forceps" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44394", + "display": "Colonoscopy through stoma; with removal of tumor(s), polyp(s), or other lesion(s) by snare technique" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44405", + "display": "Colonoscopy through stoma; with transendoscopic balloon dilation" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44397", + "display": "Colonoscopy through stoma; with transendoscopic stent placement (includes predilation)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "44407", + "display": "Colonoscopy through stoma; with transendoscopic ultrasound guided intramural or transmural fine needle aspiration/biopsy(s), includes endoscopic ultrasound examination limited to the sigmoid, descending, transverse, or ascending colon and cecum and adjacent structures" + }, + { + "system": "http://snomed.info/sct", + "code": "12350003", + "display": "Colonoscopy with rigid sigmoidoscope through colotomy" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45383", + "display": "Colonoscopy, flexible, proximal to splenic flexure; with ablation of tumor(s), polyp(s), or other lesion(s) not amenable to removal by hot biopsy forceps, bipolar cautery or snare technique" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45387", + "display": "Colonoscopy, flexible, proximal to splenic flexure; with transendoscopic stent placement (includes predilation)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45378", + "display": "Colonoscopy, flexible; diagnostic, including collection of specimen(s) by brushing or washing, when performed (separate procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45388", + "display": "Colonoscopy, flexible; with ablation of tumor(s), polyp(s), or other lesion(s) (includes pre- and post-dilation and guide wire passage, when performed)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45398", + "display": "Colonoscopy, flexible; with band ligation(s) (eg, hemorrhoids)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45380", + "display": "Colonoscopy, flexible; with biopsy, single or multiple" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45382", + "display": "Colonoscopy, flexible; with control of bleeding, any method" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45393", + "display": "Colonoscopy, flexible; with decompression (for pathologic distention) (eg, volvulus, megacolon), including placement of decompression tube, when performed" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45381", + "display": "Colonoscopy, flexible; with directed submucosal injection(s), any substance" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45390", + "display": "Colonoscopy, flexible; with endoscopic mucosal resection" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45389", + "display": "Colonoscopy, flexible; with endoscopic stent placement (includes pre- and post-dilation and guide wire passage, when performed)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45391", + "display": "Colonoscopy, flexible; with endoscopic ultrasound examination limited to the rectum, sigmoid, descending, transverse, or ascending colon and cecum, and adjacent structures" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45379", + "display": "Colonoscopy, flexible; with removal of foreign body(s)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45384", + "display": "Colonoscopy, flexible; with removal of tumor(s), polyp(s), or other lesion(s) by hot biopsy forceps" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45385", + "display": "Colonoscopy, flexible; with removal of tumor(s), polyp(s), or other lesion(s) by snare technique" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45386", + "display": "Colonoscopy, flexible; with transendoscopic balloon dilation" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45392", + "display": "Colonoscopy, flexible; with transendoscopic ultrasound guided intramural or transmural fine needle aspiration/biopsy(s), includes endoscopic ultrasound examination limited to the rectum, sigmoid, descending, transverse, or ascending colon and cecum, and adjacent structures" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45355", + "display": "Colonoscopy, rigid or flexible, transabdominal via colotomy, single or multiple" + }, + { + "system": "https://www.cms.gov/Medicare/Coding/MedHCPCSGenInfo/index.html", + "code": "G0105", + "display": "Colorectal cancer screening; colonoscopy on individual at high risk" + }, + { + "system": "https://www.cms.gov/Medicare/Coding/MedHCPCSGenInfo/index.html", + "code": "G0121", + "display": "Colorectal cancer screening; colonoscopy on individual not meeting criteria for high risk" + }, + { + "system": "http://snomed.info/sct", + "code": "427459009", + "display": "Diagnostic endoscopic examination of colonic pouch and biopsy of colonic pouch using colonoscope" + }, + { + "system": "http://snomed.info/sct", + "code": "174184006", + "display": "Diagnostic endoscopic examination on colon" + }, + { + "system": "http://snomed.info/sct", + "code": "367535003", + "display": "Fiberoptic colonoscopy" + }, + { + "system": "http://snomed.info/sct", + "code": "8180007", + "display": "Fiberoptic colonoscopy through colostomy" + }, + { + "system": "http://snomed.info/sct", + "code": "25732003", + "display": "Fiberoptic colonoscopy with biopsy" + }, + { + "system": "http://snomed.info/sct", + "code": "34264006", + "display": "Intraoperative colonoscopy" + }, + { + "system": "http://snomed.info/sct", + "code": "235151005", + "display": "Limited colonoscopy" + }, + { + "system": "http://snomed.info/sct", + "code": "174158000", + "display": "Open colonoscopy" + }, + { + "system": "http://snomed.info/sct", + "code": "444783004", + "display": "Screening colonoscopy" + }, + { + "system": "http://snomed.info/sct", + "code": "303587008", + "display": "Therapeutic colonoscopy" + }, + { + "system": "http://snomed.info/sct", + "code": "235150006", + "display": "Total colonoscopy" + } + ] + } +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-4.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-4.json new file mode 100644 index 00000000000..139a8e6246b --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-4.json @@ -0,0 +1,208 @@ +{ + "resourceType": "ValueSet", + "id": "2.16.840.1.113883.3.464.1003.198.12.1010", + "meta": { + "versionId": "6", + "lastUpdated": "2017-07-25T09:54:33.579+00:00" + }, + "url": "http://measure.eval.kanvix.com/cql-measure-processor/baseDstu3/Valueset/2.16.840.1.113883.3.464.1003.198.12.1010", + "name": "Flexible Sigmoidoscopy eMeasure", + "compose": { + "include": [ + { + "system": "http://www.ama-assn.org/go/cpt", + "version": "2015.1.14AB", + "concept": [ + { + "code": "45330" + }, + { + "code": "45339" + }, + { + "code": "45331" + }, + { + "code": "45334" + }, + { + "code": "45337" + }, + { + "code": "45340" + }, + { + "code": "45335" + }, + { + "code": "45341" + }, + { + "code": "45332" + }, + { + "code": "45333" + }, + { + "code": "45338" + }, + { + "code": "45345" + }, + { + "code": "45342" + }, + { + "code": "45346" + }, + { + "code": "45347" + }, + { + "code": "45349" + }, + { + "code": "45350" + } + ] + }, + { + "system": "https://www.cms.gov/Medicare/Coding/MedHCPCSGenInfo/index.html", + "version": "2016.1.15AB", + "concept": [ + { + "code": "G0104" + } + ] + }, + { + "system": "http://snomed.info/sct", + "version": "2014.07.14AA", + "filter": [ + { + "property": "concept", + "op": "is-a", + "value": "44441009" + } + ] + } + ] + }, + "expansion": { + "timestamp": "2016-09-20T13:20:03.237-04:00", + "total": 22, + "offset": 0, + "contains": [ + { + "system": "https://www.cms.gov/Medicare/Coding/MedHCPCSGenInfo/index.html", + "code": "G0104", + "display": "Colorectal cancer screening; flexible sigmoidoscopy" + }, + { + "system": "http://snomed.info/sct", + "code": "425634007", + "display": "Diagnostic endoscopic examination of lower bowel and sampling for bacterial overgrowth using fiberoptic sigmoidoscope" + }, + { + "system": "http://snomed.info/sct", + "code": "44441009", + "display": "Flexible fiberoptic sigmoidoscopy" + }, + { + "system": "http://snomed.info/sct", + "code": "112870002", + "display": "Flexible fiberoptic sigmoidoscopy for removal of foreign body" + }, + { + "system": "http://snomed.info/sct", + "code": "396226005", + "display": "Flexible fiberoptic sigmoidoscopy with biopsy" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45330", + "display": "Sigmoidoscopy, flexible; diagnostic, including collection of specimen(s) by brushing or washing, when performed (separate procedure)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45346", + "display": "Sigmoidoscopy, flexible; with ablation of tumor(s), polyp(s), or other lesion(s) (includes pre- and post-dilation and guide wire passage, when performed)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45339", + "display": "Sigmoidoscopy, flexible; with ablation of tumor(s), polyp(s), or other lesion(s) not amenable to removal by hot biopsy forceps, bipolar cautery or snare technique" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45350", + "display": "Sigmoidoscopy, flexible; with band ligation(s) (eg, hemorrhoids)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45331", + "display": "Sigmoidoscopy, flexible; with biopsy, single or multiple" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45334", + "display": "Sigmoidoscopy, flexible; with control of bleeding, any method" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45337", + "display": "Sigmoidoscopy, flexible; with decompression (for pathologic distention) (eg, volvulus, megacolon), including placement of decompression tube, when performed" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45335", + "display": "Sigmoidoscopy, flexible; with directed submucosal injection(s), any substance" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45349", + "display": "Sigmoidoscopy, flexible; with endoscopic mucosal resection" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45341", + "display": "Sigmoidoscopy, flexible; with endoscopic ultrasound examination" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45347", + "display": "Sigmoidoscopy, flexible; with placement of endoscopic stent (includes pre- and post-dilation and guide wire passage, when performed)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45332", + "display": "Sigmoidoscopy, flexible; with removal of foreign body(s)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45333", + "display": "Sigmoidoscopy, flexible; with removal of tumor(s), polyp(s), or other lesion(s) by hot biopsy forceps" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45338", + "display": "Sigmoidoscopy, flexible; with removal of tumor(s), polyp(s), or other lesion(s) by snare technique" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45340", + "display": "Sigmoidoscopy, flexible; with transendoscopic balloon dilation" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45345", + "display": "Sigmoidoscopy, flexible; with transendoscopic stent placement (includes predilation)" + }, + { + "system": "http://www.ama-assn.org/go/cpt", + "code": "45342", + "display": "Sigmoidoscopy, flexible; with transendoscopic ultrasound guided intramural or transmural fine needle aspiration/biopsy(s)" + } + ] + } +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-5.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-5.json new file mode 100644 index 00000000000..a060cf3f2c7 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/measure-processing-valueset-5.json @@ -0,0 +1,147 @@ +{ + "resourceType": "ValueSet", + "id": "2.16.840.1.113883.3.464.1003.198.12.1011", + "meta": { + "versionId": "3", + "lastUpdated": "2017-07-25T09:54:33.579+00:00" + }, + "url": "http://measure.eval.kanvix.com/cql-measure-processor/baseDstu3/Valueset/2.16.840.1.113883.3.464.1003.198.12.1011", + "name": "Fecal Occult Blood Test (FOBT) eMeasure", + "compose": { + "include": [ + { + "system": "http://loinc.org", + "version": "2.44.13AA", + "concept": [ + { + "code": "27396-1" + }, + { + "code": "58453-2" + }, + { + "code": "2335-8" + }, + { + "code": "14563-1" + }, + { + "code": "14564-9" + }, + { + "code": "14565-6" + }, + { + "code": "12503-9" + }, + { + "code": "12504-7" + }, + { + "code": "27401-9" + }, + { + "code": "27925-7" + }, + { + "code": "27926-5" + }, + { + "code": "29771-3" + }, + { + "code": "57905-2" + }, + { + "code": "56490-6" + }, + { + "code": "56491-4" + } + ] + } + ] + }, + "expansion": { + "timestamp": "2016-09-20T13:32:34.390-04:00", + "total": 15, + "offset": 0, + "contains": [ + { + "system": "http://loinc.org", + "code": "27396-1", + "display": "Hemoglobin.gastrointestinal [Mass/mass] in Stool" + }, + { + "system": "http://loinc.org", + "code": "58453-2", + "display": "Hemoglobin.gastrointestinal [Mass/volume] in Stool by Immunologic method" + }, + { + "system": "http://loinc.org", + "code": "2335-8", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool" + }, + { + "system": "http://loinc.org", + "code": "14563-1", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool --1st specimen" + }, + { + "system": "http://loinc.org", + "code": "14564-9", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool --2nd specimen" + }, + { + "system": "http://loinc.org", + "code": "14565-6", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool --3rd specimen" + }, + { + "system": "http://loinc.org", + "code": "12503-9", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool --4th specimen" + }, + { + "system": "http://loinc.org", + "code": "12504-7", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool --5th specimen" + }, + { + "system": "http://loinc.org", + "code": "27401-9", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool --6th specimen" + }, + { + "system": "http://loinc.org", + "code": "27925-7", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool --7th specimen" + }, + { + "system": "http://loinc.org", + "code": "27926-5", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool --8th specimen" + }, + { + "system": "http://loinc.org", + "code": "29771-3", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool by Immunologic method" + }, + { + "system": "http://loinc.org", + "code": "57905-2", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool by Immunologic method --1st specimen" + }, + { + "system": "http://loinc.org", + "code": "56490-6", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool by Immunologic method --2nd specimen" + }, + { + "system": "http://loinc.org", + "code": "56491-4", + "display": "Hemoglobin.gastrointestinal [Presence] in Stool by Immunologic method --3rd specimen" + } + ] + } +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/plandefinition-apply-library.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/plandefinition-apply-library.json new file mode 100644 index 00000000000..4df7e6e9eb0 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/plandefinition-apply-library.json @@ -0,0 +1,19 @@ +{ + "resourceType": "Library", + "id": "plandefinitionApplyTest", + "version": "1.0", + "status": "draft", + "type": { + "coding": [ + { + "code": "logic-library" + } + ] + }, + "content": [ + { + "contentType": "text/cql", + "data": "bGlicmFyeSBwbGFuZGVmaW5pdGlvbkFwcGx5VGVzdCB2ZXJzaW9uICcxLjAnDQoNCmRlZmluZSBSZXN1bHRzOg0KICAgIHRydWUNCg0KZGVmaW5lICJEeW5hbWljIERldGFpbCBEZWZpbml0aW9uIjoNCiAgICAnVGhpcyBpcyBhIGR5bmFtaWMgZGVmaW5pdGlvbiEn" + } + ] +} \ No newline at end of file diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/plandefinition-apply.json b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/plandefinition-apply.json new file mode 100644 index 00000000000..77e0f7253d8 --- /dev/null +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/resources/ca/uhn/fhir/jpa/cds/example/plandefinition-apply.json @@ -0,0 +1,59 @@ +{ + "resourceType": "PlanDefinition", + "id": "apply-example", + "text": { + "status": "generated", + "div": "<div xmlns=\"http://www.w3.org/1999/xhtml\">General PlanDefinition $apply example resource</div>" + }, + "identifier": [ + { + "use": "official", + "value": "apply-example" + } + ], + "version": "1.0", + "name": "Example", + "title": "Example for PlanDefinition $apply operation", + "type": { + "coding": [ + { + "system": "http://hl7.org/fhir/plan-definition-type", + "code": "eca-rule", + "display": "ECA Rule" + } + ] + }, + "status": "draft", + "date": "2017-09-18", + "purpose": "Testing", + "usage": "This resource is to be used only for testing", + "topic": [ + { + "text": "Testing $apply operation" + } + ], + "library": [ + { + "reference": "Library/plandefinitionApplyTest" + } + ], + "action": [ + { + "condition": [ + { + "kind": "applicability", + "description": "Simple test", + "language": "text/cql", + "expression": "plandefinitionApplyTest.Results" + } + ], + "dynamicValue": [ + { + "description": "Set CarePlan detail definition", + "path": "title", + "expression": "plandefinitionApplyTest.\"Dynamic Detail Definition\"" + } + ] + } + ] +} \ No newline at end of file diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 8f8cafe83f8..a947d8fecef 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -133,7 +133,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> + <argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> </configuration> </plugin> <!-- diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index 7bfc6ff2761..96c3237adb4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -149,8 +149,12 @@ public class FhirContext { myVersion = FhirVersionEnum.DSTU2.getVersionImplementation(); } else if (FhirVersionEnum.DSTU2_HL7ORG.isPresentOnClasspath()) { myVersion = FhirVersionEnum.DSTU2_HL7ORG.getVersionImplementation(); + } else if (FhirVersionEnum.DSTU2_1.isPresentOnClasspath()) { + myVersion = FhirVersionEnum.DSTU2_1.getVersionImplementation(); } else if (FhirVersionEnum.DSTU3.isPresentOnClasspath()) { myVersion = FhirVersionEnum.DSTU3.getVersionImplementation(); + } else if (FhirVersionEnum.R4.isPresentOnClasspath()) { + myVersion = FhirVersionEnum.R4.getVersionImplementation(); } else { throw new IllegalStateException(getLocalizer().getMessage(FhirContext.class, "noStructures")); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java index 7e1ba077c86..bcebc8bbbda 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java @@ -102,40 +102,130 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST> */ CodeValidationResult<CDCT, IST> validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay); + abstract class BaseConceptProperty { + private final String myPropertyName; + + /** + * Constructor + */ + protected BaseConceptProperty(String thePropertyName) { + myPropertyName = thePropertyName; + } + + public String getPropertyName() { + return myPropertyName; + } + } + + class StringConceptProperty extends BaseConceptProperty { + private final String myValue; + + /** + * Constructor + * + * @param theName The name + */ + public StringConceptProperty(String theName, String theValue) { + super(theName); + myValue = theValue; + } + + public String getValue() { + return myValue; + } + } + + class CodingConceptProperty extends BaseConceptProperty { + private final String myCode; + private final String myCodeSystem; + private final String myDisplay; + + /** + * Constructor + * + * @param theName The name + */ + public CodingConceptProperty(String theName, String theCodeSystem, String theCode, String theDisplay) { + super(theName); + myCodeSystem = theCodeSystem; + myCode = theCode; + myDisplay = theDisplay; + } + + public String getCode() { + return myCode; + } + + public String getCodeSystem() { + return myCodeSystem; + } + + public String getDisplay() { + return myDisplay; + } + } + class CodeValidationResult<CDCT, IST> { - private CDCT definition; - private String message; - private IST severity; + private CDCT myDefinition; + private String myMessage; + private IST mySeverity; + private String myCodeSystemName; + private String myCodeSystemVersion; + private List<BaseConceptProperty> myProperties; public CodeValidationResult(CDCT theNext) { - this.definition = theNext; + this.myDefinition = theNext; } public CodeValidationResult(IST severity, String message) { - this.severity = severity; - this.message = message; + this.mySeverity = severity; + this.myMessage = message; } public CodeValidationResult(IST severity, String message, CDCT definition) { - this.severity = severity; - this.message = message; - this.definition = definition; + this.mySeverity = severity; + this.myMessage = message; + this.myDefinition = definition; } public CDCT asConceptDefinition() { - return definition; + return myDefinition; + } + + public String getCodeSystemName() { + return myCodeSystemName; + } + + public void setCodeSystemName(String theCodeSystemName) { + myCodeSystemName = theCodeSystemName; + } + + public String getCodeSystemVersion() { + return myCodeSystemVersion; + } + + public void setCodeSystemVersion(String theCodeSystemVersion) { + myCodeSystemVersion = theCodeSystemVersion; } public String getMessage() { - return message; + return myMessage; + } + + public List<BaseConceptProperty> getProperties() { + return myProperties; + } + + public void setProperties(List<BaseConceptProperty> theProperties) { + myProperties = theProperties; } public IST getSeverity() { - return severity; + return mySeverity; } public boolean isOk() { - return definition != null; + return myDefinition != null; } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java index 3a5f64c95e1..406ab79267f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java @@ -605,4 +605,29 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable { } + public static final class ExtensionResourceMetadataKey extends ResourceMetadataKeyEnum<ExtensionDt> { + public ExtensionResourceMetadataKey(String url) { + super(url); + } + + @Override + public ExtensionDt get(IResource theResource) { + Object retValObj = theResource.getResourceMetadata().get(this); + if (retValObj == null) { + return null; + } else if (retValObj instanceof ExtensionDt) { + return (ExtensionDt) retValObj; + } + throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + + "' in resource metadata for key " + this.name() + " - Expected " + + ExtensionDt.class.getCanonicalName()); + } + + @Override + public void put(IResource theResource, ExtensionDt theObject) { + theResource.getResourceMetadata().put(this, theObject); + } + } + + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java index 3e2482d4ca5..e05e78f3399 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java @@ -918,6 +918,18 @@ public abstract class BaseParser implements IParser { throw new DataFormatException(nextChild + " has no child of type " + theType); } + protected List<Map.Entry<ResourceMetadataKeyEnum<?>, Object>> getExtensionMetadataKeys(IResource resource) { + List<Map.Entry<ResourceMetadataKeyEnum<?>, Object>> extensionMetadataKeys = new ArrayList<Map.Entry<ResourceMetadataKeyEnum<?>, Object>>(); + for (Map.Entry<ResourceMetadataKeyEnum<?>, Object> entry : resource.getResourceMetadata().entrySet()) { + if (entry.getKey() instanceof ResourceMetadataKeyEnum.ExtensionResourceMetadataKey) { + extensionMetadataKeys.add(entry); + } + } + + return extensionMetadataKeys; + } + + protected static <T> List<T> extractMetadataListNotNull(IResource resource, ResourceMetadataKeyEnum<List<T>> key) { List<? extends T> securityLabels = key.get(resource); if (securityLabels == null) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index d56aff6e5d2..c0b28de9b74 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -45,10 +45,7 @@ import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.ID_DATATYPE; import static ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum.PRIMITIVE_DATATYPE; @@ -654,8 +651,9 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { if (isBlank(versionIdPart)) { versionIdPart = ResourceMetadataKeyEnum.VERSION.get(resource); } + List<Map.Entry<ResourceMetadataKeyEnum<?>, Object>> extensionMetadataKeys = getExtensionMetadataKeys(resource); - if (super.shouldEncodeResourceMeta(resource) && ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false) { + if (super.shouldEncodeResourceMeta(resource) && (ElementUtil.isEmpty(versionIdPart, updated, securityLabels, tags, profiles) == false) || !extensionMetadataKeys.isEmpty()) { beginObject(theEventWriter, "meta"); writeOptionalTagWithTextNode(theEventWriter, "versionId", versionIdPart); writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", updated); @@ -695,6 +693,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { theEventWriter.endArray(); } + addExtensionMetadata(theResDef, theResource, theContainedResource, theSubResource, extensionMetadataKeys, resDef, theEventWriter); + theEventWriter.endObject(); // end meta } } @@ -704,6 +704,23 @@ public class JsonParser extends BaseParser implements IJsonLikeParser { theEventWriter.endObject(); } + + private void addExtensionMetadata(RuntimeResourceDefinition theResDef, IBaseResource theResource, + boolean theContainedResource, boolean theSubResource, + List<Map.Entry<ResourceMetadataKeyEnum<?>, Object>> extensionMetadataKeys, + RuntimeResourceDefinition resDef, + JsonLikeWriter theEventWriter) throws IOException { + if (extensionMetadataKeys.isEmpty()) { + return; + } + + ExtensionDt metaResource = new ExtensionDt(); + for (Map.Entry<ResourceMetadataKeyEnum<?>, Object> entry : extensionMetadataKeys) { + metaResource.addUndeclaredExtension((ExtensionDt) entry.getValue()); + } + encodeCompositeElementToStreamWriter(theResDef, theResource, metaResource, theEventWriter, theContainedResource, theSubResource, new CompositeChildElement(resDef, theSubResource)); + } + /** * This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object * called _name): resource extensions, and extension extensions diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java index 6f4c5d02ad6..8021523cedd 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java @@ -841,6 +841,25 @@ class ParserState<T> { } } + @Override + public void enteringNewElementExtension(StartElement theElem, String theUrlAttr, boolean theIsModifier, final String baseServerUrl) { + ResourceMetadataKeyEnum.ExtensionResourceMetadataKey resourceMetadataKeyEnum = new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey(theUrlAttr); + Object metadataValue = myMap.get(resourceMetadataKeyEnum); + ExtensionDt newExtension; + if (metadataValue == null) { + newExtension = new ExtensionDt(theIsModifier); + } else if (metadataValue instanceof ExtensionDt) { + newExtension = (ExtensionDt) metadataValue; + } else { + throw new IllegalStateException("Expected ExtensionDt as custom resource metadata type, got: " + metadataValue.getClass().getSimpleName()); + } + newExtension.setUrl(theUrlAttr); + myMap.put(resourceMetadataKeyEnum, newExtension); + + ExtensionState newState = new ExtensionState(getPreResourceState(), newExtension); + push(newState); + } + } private class MetaVersionElementState extends BaseState { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java index 7a7d04c2483..721dccad006 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java @@ -104,24 +104,33 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/ void doSetValueAsQueryToken(FhirContext theContext, String theParamName, String theQualifier, String theValue) { String q = theQualifier; String resourceType = null; + boolean skipSetValue = false; if (isNotBlank(q)) { if (q.startsWith(":")) { int nextIdx = q.indexOf('.'); if (nextIdx != -1) { resourceType = q.substring(1, nextIdx); myChain = q.substring(nextIdx + 1); + // type is explicitly defined so use it + myId.setParts(null, resourceType, theValue, null); + skipSetValue = true; } else { resourceType = q.substring(1); } } else if (q.startsWith(".")) { myChain = q.substring(1); + // type not defined but this is a chain, so treat value as opaque + myId.setParts(null, null, theValue, null); + skipSetValue = true; } } - setValue(theValue); + if (!skipSetValue) { + setValue(theValue); - if (isNotBlank(resourceType) && isBlank(getResourceType())) { - setValue(resourceType + '/' + theValue); + if (isNotBlank(resourceType) && isBlank(getResourceType())) { + setValue(resourceType + '/' + theValue); + } } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StopWatch.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StopWatch.java index 8103c31c81f..63c9fe115a2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StopWatch.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/StopWatch.java @@ -6,8 +6,11 @@ import org.apache.commons.lang3.time.DateUtils; import java.text.DecimalFormat; import java.text.NumberFormat; import java.util.Date; +import java.util.LinkedHashMap; import java.util.concurrent.TimeUnit; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + /* * #%L * HAPI FHIR - Core Library @@ -17,9 +20,9 @@ import java.util.concurrent.TimeUnit; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -29,6 +32,14 @@ import java.util.concurrent.TimeUnit; */ /** + * A multipurpose stopwatch which can be used to time tasks and produce + * human readable output about task duration, throughput, estimated task completion, + * etc. + * <p> + * <p> + * <b>Thread Safety Note: </b> StopWatch is not intended to be thread safe. + * </p> + * * @since HAPI FHIR 3.3.0 */ public class StopWatch { @@ -37,6 +48,9 @@ public class StopWatch { private static final NumberFormat TEN_DAY_FORMAT = new DecimalFormat("0"); private static Long ourNowForUnitTest; private long myStarted = now(); + private long myCurrentTaskStarted = -1L; + private LinkedHashMap<String, Long> myTaskTotals; + private String myCurrentTaskName; /** * Constructor @@ -54,6 +68,66 @@ public class StopWatch { myStarted = theStart.getTime(); } + private void ensureTaskTotalsMapExists() { + if (myTaskTotals == null) { + myTaskTotals = new LinkedHashMap<>(); + } + } + + /** + * Finish the counter on the current task (which was started by calling + * {@link #startTask(String)}. This method has no effect if no task + * is currently started so it's ok to call it more than once. + */ + public void endCurrentTask() { + if (isNotBlank(myCurrentTaskName)) { + ensureTaskTotalsMapExists(); + Long existingTotal = myTaskTotals.get(myCurrentTaskName); + long taskTimeElapsed = now() - myCurrentTaskStarted; + Long newTotal = existingTotal != null ? existingTotal + taskTimeElapsed : taskTimeElapsed; + myTaskTotals.put(myCurrentTaskName, newTotal); + } + myCurrentTaskName = null; + } + + /** + * Returns a string providing the durations of all tasks collected by {@link #startTask(String)} + */ + public String formatTaskDurations() { + + // Flush the current task if it's ongoing + String continueTask = myCurrentTaskName; + if (isNotBlank(myCurrentTaskName)) { + endCurrentTask(); + startTask(continueTask); + } + + ensureTaskTotalsMapExists(); + StringBuilder b = new StringBuilder(); + for (String nextTask : myTaskTotals.keySet()) { + if (b.length() > 0) { + b.append("\n"); + } + + b.append(nextTask); + b.append(": "); + b.append(formatMillis(myTaskTotals.get(nextTask))); + } + + return b.toString(); + } + + /** + * Determine the current throughput per unit of time (specified in theUnit) + * assuming that theNumOperations operations have happened. + * <p> + * For example, if this stopwatch has 2 seconds elapsed, and this method is + * called for theNumOperations=30 and TimeUnit=SECONDS, + * this method will return 15 + * </p> + * + * @see #getThroughput(int, TimeUnit) + */ public String formatThroughput(int theNumOperations, TimeUnit theUnit) { double throughput = getThroughput(theNumOperations, theUnit); return new DecimalFormat("0.0").format(throughput); @@ -99,6 +173,17 @@ public class StopWatch { return new Date(myStarted); } + /** + * Determine the current throughput per unit of time (specified in theUnit) + * assuming that theNumOperations operations have happened. + * <p> + * For example, if this stopwatch has 2 seconds elapsed, and this method is + * called for theNumOperations=30 and TimeUnit=SECONDS, + * this method will return 15 + * </p> + * + * @see #formatThroughput(int, TimeUnit) + */ public double getThroughput(int theNumOperations, TimeUnit theUnit) { if (theNumOperations <= 0) { return 0.0f; @@ -117,6 +202,23 @@ public class StopWatch { myStarted = now(); } + /** + * Starts a counter for a sub-task + * <p> + * <b>Thread Safety Note: </b> This method is not threadsafe! Do not use subtasks in a + * multithreaded environment. + * </p> + * + * @param theTaskName Note that if theTaskName is blank or empty, no task is started + */ + public void startTask(String theTaskName) { + endCurrentTask(); + if (isNotBlank(theTaskName)) { + myCurrentTaskStarted = now(); + } + myCurrentTaskName = theTaskName; + } + /** * Formats value in an appropriate format. See {@link #formatMillis(long)}} * for a description of the format diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StopWatchTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StopWatchTest.java index 19a234706f5..45a854094f5 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StopWatchTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/util/StopWatchTest.java @@ -103,17 +103,32 @@ public class StopWatchTest { assertEquals("00:01:00.000", StopWatch.formatMillis(DateUtils.MILLIS_PER_MINUTE)); assertEquals("00:01:01", StopWatch.formatMillis(DateUtils.MILLIS_PER_MINUTE + DateUtils.MILLIS_PER_SECOND)); assertEquals("01:00:00", StopWatch.formatMillis(DateUtils.MILLIS_PER_HOUR)); - assertEquals("1.0 day", StopWatch.formatMillis(DateUtils.MILLIS_PER_DAY)); - assertEquals("2.0 days", StopWatch.formatMillis(DateUtils.MILLIS_PER_DAY * 2)); - assertEquals("2.0 days", StopWatch.formatMillis((DateUtils.MILLIS_PER_DAY * 2) + 1)); - assertEquals("2.4 days", StopWatch.formatMillis((DateUtils.MILLIS_PER_DAY * 2) + (10 * DateUtils.MILLIS_PER_HOUR))); + assertEquals("1.0 day", StopWatch.formatMillis(DateUtils.MILLIS_PER_DAY).replace(',', '.')); + assertEquals("2.0 days", StopWatch.formatMillis(DateUtils.MILLIS_PER_DAY * 2).replace(',', '.')); + assertEquals("2.0 days", StopWatch.formatMillis((DateUtils.MILLIS_PER_DAY * 2) + 1).replace(',', '.')); + assertEquals("2.4 days", StopWatch.formatMillis((DateUtils.MILLIS_PER_DAY * 2) + (10 * DateUtils.MILLIS_PER_HOUR)).replace(',', '.')); assertEquals("11 days", StopWatch.formatMillis((DateUtils.MILLIS_PER_DAY * 11) + (10 * DateUtils.MILLIS_PER_HOUR))); } + @Test + public void testFormatTaskDurations() { + StopWatch sw = new StopWatch(); + + StopWatch.setNowForUnitTestForUnitTest(1000L); + sw.startTask("TASK1"); + + StopWatch.setNowForUnitTestForUnitTest(1500L); + sw.startTask("TASK2"); + + StopWatch.setNowForUnitTestForUnitTest(1600L); + String taskDurations = sw.formatTaskDurations(); + assertEquals("TASK1: 500ms\nTASK2: 100ms", taskDurations); + } + @Test public void testFormatThroughput60Ops4Min() { StopWatch sw = new StopWatch(DateUtils.addMinutes(new Date(), -4)); - String throughput = sw.formatThroughput(60, TimeUnit.MINUTES); + String throughput = sw.formatThroughput(60, TimeUnit.MINUTES).replace(',', '.'); ourLog.info("{} operations in {}ms = {} ops / second", 60, sw.getMillis(), throughput); assertThat(throughput, oneOf("14.9", "15.0", "15.1", "14,9", "15,0", "15,1")); } diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java b/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java index e14bd2e7b7d..79f240d1547 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-app/src/main/java/ca/uhn/fhir/cli/UploadTerminologyCommand.java @@ -41,7 +41,7 @@ public class UploadTerminologyCommand extends BaseCommand { opt.setRequired(true); options.addOption(opt); - opt = new Option("u", "url", true, "The code system URL associated with this upload (e.g. " + IHapiTerminologyLoaderSvc.SCT_URL + ")"); + opt = new Option("u", "url", true, "The code system URL associated with this upload (e.g. " + IHapiTerminologyLoaderSvc.SCT_URI + ")"); opt.setRequired(false); options.addOption(opt); diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index 751eabb88d9..7511f23fd20 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -8,10 +8,9 @@ import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; -import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3; import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; -import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; @@ -24,11 +23,9 @@ import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.cors.CorsConfiguration; import javax.servlet.ServletException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -81,10 +78,10 @@ public class JpaServerDemo extends RestfulServer { systemProvider.add(myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class)); } else if (fhirVersion == FhirVersionEnum.DSTU3) { systemProvider.add(myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class)); - systemProvider.add(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class)); + systemProvider.add(myAppCtx.getBean(TerminologyUploaderProvider.class)); } else if (fhirVersion == FhirVersionEnum.R4) { systemProvider.add(myAppCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class)); - systemProvider.add(myAppCtx.getBean(TerminologyUploaderProviderR4.class)); + systemProvider.add(myAppCtx.getBean(TerminologyUploaderProvider.class)); } else { throw new IllegalStateException(); } diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index b9adc3031f0..f9abc66d9be 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -66,7 +66,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> + <argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> </configuration> </plugin> </plugins> diff --git a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java index 1284efca1e2..d4828837d1c 100644 --- a/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java +++ b/hapi-fhir-client/src/main/java/ca/uhn/fhir/rest/client/impl/GenericClient.java @@ -2105,14 +2105,14 @@ public class GenericClient extends BaseClient implements IGenericClient { @Override public IUpdateTyped resource(IBaseResource theResource) { - Validate.notNull(theResource, "Resource can not be null"); + //Validate.notNull(theResource, "Resource can not be null"); myResource = theResource; return this; } @Override public IUpdateTyped resource(String theResourceBody) { - Validate.notBlank(theResourceBody, "Body can not be null or blank"); + //Validate.notBlank(theResourceBody, "Body can not be null or blank"); myResourceBody = theResourceBody; return this; } diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java index 8178c393418..04c4c03a725 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java @@ -34,8 +34,6 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider; -import org.hl7.fhir.dstu3.model.CapabilityStatement; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.LoggerFactory; @@ -67,10 +65,10 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv /** the conformance. It is created once during startup */ private org.hl7.fhir.r4.model.CapabilityStatement myR4CapabilityStatement; - private CapabilityStatement myDstu3CapabilityStatement; + private org.hl7.fhir.dstu3.model.CapabilityStatement myDstu3CapabilityStatement; private org.hl7.fhir.dstu2016may.model.Conformance myDstu2_1Conformance; - private ca.uhn.fhir.model.dstu2.resource.Conformance myDstu2Conformance; private org.hl7.fhir.instance.model.Conformance myDstu2Hl7OrgConformance; + private ca.uhn.fhir.model.dstu2.resource.Conformance myDstu2Conformance; /** * Constructor allowing the description, servername and server to be set @@ -127,26 +125,35 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv HardcodedServerAddressStrategy hardcodedServerAddressStrategy = new HardcodedServerAddressStrategy(); hardcodedServerAddressStrategy.setValue(getBaseForServer()); serverConfiguration.setServerAddressStrategy(hardcodedServerAddressStrategy); - if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.R4)) { - org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider serverCapabilityStatementProvider = new org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider(serverConfiguration); - serverCapabilityStatementProvider.initializeOperations(); - myR4CapabilityStatement = serverCapabilityStatementProvider.getServerConformance(null); - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) { - ServerCapabilityStatementProvider serverCapabilityStatementProvider = new ServerCapabilityStatementProvider(serverConfiguration); - serverCapabilityStatementProvider.initializeOperations(); - myDstu3CapabilityStatement = serverCapabilityStatementProvider.getServerConformance(null); - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_1)) { - org.hl7.fhir.dstu2016may.hapi.rest.server.ServerConformanceProvider serverCapabilityStatementProvider = new org.hl7.fhir.dstu2016may.hapi.rest.server.ServerConformanceProvider(serverConfiguration); - serverCapabilityStatementProvider.initializeOperations(); - myDstu2_1Conformance = serverCapabilityStatementProvider.getServerConformance(null); - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2)) { - ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider serverCapabilityStatementProvider = new ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider(serverConfiguration); - serverCapabilityStatementProvider.initializeOperations(); - myDstu2Conformance = serverCapabilityStatementProvider.getServerConformance(null); - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) { - org.hl7.fhir.instance.conf.ServerConformanceProvider serverCapabilityStatementProvider = new org.hl7.fhir.instance.conf.ServerConformanceProvider(serverConfiguration); - serverCapabilityStatementProvider.initializeOperations(); - myDstu2Hl7OrgConformance = serverCapabilityStatementProvider.getServerConformance(null); + FhirVersionEnum fhirContextVersion = super.getFhirContext().getVersion().getVersion(); + switch (fhirContextVersion) { + case R4: + org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider r4ServerCapabilityStatementProvider = new org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider(serverConfiguration); + r4ServerCapabilityStatementProvider.initializeOperations(); + myR4CapabilityStatement = r4ServerCapabilityStatementProvider.getServerConformance(null); + break; + case DSTU3: + org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider dstu3ServerCapabilityStatementProvider = new org.hl7.fhir.dstu3.hapi.rest.server.ServerCapabilityStatementProvider(serverConfiguration); + dstu3ServerCapabilityStatementProvider.initializeOperations(); + myDstu3CapabilityStatement = dstu3ServerCapabilityStatementProvider.getServerConformance(null); + break; + case DSTU2_1: + org.hl7.fhir.dstu2016may.hapi.rest.server.ServerConformanceProvider dstu2_1ServerConformanceProvider = new org.hl7.fhir.dstu2016may.hapi.rest.server.ServerConformanceProvider(serverConfiguration); + dstu2_1ServerConformanceProvider.initializeOperations(); + myDstu2_1Conformance = dstu2_1ServerConformanceProvider.getServerConformance(null); + break; + case DSTU2_HL7ORG: + org.hl7.fhir.instance.conf.ServerConformanceProvider dstu2Hl7OrgServerConformanceProvider = new org.hl7.fhir.instance.conf.ServerConformanceProvider(serverConfiguration); + dstu2Hl7OrgServerConformanceProvider.initializeOperations(); + myDstu2Hl7OrgConformance = dstu2Hl7OrgServerConformanceProvider.getServerConformance(null); + break; + case DSTU2: + ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider dstu2ServerConformanceProvider = new ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider(serverConfiguration); + dstu2ServerConformanceProvider.initializeOperations(); + myDstu2Conformance = dstu2ServerConformanceProvider.getServerConformance(null); + break; + default: + throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion); } } @@ -181,20 +188,26 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv IRestfulResponse response = request.build().getResponse(); response.addHeader(Constants.HEADER_CORS_ALLOW_ORIGIN, "*"); - IBaseResource conformance = null; - if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.R4)) { - conformance = myR4CapabilityStatement; -// return (Response) response.returnResponse(ParseAction.create(myDstu3CapabilityStatement), Constants.STATUS_HTTP_200_OK, true, null, getResourceType().getSimpleName()); - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) { - conformance = myDstu3CapabilityStatement; -// return (Response) response.returnResponse(ParseAction.create(myDstu3CapabilityStatement), Constants.STATUS_HTTP_200_OK, true, null, getResourceType().getSimpleName()); - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_1)) { - conformance = myDstu2_1Conformance; - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2)) { - conformance = myDstu2Conformance; -// return (Response) response.returnResponse(ParseAction.create(myDstu2CapabilityStatement), Constants.STATUS_HTTP_200_OK, true, null, getResourceType().getSimpleName()); - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) { - conformance = myDstu2Hl7OrgConformance; + IBaseResource conformance; + FhirVersionEnum fhirContextVersion = super.getFhirContext().getVersion().getVersion(); + switch (fhirContextVersion) { + case R4: + conformance = myR4CapabilityStatement; + break; + case DSTU3: + conformance = myDstu3CapabilityStatement; + break; + case DSTU2_1: + conformance = myDstu2_1Conformance; + break; + case DSTU2_HL7ORG: + conformance = myDstu2Hl7OrgConformance; + break; + case DSTU2: + conformance = myDstu2Conformance; + break; + default: + throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion); } if (conformance != null) { @@ -279,18 +292,21 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv @SuppressWarnings("unchecked") @Override public Class<IBaseResource> getResourceType() { - if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.R4)) { - return Class.class.cast(org.hl7.fhir.r4.model.CapabilityStatement.class); - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) { - return Class.class.cast(CapabilityStatement.class); - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_1)) { - return Class.class.cast(org.hl7.fhir.dstu2016may.model.Conformance.class); - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2)) { - return Class.class.cast(ca.uhn.fhir.model.dstu2.resource.Conformance.class); - } else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) { - return Class.class.cast(org.hl7.fhir.instance.model.Conformance.class); + FhirVersionEnum fhirContextVersion = super.getFhirContext().getVersion().getVersion(); + switch (fhirContextVersion) { + case R4: + return Class.class.cast(org.hl7.fhir.r4.model.CapabilityStatement.class); + case DSTU3: + return Class.class.cast(org.hl7.fhir.dstu3.model.CapabilityStatement.class); + case DSTU2_1: + return Class.class.cast(org.hl7.fhir.dstu2016may.model.Conformance.class); + case DSTU2_HL7ORG: + return Class.class.cast(org.hl7.fhir.instance.model.Conformance.class); + case DSTU2: + return Class.class.cast(ca.uhn.fhir.model.dstu2.resource.Conformance.class); + default: + throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion); } - return null; } } diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java index b545cd7cf75..35d1a6f0e7c 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/util/JaxRsRequest.java @@ -28,12 +28,11 @@ import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.dstu3.model.IdType; +import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider; -import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.*; import ca.uhn.fhir.rest.api.server.IRestfulResponse; import ca.uhn.fhir.rest.api.server.RequestDetails; @@ -93,28 +92,68 @@ public class JaxRsRequest extends RequestDetails { FhirVersionEnum fhirContextVersion = myServer.getFhirContext().getVersion().getVersion(); if (StringUtils.isNotBlank(myVersion)) { - if (FhirVersionEnum.DSTU3.equals(fhirContextVersion) || FhirVersionEnum.DSTU2_HL7ORG.equals(fhirContextVersion)) { - result.setId( - new IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); - } else if (FhirVersionEnum.DSTU2.equals(fhirContextVersion)) { - result.setId( - new IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); - } + switch (fhirContextVersion) { + case R4: + result.setId(new org.hl7.fhir.r4.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); + break; + case DSTU3: + result.setId(new org.hl7.fhir.dstu3.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); + break; + case DSTU2_1: + result.setId(new org.hl7.fhir.dstu2016may.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); + break; + case DSTU2_HL7ORG: + result.setId(new org.hl7.fhir.instance.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); + break; + case DSTU2: + result.setId(new ca.uhn.fhir.model.primitive.IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion))); + break; + default: + throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion); + } } else if (StringUtils.isNotBlank(myId)) { - if (FhirVersionEnum.DSTU3.equals(fhirContextVersion) || FhirVersionEnum.DSTU2_HL7ORG.equals(fhirContextVersion)) { - result.setId(new IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); - } else if (FhirVersionEnum.DSTU2.equals(fhirContextVersion)) { - result.setId(new IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); - } + switch (fhirContextVersion) { + case R4: + result.setId(new org.hl7.fhir.r4.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); + break; + case DSTU3: + result.setId(new org.hl7.fhir.dstu3.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); + break; + case DSTU2_1: + result.setId(new org.hl7.fhir.dstu2016may.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); + break; + case DSTU2_HL7ORG: + result.setId(new org.hl7.fhir.instance.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); + break; + case DSTU2: + result.setId(new ca.uhn.fhir.model.primitive.IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId))); + break; + default: + throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion); + } } if (myRestOperation == RestOperationTypeEnum.UPDATE) { String contentLocation = result.getHeader(Constants.HEADER_CONTENT_LOCATION); if (contentLocation != null) { - if (FhirVersionEnum.DSTU3.equals(fhirContextVersion) || FhirVersionEnum.DSTU2_HL7ORG.equals(fhirContextVersion)) { - result.setId(new IdType(contentLocation)); - } else if (FhirVersionEnum.DSTU2.equals(fhirContextVersion)) { - result.setId(new IdDt(contentLocation)); + switch (fhirContextVersion) { + case R4: + result.setId(new org.hl7.fhir.r4.model.IdType(contentLocation)); + break; + case DSTU3: + result.setId(new org.hl7.fhir.dstu3.model.IdType(contentLocation)); + break; + case DSTU2_1: + result.setId(new org.hl7.fhir.dstu2016may.model.IdType(contentLocation)); + break; + case DSTU2_HL7ORG: + result.setId(new org.hl7.fhir.instance.model.IdType(contentLocation)); + break; + case DSTU2: + result.setId(new ca.uhn.fhir.model.primitive.IdDt(contentLocation)); + break; + default: + throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion); } } } diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index bbe19c60c6f..7fdfe61324c 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -600,7 +600,7 @@ <artifactId>maven-surefire-plugin</artifactId> <configuration> <runOrder>alphabetical</runOrder> - <argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx1024m</argLine> + <argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx1024m</argLine> <forkCount>0.6C</forkCount> </configuration> </plugin> diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java index ab882885774..a01dd043e01 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java @@ -9,7 +9,7 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; import ca.uhn.fhir.jpa.dao.ISearchParamRegistry; import ca.uhn.fhir.jpa.dao.dstu3.SearchParamExtractorDstu3; import ca.uhn.fhir.jpa.dao.dstu3.SearchParamRegistryDstu3; -import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.term.*; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3; import ca.uhn.fhir.validation.IValidatorModule; @@ -115,8 +115,8 @@ public class BaseDstu3Config extends BaseConfig { } @Bean(autowire = Autowire.BY_TYPE) - public TerminologyUploaderProviderDstu3 terminologyUploaderProvider() { - TerminologyUploaderProviderDstu3 retVal = new TerminologyUploaderProviderDstu3(); + public TerminologyUploaderProvider terminologyUploaderProvider() { + TerminologyUploaderProvider retVal = new TerminologyUploaderProvider(); retVal.setContext(fhirContextDstu3()); return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java index c9ff4f94920..6a0d5cc286b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java @@ -10,7 +10,7 @@ import ca.uhn.fhir.jpa.dao.ISearchParamRegistry; import ca.uhn.fhir.jpa.dao.r4.SearchParamExtractorR4; import ca.uhn.fhir.jpa.dao.r4.SearchParamRegistryR4; import ca.uhn.fhir.jpa.graphql.JpaStorageServices; -import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.term.HapiTerminologySvcR4; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IHapiTerminologySvcR4; @@ -133,8 +133,8 @@ public class BaseR4Config extends BaseConfig { } @Bean(autowire = Autowire.BY_TYPE) - public TerminologyUploaderProviderR4 terminologyUploaderProvider() { - TerminologyUploaderProviderR4 retVal = new TerminologyUploaderProviderR4(); + public TerminologyUploaderProvider terminologyUploaderProvider() { + TerminologyUploaderProvider retVal = new TerminologyUploaderProvider(); retVal.setContext(fhirContextR4()); return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java index 5e2e3096965..fb97650f2f6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoValueSetDstu2.java @@ -194,7 +194,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet> retVal.setSearchedForSystem(system); retVal.setFound(true); if (nextCode.getAbstract() != null) { - retVal.setCodeIsAbstract(nextCode.getAbstract().booleanValue()); + retVal.setCodeIsAbstract(nextCode.getAbstract()); } retVal.setCodeDisplay(nextCode.getDisplay()); retVal.setCodeSystemVersion(nextCode.getVersion()); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java index b3722291fe9..1a2b12b3210 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java @@ -1,13 +1,12 @@ package ca.uhn.fhir.jpa.dao; +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; -import org.hl7.fhir.r4.model.BooleanType; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r4.model.*; import java.util.List; @@ -48,6 +47,8 @@ public interface IFhirResourceDaoCodeSystem<T extends IBaseResource, CD, CC> ext private boolean myFound; private String mySearchedForCode; private String mySearchedForSystem; + private List<IContextValidationSupport.BaseConceptProperty> myProperties; + /** * Constructor */ @@ -111,6 +112,10 @@ public interface IFhirResourceDaoCodeSystem<T extends IBaseResource, CD, CC> ext myFound = theFound; } + public void setProperties(List<IContextValidationSupport.BaseConceptProperty> theProperties) { + myProperties = theProperties; + } + public void throwNotFoundIfAppropriate() { if (isFound() == false) { throw new ResourceNotFoundException("Unable to find code[" + getSearchedForCode() + "] in system[" + getSearchedForSystem() + "]"); @@ -127,6 +132,35 @@ public interface IFhirResourceDaoCodeSystem<T extends IBaseResource, CD, CC> ext retVal.addParameter().setName("display").setValue(new StringType(getCodeDisplay())); retVal.addParameter().setName("abstract").setValue(new BooleanType(isCodeIsAbstract())); + if (myProperties != null) { + for (IContextValidationSupport.BaseConceptProperty next : myProperties) { + Parameters.ParametersParameterComponent property = retVal.addParameter().setName("property"); + property + .addPart() + .setName("code") + .setValue(new CodeType(next.getPropertyName())); + + if (next instanceof IContextValidationSupport.StringConceptProperty) { + IContextValidationSupport.StringConceptProperty prop = (IContextValidationSupport.StringConceptProperty) next; + property + .addPart() + .setName("value") + .setValue(new StringType(prop.getValue())); + } else if (next instanceof IContextValidationSupport.CodingConceptProperty) { + IContextValidationSupport.CodingConceptProperty prop = (IContextValidationSupport.CodingConceptProperty) next; + property + .addPart() + .setName("value") + .setValue(new Coding() + .setSystem(prop.getCodeSystem()) + .setCode(prop.getCode()) + .setDisplay(prop.getDisplay())); + } else { + throw new IllegalStateException("Don't know how to handle " + next.getClass()); + } + } + } + return retVal; } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java new file mode 100644 index 00000000000..c9f8ed09029 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java @@ -0,0 +1,37 @@ +package ca.uhn.fhir.jpa.dao.data; + +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.entity.TermConceptProperty; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public interface ITermConceptPropertyDao extends JpaRepository<TermConceptProperty, Long> { + // nothing +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index e66995fb1d7..55fc51cec67 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -52,6 +52,7 @@ import java.util.Date; import java.util.List; import java.util.Set; +import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> { @@ -98,7 +99,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem) { List<IIdType> valueSetIds; Set<Long> ids = searchForIds(new SearchParameterMap(CodeSystem.SP_CODE, new TokenParam(theSystem, theCode))); - valueSetIds = new ArrayList<IIdType>(); + valueSetIds = new ArrayList<>(); for (Long next : ids) { valueSetIds.add(new IdType("CodeSystem", next)); } @@ -128,11 +129,11 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys system = theSystem.getValue(); } - ourLog.info("Looking up {} / {}", system, code); + ourLog.debug("Looking up {} / {}", system, code); if (myValidationSupport.isCodeSystemSupported(getContext(), system)) { - ourLog.info("Code system {} is supported", system); + ourLog.debug("Code system {} is supported", system); CodeValidationResult result = myValidationSupport.validateCode(getContext(), system, code, null); if (result != null) { @@ -142,32 +143,20 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys retVal.setSearchedForCode(code); retVal.setSearchedForSystem(system); retVal.setCodeDisplay(result.asConceptDefinition().getDisplay()); - retVal.setCodeSystemDisplayName("Unknown"); - retVal.setCodeSystemVersion(""); + + String codeSystemDisplayName = result.getCodeSystemName(); + if (isBlank(codeSystemDisplayName)) { + codeSystemDisplayName = "Unknown"; + } + + retVal.setCodeSystemDisplayName(codeSystemDisplayName); + retVal.setCodeSystemVersion(result.getCodeSystemVersion()); + retVal.setProperties(result.getProperties()); + return retVal; } } -// HapiWorkerContext ctx = new HapiWorkerContext(getContext(), myValidationSupport); -// ValueSetExpander expander = ctx.getExpander(); -// ValueSet source = new ValueSet(); -// source.getCompose().addInclude().setSystem(system).addConcept().setCode(code); -// -// ValueSetExpansionOutcome expansion; -// try { -// expansion = expander.expand(source); -// } catch (Exception e) { -// throw new InternalErrorException(e); -// } -// -// if (expansion.getValueset() != null) { -// List<ValueSetExpansionContainsComponent> contains = expansion.getValueset().getExpansion().getContains(); -// LookupCodeResult result = lookup(contains, system, code); -// if (result != null) { -// return result; -// } -// } - } // We didn't find it.. @@ -199,7 +188,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys if (isNotBlank(next.getCode())) { TermConcept termConcept = new TermConcept(); termConcept.setCode(next.getCode()); - termConcept.setCodeSystem(theCodeSystemVersion); + termConcept.setCodeSystemVersion(theCodeSystemVersion); termConcept.setDisplay(next.getDisplay()); termConcept.addChildren(toPersistedConcepts(next.getConcept(), theCodeSystemVersion), RelationshipTypeEnum.ISA); retVal.add(termConcept); @@ -229,7 +218,7 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys persCs.setResource(retVal); persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs)); ourLog.info("Code system has {} concepts", persCs.getConcepts().size()); - myTerminologySvc.storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, persCs); + myTerminologySvc.storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, cs.getName(), persCs); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSubscriptionDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSubscriptionDstu3.java index 40cb9a0e9d2..5ca5a027271 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSubscriptionDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSubscriptionDstu3.java @@ -99,11 +99,7 @@ public class FhirResourceDaoSubscriptionDstu3 extends FhirResourceDaoDstu3<Subsc } protected void validateChannelPayload(Subscription theResource) { - if (isBlank(theResource.getChannel().getPayload())) { - throw new UnprocessableEntityException("Subscription.channel.payload must be populated for rest-hook subscriptions"); - } - - if (EncodingEnum.forContentType(theResource.getChannel().getPayload()) == null) { + if (!isBlank(theResource.getChannel().getPayload()) && EncodingEnum.forContentType(theResource.getChannel().getPayload()) == null) { throw new UnprocessableEntityException("Invalid value for Subscription.channel.payload: " + theResource.getChannel().getPayload()); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index 64ccf5b564d..43dc130ee64 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -32,8 +32,6 @@ import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.rest.param.ParameterUtil; import org.apache.commons.lang3.Validate; import org.apache.http.NameValuePair; -import org.hibernate.Session; -import org.hibernate.internal.SessionImpl; import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.dstu3.model.Bundle.*; import org.hl7.fhir.dstu3.model.OperationOutcome.IssueSeverity; @@ -150,7 +148,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> { } if (transactionType == null) { - String message = "Transactiion Bundle did not specify valid Bundle.type, assuming " + BundleType.TRANSACTION.toCode(); + String message = "Transaction Bundle did not specify valid Bundle.type, assuming " + BundleType.TRANSACTION.toCode(); ourLog.warn(message); transactionType = BundleType.TRANSACTION; } @@ -158,9 +156,10 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> { throw new InvalidRequestException("Unable to process transaction where incoming Bundle.type = " + transactionType.toCode()); } - ourLog.info("Beginning {} with {} resources", theActionName, theRequest.getEntry().size()); + ourLog.debug("Beginning {} with {} resources", theActionName, theRequest.getEntry().size()); - long start = System.currentTimeMillis(); +// long start = System.currentTimeMillis(); + final StopWatch transactionSw = new StopWatch(); final Date updateTime = new Date(); final Set<IdType> allIds = new LinkedHashSet<IdType>(); @@ -202,7 +201,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> { * Basically if the resource has a match URL that references a placeholder, * we try to handle the resource with the placeholder first. */ - Set<String> placeholderIds = new HashSet<String>(); + Set<String> placeholderIds = new HashSet<>(); final List<BundleEntryComponent> entries = theRequest.getEntry(); for (BundleEntryComponent nextEntry : entries) { if (isNotBlank(nextEntry.getFullUrl()) && nextEntry.getFullUrl().startsWith(IdType.URN_PREFIX)) { @@ -224,7 +223,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> { Map<BundleEntryComponent, ResourceTable> entriesToProcess = txManager.execute(new TransactionCallback<Map<BundleEntryComponent, ResourceTable>>() { @Override public Map<BundleEntryComponent, ResourceTable> doInTransaction(TransactionStatus status) { - return doTransactionWriteOperations(theRequestDetails, theRequest, theActionName, updateTime, allIds, idSubstitutions, idToPersistedOutcome, response, originalRequestOrder, entries); + return doTransactionWriteOperations(theRequestDetails, theRequest, theActionName, updateTime, allIds, idSubstitutions, idToPersistedOutcome, response, originalRequestOrder, entries, transactionSw); } }); for (Entry<BundleEntryComponent, ResourceTable> nextEntry : entriesToProcess.entrySet()) { @@ -237,8 +236,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> { /* * Loop through the request and process any entries of type GET */ - for (int i = 0; i < getEntries.size(); i++) { - BundleEntryComponent nextReqEntry = getEntries.get(i); + for (BundleEntryComponent nextReqEntry : getEntries) { Integer originalOrder = originalRequestOrder.get(nextReqEntry); BundleEntryComponent nextRespEntry = response.getEntry().get(originalOrder); @@ -258,7 +256,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> { for (NameValuePair next : parameters) { paramValues.put(next.getName(), next.getValue()); } - for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) { + for (Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) { String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]); requestDetails.addParameter(nextParamEntry.getKey(), nextValue); } @@ -302,8 +300,8 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> { } - long delay = System.currentTimeMillis() - start; - ourLog.info(theActionName + " completed in {}ms", new Object[] { delay }); + ourLog.info(theActionName + " completed in {}", transactionSw.toString()); + ourLog.info(theActionName + " details:\n{}", transactionSw.formatTaskDurations()); response.setType(BundleType.TRANSACTIONRESPONSE); return response; @@ -311,12 +309,12 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> { @SuppressWarnings("unchecked") private Map<BundleEntryComponent, ResourceTable> doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date updateTime, Set<IdType> allIds, - Map<IdType, IdType> theIdSubstitutions, Map<IdType, DaoMethodOutcome> idToPersistedOutcome, Bundle response, IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder, List<BundleEntryComponent> theEntries) { - Set<String> deletedResources = new HashSet<String>(); - List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>(); - Map<BundleEntryComponent, ResourceTable> entriesToProcess = new IdentityHashMap<BundleEntryComponent, ResourceTable>(); - Set<ResourceTable> nonUpdatedEntities = new HashSet<ResourceTable>(); - Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<String, Class<? extends IBaseResource>>(); + Map<IdType, IdType> theIdSubstitutions, Map<IdType, DaoMethodOutcome> idToPersistedOutcome, Bundle response, IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder, List<BundleEntryComponent> theEntries, StopWatch theStopWatch) { + Set<String> deletedResources = new HashSet<>(); + List<DeleteConflict> deleteConflicts = new ArrayList<>(); + Map<BundleEntryComponent, ResourceTable> entriesToProcess = new IdentityHashMap<>(); + Set<ResourceTable> nonUpdatedEntities = new HashSet<>(); + Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<>(); /* * Loop through the request and process any entries of type @@ -371,6 +369,8 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> { String resourceType = res != null ? getContext().getResourceDefinition(res).getName() : null; BundleEntryComponent nextRespEntry = response.getEntry().get(originalRequestOrder.get(nextReqEntry)); + theStopWatch.startTask("Process entry " + i + ": " + verb + " " + defaultString(resourceType)); + switch (verb) { case POST: { // CREATE @@ -470,6 +470,8 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> { } } + theStopWatch.endCurrentTask(); + /* * Make sure that there are no conflicts from deletions. E.g. we can't delete something * if something else has a reference to it.. Unless the thing that has a reference to it @@ -538,8 +540,12 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> { } } + theStopWatch.startTask("Flush Session"); + flushJpaSession(); + theStopWatch.endCurrentTask(); + /* * Double check we didn't allow any duplicates we shouldn't have */ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java index 5613d4c9a45..030683b562e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java @@ -52,6 +52,7 @@ import java.util.Date; import java.util.List; import java.util.Set; +import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> { @@ -67,30 +68,6 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> i @Autowired private ValidationSupportChain myValidationSupport; -// private LookupCodeResult lookup(List<ValueSetExpansionContainsComponent> theContains, String theSystem, String theCode) { -// for (ValueSetExpansionContainsComponent nextCode : theContains) { -// -// String system = nextCode.getSystem(); -// String code = nextCode.getCode(); -// if (theSystem.equals(system) && theCode.equals(code)) { -// LookupCodeResult retVal = new LookupCodeResult(); -// retVal.setSearchedForCode(code); -// retVal.setSearchedForSystem(system); -// retVal.setFound(true); -// if (nextCode.getAbstractElement().getValue() != null) { -// retVal.setCodeIsAbstract(nextCode.getAbstractElement().booleanValue()); -// } -// retVal.setCodeDisplay(nextCode.getDisplay()); -// retVal.setCodeSystemVersion(nextCode.getVersion()); -// retVal.setCodeSystemDisplayName("Unknown"); // TODO: implement -// return retVal; -// } -// -// } -// -// return null; -// } - @Override public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem) { List<IIdType> valueSetIds; @@ -139,32 +116,20 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> i retVal.setSearchedForCode(code); retVal.setSearchedForSystem(system); retVal.setCodeDisplay(result.asConceptDefinition().getDisplay()); - retVal.setCodeSystemDisplayName("Unknown"); - retVal.setCodeSystemVersion(""); + + String codeSystemDisplayName = result.getCodeSystemName(); + if (isBlank(codeSystemDisplayName)) { + codeSystemDisplayName = "Unknown"; + } + + retVal.setCodeSystemDisplayName(codeSystemDisplayName); + retVal.setCodeSystemVersion(result.getCodeSystemVersion()); + retVal.setProperties(result.getProperties()); + return retVal; } } -// HapiWorkerContext ctx = new HapiWorkerContext(getContext(), myValidationSupport); -// ValueSetExpander expander = ctx.getExpander(); -// ValueSet source = new ValueSet(); -// source.getCompose().addInclude().setSystem(system).addConcept().setCode(code); -// -// ValueSetExpansionOutcome expansion; -// try { -// expansion = expander.expand(source); -// } catch (Exception e) { -// throw new InternalErrorException(e); -// } -// -// if (expansion.getValueset() != null) { -// List<ValueSetExpansionContainsComponent> contains = expansion.getValueset().getExpansion().getContains(); -// LookupCodeResult result = lookup(contains, system, code); -// if (result != null) { -// return result; -// } -// } - } // We didn't find it.. @@ -196,7 +161,7 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> i if (isNotBlank(next.getCode())) { TermConcept termConcept = new TermConcept(); termConcept.setCode(next.getCode()); - termConcept.setCodeSystem(theCodeSystemVersion); + termConcept.setCodeSystemVersion(theCodeSystemVersion); termConcept.setDisplay(next.getDisplay()); termConcept.addChildren(toPersistedConcepts(next.getConcept(), theCodeSystemVersion), RelationshipTypeEnum.ISA); retVal.add(termConcept); @@ -228,7 +193,7 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> i persCs.setResource(retVal); persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs)); ourLog.info("Code system has {} concepts", persCs.getConcepts().size()); - myTerminologySvc.storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, persCs); + myTerminologySvc.storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, cs.getName(), persCs); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java index 5fe3c8d8eaf..8fb7349189e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSubscriptionR4.java @@ -96,11 +96,7 @@ public class FhirResourceDaoSubscriptionR4 extends FhirResourceDaoR4<Subscriptio } protected void validateChannelPayload(Subscription theResource) { - if (isBlank(theResource.getChannel().getPayload())) { - throw new UnprocessableEntityException("Subscription.channel.payload must be populated for rest-hook subscriptions"); - } - - if (EncodingEnum.forContentType(theResource.getChannel().getPayload()) == null) { + if (!isBlank(theResource.getChannel().getPayload()) && EncodingEnum.forContentType(theResource.getChannel().getPayload()) == null) { throw new UnprocessableEntityException("Invalid value for Subscription.channel.payload: " + theResource.getChannel().getPayload()); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java index f4e2e05f1e4..d32621b087c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java @@ -157,7 +157,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> { } if (transactionType == null) { - String message = "Transactiion Bundle did not specify valid Bundle.type, assuming " + BundleType.TRANSACTION.toCode(); + String message = "Transaction Bundle did not specify valid Bundle.type, assuming " + BundleType.TRANSACTION.toCode(); ourLog.warn(message); transactionType = BundleType.TRANSACTION; } @@ -165,7 +165,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> { throw new InvalidRequestException("Unable to process transaction where incoming Bundle.type = " + transactionType.toCode()); } - ourLog.info("Beginning {} with {} resources", theActionName, theRequest.getEntry().size()); + ourLog.debug("Beginning {} with {} resources", theActionName, theRequest.getEntry().size()); long start = System.currentTimeMillis(); final Date updateTime = new Date(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java index 2b374fbfc13..b51192d8770 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java @@ -23,6 +23,8 @@ package ca.uhn.fhir.jpa.entity; import javax.persistence.*; import java.io.Serializable; +import static org.apache.commons.lang3.StringUtils.left; + //@formatter:off @Table(name = "TRM_CODESYSTEM", uniqueConstraints = { @UniqueConstraint(name = "IDX_CS_CODESYSTEM", columnNames = {"CODE_SYSTEM_URI"}) @@ -31,6 +33,7 @@ import java.io.Serializable; //@formatter:on public class TermCodeSystem implements Serializable { private static final long serialVersionUID = 1L; + public static final int CS_NAME_LENGTH = 200; @Column(name = "CODE_SYSTEM_URI", nullable = false) private String myCodeSystemUri; @@ -48,11 +51,17 @@ public class TermCodeSystem implements Serializable { private ResourceTable myResource; @Column(name = "RES_ID", insertable = false, updatable = false) private Long myResourcePid; + @Column(name = "CS_NAME", nullable = true) + private String myName; public String getCodeSystemUri() { return myCodeSystemUri; } + public String getName() { + return myName; + } + public void setCodeSystemUri(String theCodeSystemUri) { myCodeSystemUri = theCodeSystemUri; } @@ -73,6 +82,10 @@ public class TermCodeSystem implements Serializable { return myResource; } + public void setName(String theName) { + myName = left(theName, CS_NAME_LENGTH); + } + public void setResource(ResourceTable theResource) { myResource = theResource; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java index c91606fcb40..4197c51121a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,28 +20,15 @@ package ca.uhn.fhir.jpa.entity; * #L% */ +import ca.uhn.fhir.util.CoverageIgnore; + +import javax.persistence.*; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.FetchType; -import javax.persistence.ForeignKey; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.UniqueConstraint; - -import ca.uhn.fhir.util.CoverageIgnore; - //@formatter:off -@Table(name="TRM_CODESYSTEM_VER" +@Table(name = "TRM_CODESYSTEM_VER" // Note, we used to have a constraint named IDX_CSV_RESOURCEPID_AND_VER (don't reuse this) ) @Entity() @@ -54,17 +41,23 @@ public class TermCodeSystemVersion implements Serializable { @Id() @SequenceGenerator(name = "SEQ_CODESYSTEMVER_PID", sequenceName = "SEQ_CODESYSTEMVER_PID") - @GeneratedValue(strategy=GenerationType.AUTO, generator="SEQ_CODESYSTEMVER_PID") + @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CODESYSTEMVER_PID") @Column(name = "PID") private Long myId; @OneToOne() - @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey=@ForeignKey(name="FK_CODESYSVER_RES_ID")) + @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_CODESYSVER_RES_ID")) private ResourceTable myResource; @Column(name = "CS_VERSION_ID", nullable = true, updatable = false) private String myCodeSystemVersionId; - + /** + * This was added in HAPI FHIR 3.3.0 and is nullable just to avoid migration + * issued. It should be made non-nullable at some point. + */ + @ManyToOne + @JoinColumn(name = "CODESYSTEM_PID", referencedColumnName = "PID", nullable = true) + private TermCodeSystem myCodeSystem; @SuppressWarnings("unused") @OneToOne(mappedBy = "myCurrentVersion", optional = true) private TermCodeSystem myCodeSystemHavingThisVersionAsCurrentVersionIfAny; @@ -76,26 +69,6 @@ public class TermCodeSystemVersion implements Serializable { super(); } - public Collection<TermConcept> getConcepts() { - if (myConcepts == null) { - myConcepts = new ArrayList<>(); - } - return myConcepts; - } - - public Long getPid() { - return myId; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((myResource.getId() == null) ? 0 : myResource.getId().hashCode()); - result = prime * result + ((myCodeSystemVersionId == null) ? 0 : myCodeSystemVersionId.hashCode()); - return result; - } - @CoverageIgnore @Override public boolean equals(Object obj) { @@ -114,7 +87,7 @@ public class TermCodeSystemVersion implements Serializable { } else if (!myResource.getId().equals(other.myResource.getId())) { return false; } - + if (myCodeSystemVersionId == null) { if (other.myCodeSystemVersionId != null) { return false; @@ -125,20 +98,48 @@ public class TermCodeSystemVersion implements Serializable { return true; } - public ResourceTable getResource() { - return myResource; + public TermCodeSystem getCodeSystem() { + return myCodeSystem; + } + + public void setCodeSystem(TermCodeSystem theCodeSystem) { + myCodeSystem = theCodeSystem; } public String getCodeSystemVersionId() { return myCodeSystemVersionId; } - public void setResource(ResourceTable theResource) { - myResource = theResource; - } - public void setCodeSystemVersionId(String theCodeSystemVersionId) { myCodeSystemVersionId = theCodeSystemVersionId; } + public Collection<TermConcept> getConcepts() { + if (myConcepts == null) { + myConcepts = new ArrayList<>(); + } + return myConcepts; + } + + public Long getPid() { + return myId; + } + + public ResourceTable getResource() { + return myResource; + } + + public void setResource(ResourceTable theResource) { + myResource = theResource; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((myResource.getId() == null) ? 0 : myResource.getId().hashCode()); + result = prime * result + ((myCodeSystemVersionId == null) ? 0 : myCodeSystemVersionId.hashCode()); + return result; + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java index 8a39a5178cf..ca2ef4ace87 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.entity; +import ca.uhn.fhir.context.support.IContextValidationSupport; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor; import org.apache.commons.lang3.Validate; @@ -75,7 +76,7 @@ public class TermConcept implements Serializable { }) private String myDisplay; - @OneToMany(mappedBy = "myConcept") + @OneToMany(mappedBy = "myConcept", orphanRemoval = true) @Field @FieldBridge(impl = TermConceptPropertyFieldBridge.class) private Collection<TermConceptProperty> myProperties; @@ -100,7 +101,7 @@ public class TermConcept implements Serializable { } public TermConcept(TermCodeSystemVersion theCs, String theCode) { - setCodeSystem(theCs); + setCodeSystemVersion(theCs); setCode(theCode); } @@ -130,7 +131,7 @@ public class TermConcept implements Serializable { property.setType(thePropertyType); property.setKey(thePropertyName); property.setValue(thePropertyValue); - getStringProperties().add(property); + getProperties().add(property); return property; } @@ -177,14 +178,14 @@ public class TermConcept implements Serializable { myCode = theCode; } - public TermCodeSystemVersion getCodeSystem() { + public TermCodeSystemVersion getCodeSystemVersion() { return myCodeSystem; } - public void setCodeSystem(TermCodeSystemVersion theCodeSystem) { - myCodeSystem = theCodeSystem; - if (theCodeSystem.getPid() != null) { - myCodeSystemVersionPid = theCodeSystem.getPid(); + public void setCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) { + myCodeSystem = theCodeSystemVersion; + if (theCodeSystemVersion.getPid() != null) { + myCodeSystemVersionPid = theCodeSystemVersion.getPid(); } } @@ -223,7 +224,7 @@ public class TermConcept implements Serializable { return myParents; } - public Collection<TermConceptProperty> getStringProperties() { + public Collection<TermConceptProperty> getProperties() { if (myProperties == null) { myProperties = new ArrayList<>(); } @@ -232,7 +233,7 @@ public class TermConcept implements Serializable { public List<String> getStringProperties(String thePropertyName) { List<String> retVal = new ArrayList<>(); - for (TermConceptProperty next : getStringProperties()) { + for (TermConceptProperty next : getProperties()) { if (thePropertyName.equals(next.getKey())) { if (next.getType() == TermConceptPropertyTypeEnum.STRING) { retVal.add(next.getValue()); @@ -244,7 +245,7 @@ public class TermConcept implements Serializable { public List<Coding> getCodingProperties(String thePropertyName) { List<Coding> retVal = new ArrayList<>(); - for (TermConceptProperty next : getStringProperties()) { + for (TermConceptProperty next : getProperties()) { if (thePropertyName.equals(next.getKey())) { if (next.getType() == TermConceptPropertyTypeEnum.CODING) { Coding coding = new Coding(); @@ -334,4 +335,20 @@ public class TermConcept implements Serializable { return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("code", myCode).append("display", myDisplay).build(); } + public List<IContextValidationSupport.BaseConceptProperty> toValidationProperties() { + List<IContextValidationSupport.BaseConceptProperty> retVal = new ArrayList<>(); + for (TermConceptProperty next : getProperties()) { + switch (next.getType()) { + case STRING: + retVal.add(new IContextValidationSupport.StringConceptProperty(next.getKey(), next.getValue())); + break; + case CODING: + retVal.add(new IContextValidationSupport.CodingConceptProperty(next.getKey(), next.getCodeSystem(), next.getValue(), next.getDisplay())); + break; + default: + throw new IllegalStateException("Don't know how to handle " + next.getType()); + } + } + return retVal; + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java similarity index 62% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java index 4466c2c87b1..ecf58bdd21b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.provider.dstu3; +package ca.uhn.fhir.jpa.provider; /* * #%L @@ -20,81 +20,86 @@ package ca.uhn.fhir.jpa.provider.dstu3; * #L% */ -import static org.apache.commons.lang3.StringUtils.defaultString; -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.io.IOUtils; -import org.hl7.fhir.dstu3.model.*; -import org.springframework.beans.factory.annotation.Autowired; - -import ca.uhn.fhir.jpa.provider.BaseJpaProvider; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc.UploadStatistics; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.StringType; +import org.springframework.beans.factory.annotation.Autowired; -public class TerminologyUploaderProviderDstu3 extends BaseJpaProvider { +import javax.servlet.http.HttpServletRequest; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import static org.apache.commons.lang3.StringUtils.defaultString; +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +public class TerminologyUploaderProvider extends BaseJpaProvider { public static final String UPLOAD_EXTERNAL_CODE_SYSTEM = "$upload-external-code-system"; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyUploaderProviderDstu3.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyUploaderProvider.class); @Autowired private IHapiTerminologyLoaderSvc myTerminologyLoaderSvc; - //@formatter:off @Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters= { @OperationParam(name="conceptCount", type=IntegerType.class, min=1) }) public Parameters uploadExternalCodeSystem( HttpServletRequest theServletRequest, - @OperationParam(name="url", min=1) UriType theUrl, - @OperationParam(name="package", min=0) Attachment thePackage, - @OperationParam(name="localfile", min=0, max=OperationParam.MAX_UNLIMITED) List<StringType> theLocalFile, + @OperationParam(name="url", min=1) StringParam theCodeSystemUrl, + @OperationParam(name="localfile", min=1, max=OperationParam.MAX_UNLIMITED) List<StringType> theLocalFile, RequestDetails theRequestDetails ) { - //@formatter:on - + startRequest(theServletRequest); try { - List<byte[]> data = new ArrayList<byte[]>(); + List<IHapiTerminologyLoaderSvc.FileDescriptor> localFiles = new ArrayList<>(); if (theLocalFile != null && theLocalFile.size() > 0) { for (StringType nextLocalFile : theLocalFile) { if (isNotBlank(nextLocalFile.getValue())) { ourLog.info("Reading in local file: {}", nextLocalFile.getValue()); - try { - byte[] nextData = IOUtils.toByteArray(new FileInputStream(nextLocalFile.getValue())); - data.add(nextData); - } catch (IOException e) { - throw new InternalErrorException(e); + File nextFile = new File(nextLocalFile.getValue()); + if (!nextFile.exists() || nextFile.isFile()) { + throw new InvalidRequestException("Unknown file: " +nextFile.getName()); } + localFiles.add(new IHapiTerminologyLoaderSvc.FileDescriptor() { + @Override + public String getFilename() { + return nextFile.getAbsolutePath(); + } + + @Override + public InputStream getInputStream() { + try { + return new FileInputStream(nextFile); + } catch (FileNotFoundException theE) { + throw new InternalErrorException(theE); + } + } + }); } } - } else if (thePackage == null || thePackage.getData() == null || thePackage.getData().length == 0) { - throw new InvalidRequestException("No 'localfile' or 'package' parameter, or package had no data"); - } else { - data = new ArrayList<byte[]>(); - data.add(thePackage.getData()); - thePackage.setData(null); } - String url = theUrl != null ? theUrl.getValueAsString() : null; + String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null; url = defaultString(url); UploadStatistics stats; - if (IHapiTerminologyLoaderSvc.SCT_URL.equals(url)) { - stats = myTerminologyLoaderSvc.loadSnomedCt((data), theRequestDetails); - } else if (IHapiTerminologyLoaderSvc.LOINC_URL.equals(url)) { - stats = myTerminologyLoaderSvc.loadLoinc((data), theRequestDetails); + if (IHapiTerminologyLoaderSvc.SCT_URI.equals(url)) { + stats = myTerminologyLoaderSvc.loadSnomedCt(localFiles, theRequestDetails); + } else if (IHapiTerminologyLoaderSvc.LOINC_URI.equals(url)) { + stats = myTerminologyLoaderSvc.loadLoinc(localFiles, theRequestDetails); } else { throw new InvalidRequestException("Unknown URL: " + url); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4.java deleted file mode 100644 index e5520c7c561..00000000000 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4.java +++ /dev/null @@ -1,111 +0,0 @@ -package ca.uhn.fhir.jpa.provider.r4; - -/* - * #%L - * HAPI FHIR JPA Server - * %% - * Copyright (C) 2014 - 2018 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import static org.apache.commons.lang3.StringUtils.defaultString; -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.io.IOUtils; -import org.hl7.fhir.r4.model.*; -import org.springframework.beans.factory.annotation.Autowired; - -import ca.uhn.fhir.jpa.provider.BaseJpaProvider; -import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; -import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc.UploadStatistics; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; -import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; - -public class TerminologyUploaderProviderR4 extends BaseJpaProvider { - public static final String UPLOAD_EXTERNAL_CODE_SYSTEM = "$upload-external-code-system"; - - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyUploaderProviderR4.class); - - @Autowired - private IHapiTerminologyLoaderSvc myTerminologyLoaderSvc; - - //@formatter:off - @Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters= { - @OperationParam(name="conceptCount", type=IntegerType.class, min=1) - }) - public Parameters uploadExternalCodeSystem( - HttpServletRequest theServletRequest, - @OperationParam(name="url", min=1) UriType theUrl, - @OperationParam(name="package", min=0) Attachment thePackage, - @OperationParam(name="localfile", min=0, max=OperationParam.MAX_UNLIMITED) List<StringType> theLocalFile, - RequestDetails theRequestDetails - ) { - //@formatter:on - - startRequest(theServletRequest); - try { - List<byte[]> data = new ArrayList<byte[]>(); - if (theLocalFile != null && theLocalFile.size() > 0) { - for (StringType nextLocalFile : theLocalFile) { - if (isNotBlank(nextLocalFile.getValue())) { - ourLog.info("Reading in local file: {}", nextLocalFile.getValue()); - try { - byte[] nextData = IOUtils.toByteArray(new FileInputStream(nextLocalFile.getValue())); - data.add(nextData); - } catch (IOException e) { - throw new InternalErrorException(e); - } - } - } - } else if (thePackage == null || thePackage.getData() == null || thePackage.getData().length == 0) { - throw new InvalidRequestException("No 'localfile' or 'package' parameter, or package had no data"); - } else { - data = new ArrayList<byte[]>(); - data.add(thePackage.getData()); - thePackage.setData(null); - } - - String url = theUrl != null ? theUrl.getValueAsString() : null; - url = defaultString(url); - - UploadStatistics stats; - if (IHapiTerminologyLoaderSvc.SCT_URL.equals(url)) { - stats = myTerminologyLoaderSvc.loadSnomedCt((data), theRequestDetails); - } else if (IHapiTerminologyLoaderSvc.LOINC_URL.equals(url)) { - stats = myTerminologyLoaderSvc.loadLoinc((data), theRequestDetails); - } else { - throw new InvalidRequestException("Unknown URL: " + url); - } - - Parameters retVal = new Parameters(); - retVal.addParameter().setName("conceptCount").setValue(new IntegerType(stats.getConceptCount())); - return retVal; - } finally { - endRequest(theServletRequest); - } - } - - -} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionDeliverySubscriber.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionDeliverySubscriber.java index bf6a56a2c16..3b3dfcaf3a7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionDeliverySubscriber.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionDeliverySubscriber.java @@ -49,7 +49,7 @@ public abstract class BaseSubscriptionDeliverySubscriber extends BaseSubscriptio try { ResourceDeliveryMessage msg = (ResourceDeliveryMessage) theMessage.getPayload(); - subscriptionId = msg.getPayload(getContext()).getIdElement().getValue(); + subscriptionId = msg.getSubscription().getIdElement(getContext()).getValue(); if (!subscriptionTypeApplies(getContext(), msg.getSubscription().getBackingSubscription(getContext()))) { return; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java index 7f87f53f451..a04b44af8c1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java @@ -20,24 +20,26 @@ package ca.uhn.fhir.jpa.subscription.resthook; * #L% */ +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.subscription.*; -import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.EncodingEnum; -import ca.uhn.fhir.rest.client.api.IGenericClient; -import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.client.api.*; import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; import ca.uhn.fhir.rest.gclient.IClientExecutable; -import org.apache.commons.lang3.ObjectUtils; -import org.apache.commons.lang3.StringUtils; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -54,10 +56,38 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe IClientExecutable<?, ?> operation; switch (theMsg.getOperationType()) { case CREATE: - operation = theClient.update().resource(payloadResource); + if (payloadResource == null || payloadResource.isEmpty()) { + if (thePayloadType != null ) { + operation = theClient.create().resource(payloadResource); + } else { + sendNotification(theMsg); + return; + } + } else { + if (thePayloadType != null ) { + operation = theClient.update().resource(payloadResource); + } else { + sendNotification(theMsg); + return; + } + } break; case UPDATE: - operation = theClient.update().resource(payloadResource); + if (payloadResource == null || payloadResource.isEmpty()) { + if (thePayloadType != null ) { + operation = theClient.create().resource(payloadResource); + } else { + sendNotification(theMsg); + return; + } + } else { + if (thePayloadType != null ) { + operation = theClient.update().resource(payloadResource); + } else { + sendNotification(theMsg); + return; + } + } break; case DELETE: operation = theClient.delete().resourceById(theMsg.getPayloadId(getContext())); @@ -67,11 +97,19 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe return; } - operation.encoded(thePayloadType); + if (thePayloadType != null) { + operation.encoded(thePayloadType); + } ourLog.info("Delivering {} rest-hook payload {} for {}", theMsg.getOperationType(), payloadResource.getIdElement().toUnqualified().getValue(), theSubscription.getIdElement(getContext()).toUnqualifiedVersionless().getValue()); - operation.execute(); + try { + operation.execute(); + } catch (ResourceNotFoundException e) { + ourLog.error("Cannot reach "+ theMsg.getSubscription().getEndpointUrl()); + e.printStackTrace(); + throw e; + } } @Override @@ -83,13 +121,14 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe // Grab the payload type (encoding mimetype) from the subscription String payloadString = subscription.getPayloadString(); - payloadString = StringUtils.defaultString(payloadString, Constants.CT_FHIR_XML_NEW); - if (payloadString.contains(";")) { - payloadString = payloadString.substring(0, payloadString.indexOf(';')); + EncodingEnum payloadType = null; + if(payloadString != null) { + if (payloadString.contains(";")) { + payloadString = payloadString.substring(0, payloadString.indexOf(';')); + } + payloadString = payloadString.trim(); + payloadType = EncodingEnum.forContentType(payloadString); } - payloadString = payloadString.trim(); - EncodingEnum payloadType = EncodingEnum.forContentType(payloadString); - payloadType = ObjectUtils.defaultIfNull(payloadType, EncodingEnum.XML); // Create the client request getContext().getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); @@ -109,4 +148,23 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe deliverPayload(theMessage, subscription, payloadType, client); } + /** + * Sends a POST notification without a payload + * @param theMsg + */ + protected void sendNotification(ResourceDeliveryMessage theMsg) { + FhirContext context= getContext(); + Map<String, List<String>> params = new HashMap(); + List<Header> headers = new ArrayList<>(); + StringBuilder url = new StringBuilder(theMsg.getSubscription().getEndpointUrl()); + IHttpClient client = context.getRestfulClientFactory().getHttpClient(url, params, "", RequestTypeEnum.POST, headers); + IHttpRequest request = client.createParamRequest(context, params, null); + try { + IHttpResponse response = request.execute(); + } catch (IOException e) { + ourLog.error("Error trying to reach "+ theMsg.getSubscription().getEndpointUrl()); + e.printStackTrace(); + throw new ResourceNotFoundException(e.getMessage()); + } + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 0ea696cc292..9ad462731e2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -24,10 +24,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; -import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; -import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao; -import ca.uhn.fhir.jpa.dao.data.ITermConceptDao; -import ca.uhn.fhir.jpa.dao.data.ITermConceptParentChildLinkDao; +import ca.uhn.fhir.jpa.dao.data.*; import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; @@ -79,6 +76,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc @Autowired protected ITermConceptDao myConceptDao; @Autowired + protected ITermConceptPropertyDao myConceptPropertyDao; + @Autowired protected FhirContext myContext; @PersistenceContext(type = PersistenceContextType.TRANSACTION) protected EntityManager myEntityManager; @@ -409,7 +408,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc ourLog.info("Have processed {}/{} concepts ({}%)", theConceptsStack.size(), theTotalConcepts, (int) (pct * 100.0f)); } - theConcept.setCodeSystem(theCodeSystem); + theConcept.setCodeSystemVersion(theCodeSystem); theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { @@ -430,13 +429,17 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } } + for (TermConceptProperty next : theConcept.getProperties()){ + myConceptPropertyDao.save(next); + } + } private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) { - if (theNext.getCodeSystem() != null) { + if (theNext.getCodeSystemVersion() != null) { return; } - theNext.setCodeSystem(theCodeSystemVersion); + theNext.setCodeSystemVersion(theCodeSystemVersion); for (TermConceptParentChildLink next : theNext.getChildren()) { populateVersion(next.getChild(), theCodeSystemVersion); } @@ -616,7 +619,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc @Override @Transactional(propagation = Propagation.REQUIRED) - public void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, TermCodeSystemVersion theCodeSystemVersion) { + public void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, String theSystemName, TermCodeSystemVersion theCodeSystemVersion) { ourLog.info("Storing code system"); ValidateUtil.isTrueOrThrowInvalidRequest(theCodeSystemVersion.getResource() != null, "No resource supplied"); @@ -655,6 +658,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } codeSystem.setResource(theCodeSystemVersion.getResource()); codeSystem.setCodeSystemUri(theSystemUri); + codeSystem.setName(theSystemName); myCodeSystemDao.save(codeSystem); } else { if (!ObjectUtil.equals(codeSystem.getResource().getId(), theCodeSystemVersion.getResource().getId())) { @@ -663,6 +667,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc throw new UnprocessableEntityException(msg); } } + theCodeSystemVersion.setCodeSystem(codeSystem); ourLog.info("Validating all codes in CodeSystem for storage (this can take some time for large sets)"); @@ -721,7 +726,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc ourLog.info("CodeSystem resource has ID: {}", csId.getValue()); theCodeSystemVersion.setResource(resource); - storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemVersion); + storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemVersion); for (ValueSet nextValueSet : theValueSets) { createOrUpdateValueSet(nextValueSet, theRequestDetails); @@ -749,8 +754,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc private int validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, ArrayList<String> theConceptsStack, IdentityHashMap<TermConcept, Object> theAllConcepts) { - ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystem() != null, "CodesystemValue is null"); - ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystem() == theCodeSystem, "CodeSystems are not equal"); + ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystemVersion() != null, "CodesystemValue is null"); + ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystemVersion() == theCodeSystem, "CodeSystems are not equal"); ValidateUtil.isNotBlankOrThrowInvalidRequest(theConcept.getCode(), "Codesystem contains a code with no code value"); if (theConceptsStack.contains(theConcept.getCode())) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java index fd0a629de7e..d8f8aee0c5b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java @@ -235,7 +235,10 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen ConceptDefinitionComponent def = new ConceptDefinitionComponent(); def.setCode(code.getCode()); def.setDisplay(code.getDisplay()); - return new CodeValidationResult(def); + CodeValidationResult retVal = new CodeValidationResult(def); + retVal.setProperties(code.toValidationProperties()); + retVal.setCodeSystemName(code.getCodeSystemVersion().getCodeSystem().getName()); + return retVal; } return new CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java index e66a0c2acee..3af96290f2f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java @@ -103,7 +103,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements @Override protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap, RequestDetails theRequestDetails) { String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl()); - myConceptMapResourceDao.update(theConceptMap, matchUrl, theRequestDetails).getId(); + myConceptMapResourceDao.update(theConceptMap, matchUrl, theRequestDetails); } @Override @@ -201,10 +201,12 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements ConceptDefinitionComponent def = new ConceptDefinitionComponent(); def.setCode(code.getCode()); def.setDisplay(code.getDisplay()); - return new CodeValidationResult(def); + CodeValidationResult retVal = new CodeValidationResult(def); + retVal.setProperties(code.toValidationProperties()); + return retVal; } - return new CodeValidationResult(IssueSeverity.ERROR, "Unkonwn code {" + theCodeSystem + "}" + theCode); + return new CodeValidationResult(IssueSeverity.ERROR, "Unknown code {" + theCodeSystem + "}" + theCode); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologyLoaderSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologyLoaderSvc.java index b491a398b4e..7d025b66755 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologyLoaderSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologyLoaderSvc.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -20,20 +20,30 @@ package ca.uhn.fhir.jpa.term; * #L% */ -import java.util.List; - import ca.uhn.fhir.rest.api.server.RequestDetails; +import java.io.InputStream; +import java.util.List; + public interface IHapiTerminologyLoaderSvc { - String LOINC_URL = "http://loinc.org"; - String SCT_URL = "http://snomed.info/sct"; + String LOINC_URI = "http://loinc.org"; + String SCT_URI = "http://snomed.info/sct"; + String IEEE_11073_10101_URI = "urn:iso:std:iso:11073:10101"; - UploadStatistics loadLoinc(List<byte[]> theZipBytes, RequestDetails theRequestDetails); + UploadStatistics loadLoinc(List<FileDescriptor> theFiles, RequestDetails theRequestDetails); - UploadStatistics loadSnomedCt(List<byte[]> theZipBytes, RequestDetails theRequestDetails); + UploadStatistics loadSnomedCt(List<FileDescriptor> theFiles, RequestDetails theRequestDetails); - public static class UploadStatistics { + interface FileDescriptor { + + String getFilename(); + + InputStream getInputStream(); + + } + + class UploadStatistics { private final int myConceptCount; public UploadStatistics(int theConceptCount) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java index f3dbfc64cac..26f9e47c6e0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java @@ -57,7 +57,7 @@ public interface IHapiTerminologySvc { */ void setProcessDeferred(boolean theProcessDeferred); - void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, TermCodeSystemVersion theCodeSytemVersion); + void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, String theSystemName, TermCodeSystemVersion theCodeSytemVersion); boolean supportsSystem(String theCodeSystem); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java index 1bc4b23a6e2..4b3b8cfe841 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java @@ -11,13 +11,14 @@ import ca.uhn.fhir.jpa.term.snomedct.SctHandlerRelationship; import ca.uhn.fhir.jpa.util.Counter; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import org.apache.commons.csv.QuoteMode; +import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.io.input.BOMInputStream; import org.apache.commons.lang3.ObjectUtils; @@ -69,8 +70,11 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { public static final String LOINC_PART_LINK_FILE = "LoincPartLink_Beta_1.csv"; public static final String LOINC_PART_RELATED_CODE_MAPPING_FILE = "PartRelatedCodeMapping_Beta_1.csv"; public static final String LOINC_RSNA_PLAYBOOK_FILE = "LoincRsnaRadiologyPlaybook.csv"; - public static final String TOP2000_COMMON_LAB_RESULTS_US_FILE = "Top2000CommonLabResultsUS.csv"; - public static final String TOP2000_COMMON_LAB_RESULTS_SI_FILE = "Top2000CommonLabResultsSI.csv"; + public static final String LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE = "Top2000CommonLabResultsUS.csv"; + public static final String LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE = "Top2000CommonLabResultsSI.csv"; + public static final String LOINC_UNIVERSAL_LAB_ORDER_VALUESET_FILE = "LoincUniversalLabOrdersValueSet.csv"; + public static final String LOINC_IEEE_MEDICAL_DEVICE_CODE_MAPPING_TABLE_CSV = "LoincIeeeMedicalDeviceCodeMappingTable.csv"; + public static final String LOINC_IMAGING_DOCUMENT_CODES_FILE = "ImagingDocumentCodes.csv"; private static final int LOG_INCREMENT = 100000; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyLoaderSvcImpl.class); @Autowired @@ -113,85 +117,91 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { } - private void iterateOverZipFile(List<byte[]> theZipBytes, String fileNamePart, IRecordHandler handler, char theDelimiter, QuoteMode theQuoteMode) { - boolean found = false; + private void iterateOverZipFile(LoadedFileDescriptors theDescriptors, String theFileNamePart, IRecordHandler theHandler, char theDelimiter, QuoteMode theQuoteMode) { - for (byte[] nextZipBytes : theZipBytes) { - ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(nextZipBytes))); - try { - for (ZipEntry nextEntry; (nextEntry = zis.getNextEntry()) != null; ) { + for (FileDescriptor nextZipBytes : theDescriptors.getUncompressedFileDescriptors()) { + String nextFilename = nextZipBytes.getFilename(); + if (nextFilename.contains(theFileNamePart)) { + ourLog.info("Processing file {}", nextFilename); - String nextFilename = nextEntry.getName(); - if (nextFilename.contains(fileNamePart)) { - ourLog.info("Processing file {}", nextFilename); - found = true; + Reader reader; + CSVParser parsed; + try { + reader = new InputStreamReader(nextZipBytes.getInputStream(), Charsets.UTF_8); + CSVFormat format = CSVFormat.newFormat(theDelimiter).withFirstRecordAsHeader(); + if (theQuoteMode != null) { + format = format.withQuote('"').withQuoteMode(theQuoteMode); + } + parsed = new CSVParser(reader, format); + Iterator<CSVRecord> iter = parsed.iterator(); + ourLog.debug("Header map: {}", parsed.getHeaderMap()); - Reader reader; - CSVParser parsed; - try { - reader = new InputStreamReader(new BOMInputStream(zis), Charsets.UTF_8); - CSVFormat format = CSVFormat.newFormat(theDelimiter).withFirstRecordAsHeader(); - if (theQuoteMode != null) { - format = format.withQuote('"').withQuoteMode(theQuoteMode); - } - parsed = new CSVParser(reader, format); - Iterator<CSVRecord> iter = parsed.iterator(); - ourLog.debug("Header map: {}", parsed.getHeaderMap()); - - int count = 0; - int logIncrement = LOG_INCREMENT; - int nextLoggedCount = 0; - while (iter.hasNext()) { - CSVRecord nextRecord = iter.next(); - handler.accept(nextRecord); - count++; - if (count >= nextLoggedCount) { - ourLog.info(" * Processed {} records in {}", count, nextFilename); - nextLoggedCount += logIncrement; - } - } - - } catch (IOException e) { - throw new InternalErrorException(e); + int count = 0; + int nextLoggedCount = 0; + while (iter.hasNext()) { + CSVRecord nextRecord = iter.next(); + theHandler.accept(nextRecord); + count++; + if (count >= nextLoggedCount) { + ourLog.info(" * Processed {} records in {}", count, nextFilename); + nextLoggedCount += LOG_INCREMENT; } } + + } catch (IOException e) { + throw new InternalErrorException(e); } - } catch (IOException e) { - throw new InternalErrorException(e); - } finally { - IOUtils.closeQuietly(zis); } + } - // This should always be true, but just in case we've introduced a bug... - Validate.isTrue(found); } @Override - public UploadStatistics loadLoinc(List<byte[]> theZipBytes, RequestDetails theRequestDetails) { - List<String> expectedFilenameFragments = Arrays.asList( + public UploadStatistics loadLoinc(List<FileDescriptor> theFiles, RequestDetails theRequestDetails) { + LoadedFileDescriptors descriptors = new LoadedFileDescriptors(theFiles); + List<String> mandatoryFilenameFragments = Arrays.asList( LOINC_FILE, LOINC_HIERARCHY_FILE); + descriptors.verifyMandatoryFilesExist(mandatoryFilenameFragments); - verifyMandatoryFilesExist(theZipBytes, expectedFilenameFragments); + List<String> optionalFilenameFragments = Arrays.asList( + LOINC_ANSWERLIST_FILE, + LOINC_ANSWERLIST_LINK_FILE, + LOINC_PART_FILE, + LOINC_PART_LINK_FILE, + LOINC_PART_RELATED_CODE_MAPPING_FILE, + LOINC_DOCUMENT_ONTOLOGY_FILE, + LOINC_RSNA_PLAYBOOK_FILE, + LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE, + LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE, + LOINC_UNIVERSAL_LAB_ORDER_VALUESET_FILE, + LOINC_IEEE_MEDICAL_DEVICE_CODE_MAPPING_TABLE_CSV, + LOINC_IMAGING_DOCUMENT_CODES_FILE + ); + descriptors.verifyOptionalFilesExist(optionalFilenameFragments); ourLog.info("Beginning LOINC processing"); - return processLoincFiles(theZipBytes, theRequestDetails); + return processLoincFiles(descriptors, theRequestDetails); } @Override - public UploadStatistics loadSnomedCt(List<byte[]> theZipBytes, RequestDetails theRequestDetails) { - List<String> expectedFilenameFragments = Arrays.asList(SCT_FILE_DESCRIPTION, SCT_FILE_RELATIONSHIP, SCT_FILE_CONCEPT); + public UploadStatistics loadSnomedCt(List<FileDescriptor> theFiles, RequestDetails theRequestDetails) { + LoadedFileDescriptors descriptors = new LoadedFileDescriptors(theFiles); - verifyMandatoryFilesExist(theZipBytes, expectedFilenameFragments); + List<String> expectedFilenameFragments = Arrays.asList( + SCT_FILE_DESCRIPTION, + SCT_FILE_RELATIONSHIP, + SCT_FILE_CONCEPT); + descriptors.verifyMandatoryFilesExist(expectedFilenameFragments); ourLog.info("Beginning SNOMED CT processing"); - return processSnomedCtFiles(theZipBytes, theRequestDetails); + return processSnomedCtFiles(descriptors, theRequestDetails); } - UploadStatistics processLoincFiles(List<byte[]> theZipBytes, RequestDetails theRequestDetails) { + UploadStatistics processLoincFiles(LoadedFileDescriptors theDescriptors, RequestDetails theRequestDetails) { final TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion(); final Map<String, TermConcept> code2concept = new HashMap<>(); final List<ValueSet> valueSets = new ArrayList<>(); @@ -216,49 +226,61 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { // Loinc Codes handler = new LoincHandler(codeSystemVersion, code2concept, propertyNames); - iterateOverZipFile(theZipBytes, LOINC_FILE, handler, ',', QuoteMode.NON_NUMERIC); + iterateOverZipFile(theDescriptors, LOINC_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Loinc Hierarchy handler = new LoincHierarchyHandler(codeSystemVersion, code2concept); - iterateOverZipFile(theZipBytes, LOINC_HIERARCHY_FILE, handler, ',', QuoteMode.NON_NUMERIC); + iterateOverZipFile(theDescriptors, LOINC_HIERARCHY_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Answer lists (ValueSets of potential answers/values for loinc "questions") handler = new LoincAnswerListHandler(codeSystemVersion, code2concept, propertyNames, valueSets); - iterateOverZipFile(theZipBytes, LOINC_ANSWERLIST_FILE, handler, ',', QuoteMode.NON_NUMERIC); + iterateOverZipFile(theDescriptors, LOINC_ANSWERLIST_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Answer list links (connects loinc observation codes to answerlist codes) handler = new LoincAnswerListLinkHandler(code2concept, valueSets); - iterateOverZipFile(theZipBytes, LOINC_ANSWERLIST_LINK_FILE, handler, ',', QuoteMode.NON_NUMERIC); + iterateOverZipFile(theDescriptors, LOINC_ANSWERLIST_LINK_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Part file handler = new LoincPartHandler(codeSystemVersion, code2concept); - iterateOverZipFile(theZipBytes, LOINC_PART_FILE, handler, ',', QuoteMode.NON_NUMERIC); + iterateOverZipFile(theDescriptors, LOINC_PART_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Part link file handler = new LoincPartLinkHandler(codeSystemVersion, code2concept); - iterateOverZipFile(theZipBytes, LOINC_PART_LINK_FILE, handler, ',', QuoteMode.NON_NUMERIC); + iterateOverZipFile(theDescriptors, LOINC_PART_LINK_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Part related code mapping - handler = new LoincPartRelatedCodeMappingHandler(codeSystemVersion, code2concept, conceptMaps); - iterateOverZipFile(theZipBytes, LOINC_PART_RELATED_CODE_MAPPING_FILE, handler, ',', QuoteMode.NON_NUMERIC); + handler = new LoincPartRelatedCodeMappingHandler(codeSystemVersion, code2concept, valueSets, conceptMaps); + iterateOverZipFile(theDescriptors, LOINC_PART_RELATED_CODE_MAPPING_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Document Ontology File - handler = new LoincDocumentOntologyHandler(codeSystemVersion, code2concept, propertyNames, valueSets); - iterateOverZipFile(theZipBytes, LOINC_DOCUMENT_ONTOLOGY_FILE, handler, ',', QuoteMode.NON_NUMERIC); + handler = new LoincDocumentOntologyHandler(codeSystemVersion, code2concept, propertyNames, valueSets, conceptMaps); + iterateOverZipFile(theDescriptors, LOINC_DOCUMENT_ONTOLOGY_FILE, handler, ',', QuoteMode.NON_NUMERIC); // RSNA Playbook file handler = new LoincRsnaPlaybookHandler(codeSystemVersion, code2concept, propertyNames, valueSets, conceptMaps); - iterateOverZipFile(theZipBytes, LOINC_RSNA_PLAYBOOK_FILE, handler, ',', QuoteMode.NON_NUMERIC); + iterateOverZipFile(theDescriptors, LOINC_RSNA_PLAYBOOK_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Top 2000 Codes - US - handler = new LoincTop2000LabResultsUsHandler(code2concept, valueSets); - iterateOverZipFile(theZipBytes, TOP2000_COMMON_LAB_RESULTS_US_FILE, handler, ',', QuoteMode.NON_NUMERIC); + handler = new LoincTop2000LabResultsUsHandler(code2concept, valueSets, conceptMaps); + iterateOverZipFile(theDescriptors, LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Top 2000 Codes - SI - handler = new LoincTop2000LabResultsSiHandler(code2concept, valueSets); - iterateOverZipFile(theZipBytes, TOP2000_COMMON_LAB_RESULTS_SI_FILE, handler, ',', QuoteMode.NON_NUMERIC); + handler = new LoincTop2000LabResultsSiHandler(code2concept, valueSets, conceptMaps); + iterateOverZipFile(theDescriptors, LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE, handler, ',', QuoteMode.NON_NUMERIC); - theZipBytes.clear(); + // Universal Lab Order ValueSet + handler = new LoincUniversalOrderSetHandler(code2concept, valueSets, conceptMaps); + iterateOverZipFile(theDescriptors, LOINC_UNIVERSAL_LAB_ORDER_VALUESET_FILE, handler, ',', QuoteMode.NON_NUMERIC); + + // IEEE Medical Device Codes + handler = new LoincIeeeMedicalDeviceCodeHandler(code2concept, valueSets, conceptMaps); + iterateOverZipFile(theDescriptors, LOINC_IEEE_MEDICAL_DEVICE_CODE_MAPPING_TABLE_CSV, handler, ',', QuoteMode.NON_NUMERIC); + + // Imaging Document Codes + handler = new LoincImagingDocumentCodeHandler(code2concept, valueSets, conceptMaps); + iterateOverZipFile(theDescriptors, LOINC_IMAGING_DOCUMENT_CODES_FILE, handler, ',', QuoteMode.NON_NUMERIC); + + IOUtils.closeQuietly(theDescriptors); for (Entry<String, TermConcept> next : code2concept.entrySet()) { TermConcept nextConcept = next.getValue(); @@ -277,34 +299,32 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { return new UploadStatistics(conceptCount); } - UploadStatistics processSnomedCtFiles(List<byte[]> theZipBytes, RequestDetails theRequestDetails) { + private UploadStatistics processSnomedCtFiles(LoadedFileDescriptors theDescriptors, RequestDetails theRequestDetails) { final TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion(); final Map<String, TermConcept> id2concept = new HashMap<>(); final Map<String, TermConcept> code2concept = new HashMap<>(); final Set<String> validConceptIds = new HashSet<>(); IRecordHandler handler = new SctHandlerConcept(validConceptIds); - iterateOverZipFile(theZipBytes, SCT_FILE_CONCEPT, handler, '\t', null); + iterateOverZipFile(theDescriptors, SCT_FILE_CONCEPT, handler, '\t', null); ourLog.info("Have {} valid concept IDs", validConceptIds.size()); handler = new SctHandlerDescription(validConceptIds, code2concept, id2concept, codeSystemVersion); - iterateOverZipFile(theZipBytes, SCT_FILE_DESCRIPTION, handler, '\t', null); + iterateOverZipFile(theDescriptors, SCT_FILE_DESCRIPTION, handler, '\t', null); ourLog.info("Got {} concepts, cloning map", code2concept.size()); final HashMap<String, TermConcept> rootConcepts = new HashMap<>(code2concept); handler = new SctHandlerRelationship(codeSystemVersion, rootConcepts, code2concept); - iterateOverZipFile(theZipBytes, SCT_FILE_RELATIONSHIP, handler, '\t', null); + iterateOverZipFile(theDescriptors, SCT_FILE_RELATIONSHIP, handler, '\t', null); - theZipBytes.clear(); + IOUtils.closeQuietly(theDescriptors); ourLog.info("Looking for root codes"); - for (Iterator<Entry<String, TermConcept>> iter = rootConcepts.entrySet().iterator(); iter.hasNext(); ) { - if (iter.next().getValue().getParents().isEmpty() == false) { - iter.remove(); - } - } + rootConcepts + .entrySet() + .removeIf(theStringTermConceptEntry -> theStringTermConceptEntry.getValue().getParents().isEmpty() == false); ourLog.info("Done loading SNOMED CT files - {} root codes, {} total codes", rootConcepts.size(), code2concept.size()); @@ -313,13 +333,14 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { long count = circularCounter.getThenAdd(); float pct = ((float) count / rootConcepts.size()) * 100.0f; ourLog.info(" * Scanning for circular refs - have scanned {} / {} codes ({}%)", count, rootConcepts.size(), pct); - dropCircularRefs(next, new ArrayList<String>(), code2concept, circularCounter); + dropCircularRefs(next, new ArrayList<>(), code2concept, circularCounter); } codeSystemVersion.getConcepts().addAll(rootConcepts.values()); CodeSystem cs = new org.hl7.fhir.r4.model.CodeSystem(); - cs.setUrl(SCT_URL); + cs.setUrl(SCT_URI); + cs.setName("SNOMED CT"); cs.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT); storeCodeSystem(theRequestDetails, codeSystemVersion, cs, null, null); @@ -351,33 +372,6 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { myTermSvc.setProcessDeferred(true); } - private void verifyMandatoryFilesExist(List<byte[]> theZipBytes, List<String> theExpectedFilenameFragments) { - Set<String> foundFragments = new HashSet<>(); - - for (byte[] nextZipBytes : theZipBytes) { - ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(nextZipBytes))); - try { - for (ZipEntry nextEntry; (nextEntry = zis.getNextEntry()) != null; ) { - for (String next : theExpectedFilenameFragments) { - if (nextEntry.getName().contains(next)) { - foundFragments.add(next); - } - } - } - } catch (IOException e) { - throw new InternalErrorException(e); - } finally { - IOUtils.closeQuietly(zis); - } - } - - for (String next : theExpectedFilenameFragments) { - if (!foundFragments.contains(next)) { - throw new InvalidRequestException("Invalid input zip file, expected zip to contain the following name fragments: " + theExpectedFilenameFragments + " but found: " + foundFragments); - } - } - - } public static String firstNonBlank(String... theStrings) { String retVal = ""; @@ -395,10 +389,101 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { if (concept == null) { concept = new TermConcept(); id2concept.put(id, concept); - concept.setCodeSystem(codeSystemVersion); + concept.setCodeSystemVersion(codeSystemVersion); } return concept; } + static class LoadedFileDescriptors implements Closeable { + private List<File> myTemporaryFiles = new ArrayList<>(); + private List<IHapiTerminologyLoaderSvc.FileDescriptor> myUncompressedFileDescriptors = new ArrayList<>(); + + LoadedFileDescriptors(List<IHapiTerminologyLoaderSvc.FileDescriptor> theFileDescriptors) { + try { + for (FileDescriptor next : theFileDescriptors) { + File nextTemporaryFile = File.createTempFile("hapifhir", ".tmp"); + nextTemporaryFile.deleteOnExit(); + + if (next.getFilename().toLowerCase().endsWith(".zip")) { + ourLog.info("Uncompressing {} into temporary files", next.getFilename()); + try (InputStream inputStream = next.getInputStream()) { + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(inputStream)); + for (ZipEntry nextEntry; (nextEntry = zis.getNextEntry()) != null; ) { + BOMInputStream fis = new BOMInputStream(zis); + FileOutputStream fos = new FileOutputStream(nextTemporaryFile); + IOUtils.copy(fis, fos); + String nextEntryFileName = nextEntry.getName(); + myUncompressedFileDescriptors.add(new FileDescriptor() { + @Override + public String getFilename() { + return nextEntryFileName; + } + + @Override + public InputStream getInputStream() { + try { + return new FileInputStream(nextTemporaryFile); + } catch (FileNotFoundException e) { + throw new InternalErrorException(e); + } + } + }); + myTemporaryFiles.add(nextTemporaryFile); + } + } + } else { + myUncompressedFileDescriptors.add(next); + } + + } + } catch (Exception e) { + close(); + throw new InternalErrorException(e); + } + } + + @Override + public void close() { + for (File next : myTemporaryFiles) { + FileUtils.deleteQuietly(next); + } + } + + List<IHapiTerminologyLoaderSvc.FileDescriptor> getUncompressedFileDescriptors() { + return myUncompressedFileDescriptors; + } + + private List<String> notFound(List<String> theExpectedFilenameFragments) { + Set<String> foundFragments = new HashSet<>(); + for (String nextExpected : theExpectedFilenameFragments) { + for (FileDescriptor next : myUncompressedFileDescriptors) { + if (next.getFilename().contains(nextExpected)) { + foundFragments.add(nextExpected); + break; + } + } + } + + ArrayList<String> notFoundFileNameFragments = new ArrayList<>(theExpectedFilenameFragments); + notFoundFileNameFragments.removeAll(foundFragments); + return notFoundFileNameFragments; + } + + private void verifyMandatoryFilesExist(List<String> theExpectedFilenameFragments) { + List<String> notFound = notFound(theExpectedFilenameFragments); + if (!notFound.isEmpty()) { + throw new UnprocessableEntityException("Could not find the following mandatory files in input: " + notFound); + } + } + + private void verifyOptionalFilesExist(List<String> theExpectedFilenameFragments) { + List<String> notFound = notFound(theExpectedFilenameFragments); + if (!notFound.isEmpty()) { + ourLog.warn("Could not find the following optional file: " + notFound); + } + } + + + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java index 7a2c3d8dd17..f35c5e6c5da 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term.loinc; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IRecordHandler; +import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.ValueSet; @@ -9,17 +10,20 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.*; abstract class BaseHandler implements IRecordHandler { + private final List<ConceptMap> myConceptMaps; + private final Map<String, ConceptMap> myIdToConceptMaps = new HashMap<>(); private final List<ValueSet> myValueSets; private final Map<String, ValueSet> myIdToValueSet = new HashMap<>(); private final Map<String, TermConcept> myCode2Concept; - BaseHandler(Map<String, TermConcept> theCode2Concept, List<ValueSet> theValueSets) { + BaseHandler(Map<String, TermConcept> theCode2Concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { myValueSets = theValueSets; myCode2Concept = theCode2Concept; + myConceptMaps = theConceptMaps; } void addCodeAsIncludeToValueSet(ValueSet theVs, String theCodeSystemUrl, String theCode, String theDisplayName) { @@ -60,6 +64,78 @@ abstract class BaseHandler implements IRecordHandler { } } + + void addConceptMapEntry(ConceptMapping theMapping) { + if (isBlank(theMapping.getSourceCode())) { + return; + } + if (isBlank(theMapping.getTargetCode())) { + return; + } + + ConceptMap conceptMap; + if (!myIdToConceptMaps.containsKey(theMapping.getConceptMapId())) { + conceptMap = new ConceptMap(); + conceptMap.setId(theMapping.getConceptMapId()); + conceptMap.setUrl(theMapping.getConceptMapUri()); + conceptMap.setName(theMapping.getConceptMapName()); + myIdToConceptMaps.put(theMapping.getConceptMapId(), conceptMap); + myConceptMaps.add(conceptMap); + } else { + conceptMap = myIdToConceptMaps.get(theMapping.getConceptMapId()); + } + + if (isNotBlank(theMapping.getCopyright())) { + conceptMap.setCopyright(theMapping.getCopyright()); + } + + ConceptMap.SourceElementComponent source = null; + ConceptMap.ConceptMapGroupComponent group = null; + + for (ConceptMap.ConceptMapGroupComponent next : conceptMap.getGroup()) { + if (next.getSource().equals(theMapping.getSourceCodeSystem())) { + if (next.getTarget().equals(theMapping.getTargetCodeSystem())) { + if (!defaultString(theMapping.getTargetCodeSystemVersion()).equals(defaultString(next.getTargetVersion()))) { + continue; + } + group = next; + break; + } + } + } + if (group == null) { + group = conceptMap.addGroup(); + group.setSource(theMapping.getSourceCodeSystem()); + group.setTarget(theMapping.getTargetCodeSystem()); + group.setTargetVersion(defaultIfBlank(theMapping.getTargetCodeSystemVersion(), null)); + } + + for (ConceptMap.SourceElementComponent next : group.getElement()) { + if (next.getCode().equals(theMapping.getSourceCode())) { + source = next; + } + } + if (source == null) { + source = group.addElement(); + source.setCode(theMapping.getSourceCode()); + source.setDisplay(theMapping.getSourceDisplay()); + } + + boolean found = false; + for (ConceptMap.TargetElementComponent next : source.getTarget()) { + if (next.getCode().equals(theMapping.getTargetCode())) { + found = true; + } + } + if (!found) { + source + .addTarget() + .setCode(theMapping.getTargetCode()) + .setDisplay(theMapping.getTargetDisplay()) + .setEquivalence(theMapping.getEquivalence()); + } + } + ValueSet getValueSet(String theValueSetId, String theValueSetUri, String theValueSetName) { ValueSet vs; if (!myIdToValueSet.containsKey(theValueSetId)) { @@ -77,4 +153,128 @@ abstract class BaseHandler implements IRecordHandler { } + static class ConceptMapping { + + private String myCopyright; + private String myConceptMapId; + private String myConceptMapUri; + private String myConceptMapName; + private String mySourceCodeSystem; + private String mySourceCode; + private String mySourceDisplay; + private String myTargetCodeSystem; + private String myTargetCode; + private String myTargetDisplay; + private Enumerations.ConceptMapEquivalence myEquivalence; + private String myTargetCodeSystemVersion; + + String getConceptMapId() { + return myConceptMapId; + } + + ConceptMapping setConceptMapId(String theConceptMapId) { + myConceptMapId = theConceptMapId; + return this; + } + + String getConceptMapName() { + return myConceptMapName; + } + + ConceptMapping setConceptMapName(String theConceptMapName) { + myConceptMapName = theConceptMapName; + return this; + } + + String getConceptMapUri() { + return myConceptMapUri; + } + + ConceptMapping setConceptMapUri(String theConceptMapUri) { + myConceptMapUri = theConceptMapUri; + return this; + } + + String getCopyright() { + return myCopyright; + } + + ConceptMapping setCopyright(String theCopyright) { + myCopyright = theCopyright; + return this; + } + + Enumerations.ConceptMapEquivalence getEquivalence() { + return myEquivalence; + } + + ConceptMapping setEquivalence(Enumerations.ConceptMapEquivalence theEquivalence) { + myEquivalence = theEquivalence; + return this; + } + + String getSourceCode() { + return mySourceCode; + } + + ConceptMapping setSourceCode(String theSourceCode) { + mySourceCode = theSourceCode; + return this; + } + + String getSourceCodeSystem() { + return mySourceCodeSystem; + } + + ConceptMapping setSourceCodeSystem(String theSourceCodeSystem) { + mySourceCodeSystem = theSourceCodeSystem; + return this; + } + + String getSourceDisplay() { + return mySourceDisplay; + } + + ConceptMapping setSourceDisplay(String theSourceDisplay) { + mySourceDisplay = theSourceDisplay; + return this; + } + + String getTargetCode() { + return myTargetCode; + } + + ConceptMapping setTargetCode(String theTargetCode) { + myTargetCode = theTargetCode; + return this; + } + + String getTargetCodeSystem() { + return myTargetCodeSystem; + } + + ConceptMapping setTargetCodeSystem(String theTargetCodeSystem) { + myTargetCodeSystem = theTargetCodeSystem; + return this; + } + + String getTargetCodeSystemVersion() { + return myTargetCodeSystemVersion; + } + + ConceptMapping setTargetCodeSystemVersion(String theTargetCodeSystemVersion) { + myTargetCodeSystemVersion = theTargetCodeSystemVersion; + return this; + } + + String getTargetDisplay() { + return myTargetDisplay; + } + + ConceptMapping setTargetDisplay(String theTargetDisplay) { + myTargetDisplay = theTargetDisplay; + return this; + } + + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java index a53f406851b..ac5d5cdffac 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IRecordHandler; import org.apache.commons.csv.CSVRecord; +import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; import java.util.List; @@ -17,8 +18,8 @@ public class BaseLoincTop2000LabResultsHandler extends BaseHandler implements IR private String myValueSetUri; private String myValueSetName; - public BaseLoincTop2000LabResultsHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, String theValueSetId, String theValueSetUri, String theValueSetName) { - super(theCode2concept, theValueSets); + public BaseLoincTop2000LabResultsHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, String theValueSetId, String theValueSetUri, String theValueSetName, List<ConceptMap> theConceptMaps) { + super(theCode2concept, theValueSets, theConceptMaps); myValueSetId = theValueSetId; myValueSetUri = theValueSetUri; myValueSetName = theValueSetName; @@ -30,7 +31,7 @@ public class BaseLoincTop2000LabResultsHandler extends BaseHandler implements IR String displayName = trim(theRecord.get("Long Common Name")); ValueSet valueSet = getValueSet(myValueSetId, myValueSetUri, myValueSetName); - addCodeAsIncludeToValueSet(valueSet, IHapiTerminologyLoaderSvc.LOINC_URL, loincNumber, displayName); + addCodeAsIncludeToValueSet(valueSet, IHapiTerminologyLoaderSvc.LOINC_URI, loincNumber, displayName); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java index 467df6db5b3..bde2ad61f12 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java @@ -73,7 +73,7 @@ public class LoincAnswerListHandler implements IRecordHandler { vs = new ValueSet(); vs.setUrl("urn:oid:" + answerListOid); vs.addIdentifier() - .setSystem(IHapiTerminologyLoaderSvc.LOINC_URL) + .setSystem(IHapiTerminologyLoaderSvc.LOINC_URI) .setValue(answerListId); vs.setId(answerListId); vs.setName(answerListName); @@ -86,7 +86,7 @@ public class LoincAnswerListHandler implements IRecordHandler { vs .getCompose() .getIncludeFirstRep() - .setSystem(IHapiTerminologyLoaderSvc.LOINC_URL) + .setSystem(IHapiTerminologyLoaderSvc.LOINC_URI) .addConcept() .setCode(answerString) .setDisplay(displayText); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java index 8561ea999c3..7916e06ba1e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java @@ -6,7 +6,7 @@ import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IRecordHandler; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.apache.commons.csv.CSVRecord; -import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; import java.util.*; @@ -22,8 +22,8 @@ public class LoincDocumentOntologyHandler extends BaseHandler implements IRecord private final TermCodeSystemVersion myCodeSystemVersion; private final Set<String> myPropertyNames; - public LoincDocumentOntologyHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, Set<String> thePropertyNames, List<ValueSet> theValueSets) { - super(theCode2concept, theValueSets); + public LoincDocumentOntologyHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, Set<String> thePropertyNames, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { + super(theCode2concept, theValueSets, theConceptMaps); myCodeSystemVersion = theCodeSystemVersion; myCode2Concept = theCode2concept; myPropertyNames = thePropertyNames; @@ -40,7 +40,7 @@ public class LoincDocumentOntologyHandler extends BaseHandler implements IRecord // RSNA Codes VS ValueSet vs = getValueSet(DOCUMENT_ONTOLOGY_CODES_VS_ID, DOCUMENT_ONTOLOGY_CODES_VS_URI, DOCUMENT_ONTOLOGY_CODES_VS_NAME); - addCodeAsIncludeToValueSet(vs, IHapiTerminologyLoaderSvc.LOINC_URL, loincNumber, null); + addCodeAsIncludeToValueSet(vs, IHapiTerminologyLoaderSvc.LOINC_URI, loincNumber, null); // Part Properties String loincCodePropName; @@ -66,7 +66,7 @@ public class LoincDocumentOntologyHandler extends BaseHandler implements IRecord TermConcept code = myCode2Concept.get(loincNumber); if (code != null) { - code.addPropertyCoding(loincCodePropName, IHapiTerminologyLoaderSvc.LOINC_URL, partNumber, partName); + code.addPropertyCoding(loincCodePropName, IHapiTerminologyLoaderSvc.LOINC_URI, partNumber, partName); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java index 3a19582d5e3..b550dadf466 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java @@ -39,7 +39,7 @@ public class LoincHierarchyHandler implements IRecordHandler { TermConcept retVal = myCode2Concept.get(theCode); if (retVal == null) { retVal = new TermConcept(); - retVal.setCodeSystem(myCodeSystemVersion); + retVal.setCodeSystemVersion(myCodeSystemVersion); retVal.setCode(theCode); retVal.setDisplay(theDisplay); myCode2Concept.put(theCode, retVal); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java new file mode 100644 index 00000000000..1375bcea03e --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java @@ -0,0 +1,56 @@ +package ca.uhn.fhir.jpa.term.loinc; + +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; +import ca.uhn.fhir.jpa.term.IRecordHandler; +import org.apache.commons.csv.CSVRecord; +import org.hl7.fhir.r4.model.ConceptMap; +import org.hl7.fhir.r4.model.Enumerations; +import org.hl7.fhir.r4.model.ValueSet; + +import java.util.List; +import java.util.Map; + +import static org.apache.commons.lang3.StringUtils.trim; + +public class LoincIeeeMedicalDeviceCodeHandler extends BaseHandler implements IRecordHandler { + + public static final String LOINC_IEEE_CM_ID = "LOINC-IEEE-MEDICAL-DEVICE-CM"; + public static final String LOINC_IEEE_CM_URI = "http://loinc.org/fhir/loinc-ieee-device-code-mappings"; + public static final String LOINC_IEEE_CM_NAME = "LOINC/IEEE Device Code Mappings"; + + /** + * Constructor + */ + public LoincIeeeMedicalDeviceCodeHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { + super(theCode2concept, theValueSets, theConceptMaps); + } + + @Override + public void accept(CSVRecord theRecord) { + + String loincNumber = trim(theRecord.get("LOINC_NUM")); + String longCommonName = trim(theRecord.get("LOINC_LONG_COMMON_NAME")); + String ieeeCode = trim(theRecord.get("IEEE_CF_CODE10")); + String ieeeDisplayName = trim(theRecord.get("IEEE_REFID")); + + // LOINC Part -> IEEE 11073:10101 Mappings + String sourceCodeSystemUri = IHapiTerminologyLoaderSvc.LOINC_URI; + String targetCodeSystemUri = IHapiTerminologyLoaderSvc.IEEE_11073_10101_URI; + addConceptMapEntry( + new ConceptMapping() + .setConceptMapId(LOINC_IEEE_CM_ID) + .setConceptMapUri(LOINC_IEEE_CM_URI) + .setConceptMapName(LOINC_IEEE_CM_NAME) + .setSourceCodeSystem(sourceCodeSystemUri) + .setSourceCode(loincNumber) + .setSourceDisplay(longCommonName) + .setTargetCodeSystem(targetCodeSystemUri) + .setTargetCode(ieeeCode) + .setTargetDisplay(ieeeDisplayName) + .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL)); + + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java new file mode 100644 index 00000000000..4c58e6187e0 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java @@ -0,0 +1,35 @@ +package ca.uhn.fhir.jpa.term.loinc; + +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; +import ca.uhn.fhir.jpa.term.IRecordHandler; +import org.apache.commons.csv.CSVRecord; +import org.hl7.fhir.r4.model.ConceptMap; +import org.hl7.fhir.r4.model.ValueSet; + +import java.util.List; +import java.util.Map; + +import static org.apache.commons.lang3.StringUtils.trim; + +public class LoincImagingDocumentCodeHandler extends BaseHandler implements IRecordHandler { + + public static final String VS_ID = "loinc-imaging-document-codes"; + public static final String VS_URI = "http://loinc.org/fhir/loinc-imaging-document-codes"; + public static final String VS_NAME = "LOINC Imaging Document Codes"; + + public LoincImagingDocumentCodeHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { + super(theCode2concept, theValueSets, theConceptMaps); + } + + @Override + public void accept(CSVRecord theRecord) { + String loincNumber = trim(theRecord.get("LOINC_NUM")); + String displayName = trim(theRecord.get("LONG_COMMON_NAME")); + + ValueSet valueSet = getValueSet(VS_ID, VS_URI, VS_NAME); + addCodeAsIncludeToValueSet(valueSet, IHapiTerminologyLoaderSvc.LOINC_URI, loincNumber, displayName); + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java index 42590f9d0e5..e0198caacfb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java @@ -6,27 +6,26 @@ import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IRecordHandler; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.apache.commons.csv.CSVRecord; -import org.hl7.fhir.r4.model.CanonicalType; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.Enumerations; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.hl7.fhir.r4.model.ValueSet; import java.util.List; import java.util.Map; -import static org.apache.commons.lang3.StringUtils.defaultIfBlank; import static org.apache.commons.lang3.StringUtils.trim; -public class LoincPartRelatedCodeMappingHandler implements IRecordHandler { +public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements IRecordHandler { - public static final String LOINC_TO_SNOMED_CM_ID = "LOINC-TO-SNOMED-CM"; - private static final Logger ourLog = LoggerFactory.getLogger(LoincPartRelatedCodeMappingHandler.class); + public static final String LOINC_PART_MAP_ID = "LOINC-PART-MAP"; + public static final String LOINC_PART_MAP_URI = "http://loinc.org/fhir/loinc-part-map"; + public static final String LOINC_PART_MAP_NAME = "LOINC Part Map"; private final Map<String, TermConcept> myCode2Concept; private final TermCodeSystemVersion myCodeSystemVersion; private final List<ConceptMap> myConceptMaps; - public LoincPartRelatedCodeMappingHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, List<ConceptMap> theConceptMaps) { + public LoincPartRelatedCodeMappingHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { + super(theCode2concept, theValueSets, theConceptMaps); myCodeSystemVersion = theCodeSystemVersion; myCode2Concept = theCode2concept; myConceptMaps = theConceptMaps; @@ -48,83 +47,37 @@ public class LoincPartRelatedCodeMappingHandler implements IRecordHandler { String extCodeSystemVersion = trim(theRecord.get("ExtCodeSystemVersion")); String extCodeSystemCopyrightNotice = trim(theRecord.get("ExtCodeSystemCopyrightNotice")); - ConceptMap conceptMap; - if (extCodeSystem.equals(IHapiTerminologyLoaderSvc.SCT_URL)) { - conceptMap = findOrAddCodeSystem(LOINC_TO_SNOMED_CM_ID, "http://loinc.org/loinc-to-snomed", extCodeSystem, extCodeSystemCopyrightNotice); - } else { - throw new InternalErrorException("Unknown external code system ID: " + extCodeSystem); - } - - - ConceptMap.ConceptMapGroupComponent group = null; - for (ConceptMap.ConceptMapGroupComponent next : conceptMap.getGroup()) { - if (next.getTarget().equals(extCodeSystem)) { - if (defaultIfBlank(next.getTargetVersion(), "").equals(defaultIfBlank(extCodeSystemVersion, ""))) { - group = next; - break; - } - } - } - - if (group == null) { - group = conceptMap.addGroup(); - group.setSource(IHapiTerminologyLoaderSvc.LOINC_URL); - group.setTarget(extCodeSystem); - group.setTargetVersion(defaultIfBlank(extCodeSystemVersion, null)); - } - - ConceptMap.SourceElementComponent element = null; - for (ConceptMap.SourceElementComponent next : group.getElement()) { - if (next.getCode().equals(partNumber)) { - element = next; - break; - } - } - - if (element == null) { - element = group - .addElement() - .setCode(partNumber) - .setDisplay(partName); - } - - ConceptMap.TargetElementComponent target = element - .addTarget() - .setCode(extCodeId) - .setDisplay(extCodeDisplayName); - + Enumerations.ConceptMapEquivalence equivalence; switch (mapType) { case "Exact": // 'equal' is more exact than 'equivalent' in the equivalence codes - target.setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL); + equivalence = Enumerations.ConceptMapEquivalence.EQUAL; break; case "LOINC broader": - target.setEquivalence(Enumerations.ConceptMapEquivalence.NARROWER); + equivalence = Enumerations.ConceptMapEquivalence.NARROWER; break; case "LOINC narrower": - target.setEquivalence(Enumerations.ConceptMapEquivalence.WIDER); + equivalence = Enumerations.ConceptMapEquivalence.WIDER; break; default: throw new InternalErrorException("Unknown MapType: " + mapType); } + addConceptMapEntry( + new ConceptMapping() + .setConceptMapId(LOINC_PART_MAP_ID) + .setConceptMapUri(LOINC_PART_MAP_URI) + .setConceptMapName(LOINC_PART_MAP_NAME) + .setSourceCodeSystem(IHapiTerminologyLoaderSvc.LOINC_URI) + .setSourceCode(partNumber) + .setSourceDisplay(partName) + .setTargetCodeSystem(extCodeSystem) + .setTargetCode(extCodeId) + .setTargetDisplay(extCodeDisplayName) + .setTargetCodeSystemVersion(extCodeSystemVersion) + .setEquivalence(equivalence) + .setCopyright(extCodeSystemCopyrightNotice)); } - private ConceptMap findOrAddCodeSystem(String theId, String theUri, String theTargetCodeSystem, String theTargetCopyright) { - for (ConceptMap next : myConceptMaps) { - if (next.getId().equals(theId)) { - return next; - } - } - - ConceptMap cm = new ConceptMap(); - cm.setId(theId); - cm.setUrl(theUri); - cm.setSource(new CanonicalType(IHapiTerminologyLoaderSvc.LOINC_URL)); - cm.setTarget(new CanonicalType(theTargetCodeSystem)); - cm.setCopyright(theTargetCopyright); - myConceptMaps.add(cm); - return cm; - } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java index 306359f66ff..7f1efd71e15 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java @@ -6,7 +6,6 @@ import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IRecordHandler; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.apache.commons.csv.CSVRecord; -import org.hl7.fhir.r4.model.CanonicalType; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.ValueSet; @@ -16,7 +15,7 @@ import java.util.*; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; -public class LoincRsnaPlaybookHandler implements IRecordHandler { +public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHandler { public static final String RSNA_CODES_VS_ID = "RSNA-LOINC-CODES-VS"; public static final String RSNA_CODES_VS_URI = "http://loinc.org/rsna-codes"; @@ -34,19 +33,17 @@ public class LoincRsnaPlaybookHandler implements IRecordHandler { private final Set<String> myPropertyNames; private final List<ValueSet> myValueSets; private final Map<String, ValueSet> myIdToValueSet = new HashMap<>(); - private final List<ConceptMap> myConceptMaps; private final Set<String> myCodesInRsnaPlaybookValueSet = new HashSet<>(); - private final Map<String, ConceptMap> myIdToConceptMaps = new HashMap<>(); /** * Constructor */ public LoincRsnaPlaybookHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, Set<String> thePropertyNames, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { + super(theCode2concept, theValueSets, theConceptMaps); myCodeSystemVersion = theCodeSystemVersion; myCode2Concept = theCode2concept; myPropertyNames = thePropertyNames; myValueSets = theValueSets; - myConceptMaps = theConceptMaps; } @Override @@ -81,7 +78,7 @@ public class LoincRsnaPlaybookHandler implements IRecordHandler { vs .getCompose() .getIncludeFirstRep() - .setSystem(IHapiTerminologyLoaderSvc.LOINC_URL) + .setSystem(IHapiTerminologyLoaderSvc.LOINC_URI) .addConcept() .setCode(loincNumber) .setDisplay(longCommonName); @@ -105,61 +102,42 @@ public class LoincRsnaPlaybookHandler implements IRecordHandler { TermConcept code = myCode2Concept.get(loincNumber); if (code != null) { - code.addPropertyCoding(loincCodePropName, IHapiTerminologyLoaderSvc.LOINC_URL, partNumber, partName); + code.addPropertyCoding(loincCodePropName, IHapiTerminologyLoaderSvc.LOINC_URI, partNumber, partName); } // LOINC Part -> Radlex RID code mappings - addMapping(partNumber, partName, RID_MAPPING_CM_ID, RID_MAPPING_CM_URI, RID_MAPPING_CM_NAME, RID_CS_URI, rid, preferredName, Enumerations.ConceptMapEquivalence.EQUAL); + if (isNotBlank(rid)) { + addConceptMapEntry( + new ConceptMapping() + .setConceptMapId(RID_MAPPING_CM_ID) + .setConceptMapUri(RID_MAPPING_CM_URI) + .setConceptMapName(RID_MAPPING_CM_NAME) + .setSourceCodeSystem(IHapiTerminologyLoaderSvc.LOINC_URI) + .setSourceCode(partNumber) + .setSourceDisplay(partName) + .setTargetCodeSystem(RID_CS_URI) + .setTargetCode(rid) + .setTargetDisplay(preferredName) + .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL)); + } // LOINC Term -> Radlex RPID code mappings - addMapping(loincNumber, longCommonName, RPID_MAPPING_CM_ID, RPID_MAPPING_CM_URI, RPID_MAPPING_CM_NAME, RPID_CS_URI, rpid, longName, Enumerations.ConceptMapEquivalence.EQUAL); - - } - - private void addMapping(String theLoincNumber, String theLongCommonName, String theConceptMapId, String theConceptMapUri, String theConceptMapName, String theTargetCodeSystemUri, String theTargetCode, String theTargetDisplay, Enumerations.ConceptMapEquivalence theEquivalence) { - if (isNotBlank(theTargetCode)) { - - ConceptMap conceptMap; - if (!myIdToConceptMaps.containsKey(theConceptMapId)) { - conceptMap = new ConceptMap(); - conceptMap.setId(theConceptMapId); - conceptMap.setUrl(theConceptMapUri); - conceptMap.setName(theConceptMapName); - conceptMap.setSource(new CanonicalType(IHapiTerminologyLoaderSvc.LOINC_URL)); - conceptMap.setTarget(new CanonicalType(theTargetCodeSystemUri)); - myIdToConceptMaps.put(theConceptMapId, conceptMap); - myConceptMaps.add(conceptMap); - } else { - conceptMap = myIdToConceptMaps.get(theConceptMapId); - } - - ConceptMap.SourceElementComponent source = null; - ConceptMap.ConceptMapGroupComponent group = conceptMap.getGroupFirstRep(); - for (ConceptMap.SourceElementComponent next : group.getElement()) { - if (next.getCode().equals(theLoincNumber)) { - source = next; - } - } - if (source == null) { - source = group.addElement(); - source.setCode(theLoincNumber); - source.setDisplay(theLongCommonName); - } - - boolean found = false; - for (ConceptMap.TargetElementComponent next : source.getTarget()) { - if (next.getCode().equals(theTargetCode)) { - found = true; - } - } - if (!found) { - source - .addTarget() - .setCode(theTargetCode) - .setDisplay(theTargetDisplay) - .setEquivalence(theEquivalence); - } + if (isNotBlank(rpid)) { + addConceptMapEntry( + new ConceptMapping() + .setConceptMapId(RPID_MAPPING_CM_ID) + .setConceptMapUri(RPID_MAPPING_CM_URI) + .setConceptMapName(RPID_MAPPING_CM_NAME) + .setSourceCodeSystem(IHapiTerminologyLoaderSvc.LOINC_URI) + .setSourceCode(loincNumber) + .setSourceDisplay(longCommonName) + .setTargetCodeSystem(RPID_CS_URI) + .setTargetCode(rpid) + .setTargetDisplay(longName) + .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL)); } + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java index dd9bd16685d..7b0310a76f0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.term.loinc; import ca.uhn.fhir.jpa.entity.TermConcept; +import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; import java.util.List; @@ -12,8 +13,8 @@ public class LoincTop2000LabResultsSiHandler extends BaseLoincTop2000LabResultsH public static final String TOP_2000_SI_VS_URI = "http://loinc.org/top-2000-lab-results-si"; public static final String TOP_2000_SI_VS_NAME = "Top 2000 Lab Results SI"; - public LoincTop2000LabResultsSiHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets) { - super(theCode2concept, theValueSets, TOP_2000_SI_VS_ID, TOP_2000_SI_VS_URI, TOP_2000_SI_VS_NAME); + public LoincTop2000LabResultsSiHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { + super(theCode2concept, theValueSets, TOP_2000_SI_VS_ID, TOP_2000_SI_VS_URI, TOP_2000_SI_VS_NAME, theConceptMaps); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java index 4c887bd8a56..70f369b1f0f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.term.loinc; import ca.uhn.fhir.jpa.entity.TermConcept; +import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; import java.util.List; @@ -12,8 +13,8 @@ public class LoincTop2000LabResultsUsHandler extends BaseLoincTop2000LabResultsH public static final String TOP_2000_US_VS_URI = "http://loinc.org/top-2000-lab-results-us"; public static final String TOP_2000_US_VS_NAME = "Top 2000 Lab Results US"; - public LoincTop2000LabResultsUsHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets) { - super(theCode2concept, theValueSets, TOP_2000_US_VS_ID, TOP_2000_US_VS_URI, TOP_2000_US_VS_NAME); + public LoincTop2000LabResultsUsHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { + super(theCode2concept, theValueSets, TOP_2000_US_VS_ID, TOP_2000_US_VS_URI, TOP_2000_US_VS_NAME, theConceptMaps); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java new file mode 100644 index 00000000000..34208471bd9 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java @@ -0,0 +1,35 @@ +package ca.uhn.fhir.jpa.term.loinc; + +import ca.uhn.fhir.jpa.entity.TermConcept; +import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; +import ca.uhn.fhir.jpa.term.IRecordHandler; +import org.apache.commons.csv.CSVRecord; +import org.hl7.fhir.r4.model.ConceptMap; +import org.hl7.fhir.r4.model.ValueSet; + +import java.util.*; + +import static org.apache.commons.lang3.StringUtils.trim; + +public class LoincUniversalOrderSetHandler extends BaseHandler implements IRecordHandler { + + public static final String VS_ID = "loinc-universal-order-set-vs"; + public static final String VS_URI = "http://loinc.org/fhir/loinc-universal-order-set"; + public static final String VS_NAME = "LOINC Universal Order Set"; + + public LoincUniversalOrderSetHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps) { + super(theCode2concept, theValueSets, theConceptMaps); + } + + @Override + public void accept(CSVRecord theRecord) { + String loincNumber = trim(theRecord.get("LOINC_NUM")); + String displayName = trim(theRecord.get("LONG_COMMON_NAME")); + String orderObs = trim(theRecord.get("ORDER_OBS")); + + ValueSet valueSet = getValueSet(VS_ID, VS_URI, VS_NAME); + addCodeAsIncludeToValueSet(valueSet, IHapiTerminologyLoaderSvc.LOINC_URI, loincNumber, displayName); + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java index 2ec3bc3b1fa..d23f71a591c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3TerminologyTest.java @@ -96,7 +96,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { TermConcept childCA = new TermConcept(cs, "childCA").setDisplay("Child CA"); parentC.addChild(childCA, RelationshipTypeEnum.ISA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs); return codeSystem; } @@ -127,7 +127,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { parentB.addChild(childI, RelationshipTypeEnum.ISA); } - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs); return codeSystem; } @@ -163,7 +163,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle"); dogs.addChild(beagle, RelationshipTypeEnum.ISA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM,"SYSTEM NAME" , cs); return codeSystem; } @@ -713,7 +713,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test { cs.setResource(table); TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); cs.getConcepts().add(parentA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", "Snomed CT", cs); StringType code = new StringType("ParentA"); StringType system = new StringType("http://snomed.info/sct"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java index 19f7e28e6ed..f2ceb4e8fdf 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4TerminologyTest.java @@ -96,7 +96,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { TermConcept childCA = new TermConcept(cs, "childCA").setDisplay("Child CA"); parentC.addChild(childCA, RelationshipTypeEnum.ISA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM,"SYSTEM NAME" , cs); return codeSystem; } @@ -127,7 +127,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { parentB.addChild(childI, RelationshipTypeEnum.ISA); } - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM,"SYSTEM NAME" , cs); return codeSystem; } @@ -163,7 +163,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle"); dogs.addChild(beagle, RelationshipTypeEnum.ISA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM,"SYSTEM NAME" , cs); return codeSystem; } @@ -713,7 +713,7 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test { cs.setResource(table); TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A"); cs.getConcepts().add(parentA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct","Snomed CT" , cs); StringType code = new StringType("ParentA"); StringType system = new StringType("http://snomed.info/sct"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UniqueSearchParamTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UniqueSearchParamTest.java index 26aff71e15f..d92125279dd 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UniqueSearchParamTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4UniqueSearchParamTest.java @@ -19,6 +19,7 @@ import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import org.springframework.orm.jpa.JpaSystemException; +import org.springframework.test.context.TestPropertySource; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; @@ -33,6 +34,11 @@ import static org.hamcrest.Matchers.empty; import static org.junit.Assert.*; @SuppressWarnings({"unchecked", "deprecation"}) +@TestPropertySource(properties = { + // Since scheduled tasks can cause searches, which messes up the + // value returned by SearchBuilder.getLastHandlerMechanismForUnitTest() + "scheduling_disabled=true" +}) public class FhirResourceDaoR4UniqueSearchParamTest extends BaseJpaR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4UniqueSearchParamTest.class); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/AuthorizationInterceptorResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/AuthorizationInterceptorResourceProviderDstu3Test.java index 7f6afcbea30..353d2d5dca5 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/AuthorizationInterceptorResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/AuthorizationInterceptorResourceProviderDstu3Test.java @@ -20,6 +20,7 @@ import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Observation.ObservationStatus; import org.hl7.fhir.dstu3.model.Patient; +import org.hl7.fhir.dstu3.model.Practitioner; import org.hl7.fhir.dstu3.model.Reference; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.AfterClass; @@ -27,6 +28,7 @@ import org.junit.Test; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static org.hamcrest.Matchers.startsWith; @@ -41,6 +43,64 @@ public class AuthorizationInterceptorResourceProviderDstu3Test extends BaseResou unregisterInterceptors(); } + /** + * See #778 + */ + @Test + public void testReadingObservationAccessRight() throws IOException { + Practitioner practitioner1 = new Practitioner(); + final IIdType practitionerId1 = ourClient.create().resource(practitioner1).execute().getId().toUnqualifiedVersionless(); + + Practitioner practitioner2 = new Practitioner(); + final IIdType practitionerId2 = ourClient.create().resource(practitioner2).execute().getId().toUnqualifiedVersionless(); + + Patient patient = new Patient(); + patient.setActive(true); + final IIdType patientId = ourClient.create().resource(patient).execute().getId().toUnqualifiedVersionless(); + + ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) { + // allow write all Observation resource + // allow read only Observation resource in which it has a practitioner1 or practitioner2 compartment + return new RuleBuilder().allow() + .write() + .resourcesOfType(Observation.class) + .withAnyId() + .andThen() + .allow() + .read() + .resourcesOfType(Observation.class) + .inCompartment("Practitioner", Arrays.asList(practitionerId1, practitionerId2)) + .andThen() + .denyAll() + .build(); + } + }); + + Observation obs1 = new Observation(); + obs1.setStatus(ObservationStatus.FINAL); + obs1.setPerformer( + Arrays.asList(new Reference(practitionerId1), new Reference(practitionerId2))); + IIdType oid1 = ourClient.create().resource(obs1).execute().getId().toUnqualified(); + + // Observation with practitioner1 and practitioner1 as the Performer -> should have the read access + ourClient.read().resource(Observation.class).withId(oid1).execute(); + + Observation obs2 = new Observation(); + obs2.setStatus(ObservationStatus.FINAL); + obs2.setSubject(new Reference(patientId)); + IIdType oid2 = ourClient.create().resource(obs2).execute().getId().toUnqualified(); + + // Observation with patient as the subject -> read access should be blocked + try { + ourClient.read().resource(Observation.class).withId(oid2).execute(); + fail(); + } catch (ForbiddenOperationException e) { + // good + } + } + /** * See #667 */ diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java index a227dc114da..962286ef449 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig; import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; import ca.uhn.fhir.jpa.dao.dstu3.SearchParamRegistryDstu3; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor; @@ -56,7 +57,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test { private static Server ourServer; protected static String ourServerBase; protected static GenericWebApplicationContext ourWebApplicationContext; - private TerminologyUploaderProviderDstu3 myTerminologyUploaderProvider; + private TerminologyUploaderProvider myTerminologyUploaderProvider; protected static SearchParamRegistryDstu3 ourSearchParamRegistry; protected static DatabaseBackedPagingProvider ourPagingProvider; protected static SubscriptionRestHookInterceptor ourRestHookSubscriptionInterceptor; @@ -92,7 +93,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test { ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderDstu3.class); + myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class); ourRestServer.setPlainProviders(mySystemProvider, myTerminologyUploaderProvider); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java index e3cdef70f0b..3e7978030ce 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3ValueSetTest.java @@ -570,7 +570,7 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3 TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B"); cs.getConcepts().add(parentB); - theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); + theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, "SYSTEM NAME", cs); return codeSystem; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java index c62998f3bfd..ba63454dc78 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java @@ -47,7 +47,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL)) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .andParameter("package", new Attachment().setData(packageBytes)) .execute(); //@formatter:on @@ -67,7 +67,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URL)) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) .andParameter("package", new Attachment().setData(packageBytes)) .execute(); //@formatter:on @@ -86,7 +86,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URL)) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) .andParameter("package", new Attachment().setData(packageBytes)) .execute(); //@formatter:on @@ -111,7 +111,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL)) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .andParameter("localfile", new StringType(tempFile.getAbsolutePath())) .execute(); //@formatter:on @@ -132,7 +132,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL + "FOO")) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI + "FOO")) .andParameter("package", new Attachment().setData(packageBytes)) .execute(); fail(); @@ -169,7 +169,7 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL)) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .execute(); fail(); } catch (InvalidRequestException e) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java index dc9d4ff4572..d95368bd101 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/AuthorizationInterceptorResourceProviderR4Test.java @@ -25,6 +25,7 @@ import org.junit.Test; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static org.hamcrest.Matchers.startsWith; @@ -39,6 +40,64 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource unregisterInterceptors(); } + /** + * See #778 + */ + @Test + public void testReadingObservationAccessRight() throws IOException { + Practitioner practitioner1 = new Practitioner(); + final IIdType practitionerId1 = myClient.create().resource(practitioner1).execute().getId().toUnqualifiedVersionless(); + + Practitioner practitioner2 = new Practitioner(); + final IIdType practitionerId2 = myClient.create().resource(practitioner2).execute().getId().toUnqualifiedVersionless(); + + Patient patient = new Patient(); + patient.setActive(true); + final IIdType patientId = myClient.create().resource(patient).execute().getId().toUnqualifiedVersionless(); + + ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) { + @Override + public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) { + // allow write all Observation resource + // allow read only Observation resource in which it has a practitioner1 or practitioner2 compartment + return new RuleBuilder().allow() + .write() + .resourcesOfType(Observation.class) + .withAnyId() + .andThen() + .allow() + .read() + .resourcesOfType(Observation.class) + .inCompartment("Practitioner", Arrays.asList(practitionerId1, practitionerId2)) + .andThen() + .denyAll() + .build(); + } + }); + + Observation obs1 = new Observation(); + obs1.setStatus(ObservationStatus.FINAL); + obs1.setPerformer( + Arrays.asList(new Reference(practitionerId1), new Reference(practitionerId2))); + IIdType oid1 = myClient.create().resource(obs1).execute().getId().toUnqualified(); + + // Observation with practitioner1 and practitioner1 as the Performer -> should have the read access + myClient.read().resource(Observation.class).withId(oid1).execute(); + + Observation obs2 = new Observation(); + obs2.setStatus(ObservationStatus.FINAL); + obs2.setSubject(new Reference(patientId)); + IIdType oid2 = myClient.create().resource(obs2).execute().getId().toUnqualified(); + + // Observation with patient as the subject -> read access should be blocked + try { + myClient.read().resource(Observation.class).withId(oid2).execute(); + fail(); + } catch (ForbiddenOperationException e) { + // good + } + } + /** * See #667 */ diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java index 12e8f7f0967..28f3fd52b29 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig; import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; import ca.uhn.fhir.jpa.dao.r4.SearchParamRegistryR4; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor; @@ -58,7 +59,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test { protected static ISearchCoordinatorSvc mySearchCoordinatorSvc; private static Server ourServer; protected static GenericWebApplicationContext ourWebApplicationContext; - private TerminologyUploaderProviderR4 myTerminologyUploaderProvider; + private TerminologyUploaderProvider myTerminologyUploaderProvider; private Object ourGraphQLProvider; private boolean ourRestHookSubscriptionInterceptorRequested; @@ -89,7 +90,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test { ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderR4.class); + myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class); ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider"); ourRestServer.setPlainProviders(mySystemProvider, myTerminologyUploaderProvider, ourGraphQLProvider); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index ac59e02ab93..a54f95a4fb7 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -511,7 +511,7 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { TermConcept parentB = new TermConcept(cs, "ParentB").setDisplay("Parent B"); cs.getConcepts().add(parentB); - theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs); + theTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM,"SYSTEM NAME" , cs); return codeSystem; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java index e15dee56274..62aeba4f74d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java @@ -47,7 +47,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL)) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .andParameter("package", new Attachment().setData(packageBytes)) .execute(); //@formatter:on @@ -67,7 +67,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URL)) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) .andParameter("package", new Attachment().setData(packageBytes)) .execute(); //@formatter:on @@ -86,7 +86,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URL)) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) .andParameter("package", new Attachment().setData(packageBytes)) .execute(); //@formatter:on @@ -111,7 +111,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL)) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .andParameter("localfile", new StringType(tempFile.getAbsolutePath())) .execute(); //@formatter:on @@ -132,7 +132,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL + "FOO")) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI + "FOO")) .andParameter("package", new Attachment().setData(packageBytes)) .execute(); fail(); @@ -169,7 +169,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URL)) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .execute(); fail(); } catch (InvalidRequestException e) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu3Test.java index f6c75c605b4..ee96aefb4f4 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu3Test.java @@ -34,7 +34,7 @@ import static org.junit.Assert.fail; */ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestDstu2Test.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestDstu3Test.class); private static List<Observation> ourCreatedObservations = Lists.newArrayList(); private static int ourListenerPort; private static RestfulServer ourListenerRestServer; @@ -311,6 +311,25 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test { assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0)); } + @Test + public void testRestHookSubscriptionWithoutPayload() throws Exception { + String payload = ""; + + String code = "1000000050"; + String criteria1 = "Observation?code=SNOMED-CT|" + code; + String criteria2 = "Observation?code=SNOMED-CT|" + code + "111"; + + Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase); + Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase); + + Observation observation1 = sendObservation(code, "SNOMED-CT"); + + // Should see 1 subscription notification, but no payload + waitForQueueToDrain(); + waitForSize(0, ourCreatedObservations); + waitForSize(0, ourUpdatedObservations); + } + // TODO: Reenable this @Test @Ignore diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java index 3bb0931e206..15bcb11848b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java @@ -5,9 +5,7 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.convertors.VersionConvertor_30_40; -import org.hl7.fhir.dstu3.model.StringType; -import org.hl7.fhir.dstu3.model.ValueSet; -import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.*; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -16,12 +14,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import java.util.ArrayList; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { @@ -44,14 +42,15 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { @Test public void testExpandWithProperty() throws Exception { ZipCollectionBuilder files = new ZipCollectionBuilder(); - TerminologyLoaderSvcLoincTest.createLoincBundle(files); + TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files); + TerminologyLoaderSvcLoincTest.addLoincOptionalFilesToZip(files); myLoader.loadLoinc(files.getFiles(), mySrd); ValueSet input = new ValueSet(); input .getCompose() .addInclude() - .setSystem(IHapiTerminologyLoaderSvc.LOINC_URL) + .setSystem(IHapiTerminologyLoaderSvc.LOINC_URI) .addFilter() .setProperty("SCALE_TYP") .setOp(ValueSet.FilterOperator.EQUAL) @@ -65,14 +64,30 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { @Test public void testLookupWithProperties() throws Exception { ZipCollectionBuilder files = new ZipCollectionBuilder(); - TerminologyLoaderSvcLoincTest.createLoincBundle(files); + TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files); + TerminologyLoaderSvcLoincTest.addLoincOptionalFilesToZip(files); myLoader.loadLoinc(files.getFiles(), mySrd); - IFhirResourceDaoCodeSystem.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URL), null, mySrd); + IFhirResourceDaoCodeSystem.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd); org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(); Parameters parameters = VersionConvertor_30_40.convertParameters(parametersR4); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parameters)); + + assertEquals("SYSTEM", this.<CodeType>getPropertyPart(parameters, "property", "code").get().getValueAsString()); + assertEquals("Heart", this.<CodeType>getPropertyPart(parameters, "property", "value").get().getValueAsString()); + + } + + private <T extends Type> Optional<T> getPropertyPart(Parameters theParameters, String thePropName, String thePart) { + return theParameters + .getParameter() + .stream() + .filter(t -> t.getName().equals(thePropName)) + .flatMap(t -> t.getPart().stream()) + .filter(t -> t.getName().equals(thePart)) + .map(t -> (T)t.getValue()) + .findFirst(); } private Set<String> toExpandedCodes(ValueSet theExpanded) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java index 4916fa68e37..7a233c20956 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java @@ -1,9 +1,11 @@ package ca.uhn.fhir.jpa.term; +import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.loinc.*; import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ConceptMap; @@ -24,6 +26,7 @@ import java.util.List; import java.util.Map; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.empty; import static org.junit.Assert.*; import static org.mockito.Matchers.any; import static org.mockito.Mockito.times; @@ -62,38 +65,43 @@ public class TerminologyLoaderSvcLoincTest { myFiles = new ZipCollectionBuilder(); } + private Map<String, ConceptMap> extractConceptMaps() { + Map<String, ConceptMap> conceptMaps = new HashMap<>(); + for (ConceptMap next : myConceptMapCaptor.getAllValues().get(0)) { + conceptMaps.put(next.getId(), next); + } + return conceptMaps; + } + + private Map<String, TermConcept> extractConcepts() { + Map<String, TermConcept> concepts = new HashMap<>(); + for (TermConcept next : myCsvCaptor.getValue().getConcepts()) { + concepts.put(next.getCode(), next); + } + return concepts; + } + + private Map<String, ValueSet> extractValueSets() { + Map<String, ValueSet> valueSets = new HashMap<>(); + for (ValueSet next : myValueSetsCaptor.getValue()) { + valueSets.put(next.getId(), next); + } + return valueSets; + } + @Test public void testLoadLoinc() throws Exception { - createLoincBundle(myFiles); + addLoincMandatoryFilesToZip(myFiles); + addLoincOptionalFilesToZip(myFiles); // Actually do the load mySvc.loadLoinc(myFiles.getFiles(), details); verify(myTermSvcDstu3, times(1)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture()); - ValueSet input = new ValueSet(); - input - .getCompose() - .addInclude() - .setSystem(IHapiTerminologyLoaderSvc.LOINC_URL) - .addFilter() - .setProperty("SCALE_TYP") - .setOp(ValueSet.FilterOperator.EQUAL) - .setValue("Ord"); + Map<String, TermConcept> concepts = extractConcepts(); + Map<String, ValueSet> valueSets = extractValueSets(); + Map<String, ConceptMap> conceptMaps = extractConceptMaps(); - TermCodeSystemVersion ver = myCsvCaptor.getValue(); - - Map<String, TermConcept> concepts = new HashMap<>(); - for (TermConcept next : ver.getConcepts()) { - concepts.put(next.getCode(), next); - } - Map<String, ValueSet> valueSets = new HashMap<>(); - for (ValueSet next : myValueSetsCaptor.getValue()) { - valueSets.put(next.getId(), next); - } - Map<String, ConceptMap> conceptMaps = new HashMap<>(); - for (ConceptMap next : myConceptMapCaptor.getAllValues().get(0)) { - conceptMaps.put(next.getId(), next); - } ConceptMap conceptMap; TermConcept code; ValueSet vs; @@ -127,13 +135,13 @@ public class TerminologyLoaderSvcLoincTest { // AnswerList valueSet vs = valueSets.get("LL1001-8"); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, vs.getIdentifier().get(0).getSystem()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, vs.getIdentifier().get(0).getSystem()); assertEquals("LL1001-8", vs.getIdentifier().get(0).getValue()); assertEquals("PhenX05_14_30D freq amts", vs.getName()); assertEquals("urn:oid:1.3.6.1.4.1.12009.10.1.166", vs.getUrl()); assertEquals(1, vs.getCompose().getInclude().size()); assertEquals(7, vs.getCompose().getInclude().get(0).getConcept().size()); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, vs.getCompose().getInclude().get(0).getSystem()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, vs.getCompose().getInclude().get(0).getSystem()); assertEquals("LA6270-8", vs.getCompose().getInclude().get(0).getConcept().get(0).getCode()); assertEquals("Never", vs.getCompose().getInclude().get(0).getConcept().get(0).getDisplay()); @@ -143,14 +151,14 @@ public class TerminologyLoaderSvcLoincTest { assertEquals("adjusted for maternal weight", code.getDisplay()); // Part Mappings - conceptMap = conceptMaps.get(LoincPartRelatedCodeMappingHandler.LOINC_TO_SNOMED_CM_ID); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, conceptMap.getSourceCanonicalType().getValueAsString()); - assertEquals(IHapiTerminologyLoaderSvc.SCT_URL, conceptMap.getTargetCanonicalType().getValueAsString()); + conceptMap = conceptMaps.get(LoincPartRelatedCodeMappingHandler.LOINC_PART_MAP_ID); + assertEquals(null, conceptMap.getSource()); + assertEquals(null, conceptMap.getTarget()); assertEquals("This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries.", conceptMap.getCopyright()); assertEquals(1, conceptMap.getGroup().size()); group = conceptMap.getGroup().get(0); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, group.getSource()); - assertEquals(IHapiTerminologyLoaderSvc.SCT_URL, group.getTarget()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, group.getSource()); + assertEquals(IHapiTerminologyLoaderSvc.SCT_URI, group.getTarget()); assertEquals("http://snomed.info/sct/900000000000207008/version/20170731", group.getTargetVersion()); assertEquals("LP18172-4", group.getElement().get(0).getCode()); assertEquals("Interferon.beta", group.getElement().get(0).getDisplay()); @@ -163,7 +171,7 @@ public class TerminologyLoaderSvcLoincTest { assertEquals(LoincDocumentOntologyHandler.DOCUMENT_ONTOLOGY_CODES_VS_NAME, vs.getName()); assertEquals(LoincDocumentOntologyHandler.DOCUMENT_ONTOLOGY_CODES_VS_URI, vs.getUrl()); assertEquals(1, vs.getCompose().getInclude().size()); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, vs.getCompose().getInclude().get(0).getSystem()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, vs.getCompose().getInclude().get(0).getSystem()); assertEquals(3, vs.getCompose().getInclude().get(0).getConcept().size()); assertEquals("11488-4", vs.getCompose().getInclude().get(0).getConcept().get(0).getCode()); assertEquals("Consult note", vs.getCompose().getInclude().get(0).getConcept().get(0).getDisplay()); @@ -171,7 +179,7 @@ public class TerminologyLoaderSvcLoincTest { // Document ontology parts code = concepts.get("11488-4"); assertEquals(1, code.getCodingProperties("document-kind").size()); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, code.getCodingProperties("document-kind").get(0).getSystem()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, code.getCodingProperties("document-kind").get(0).getSystem()); assertEquals("LP173418-7", code.getCodingProperties("document-kind").get(0).getCode()); assertEquals("Note", code.getCodingProperties("document-kind").get(0).getDisplay()); @@ -181,7 +189,7 @@ public class TerminologyLoaderSvcLoincTest { assertEquals(LoincRsnaPlaybookHandler.RSNA_CODES_VS_URI, vs.getUrl()); assertEquals(1, vs.getCompose().getInclude().size()); assertEquals(3, vs.getCompose().getInclude().get(0).getConcept().size()); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, vs.getCompose().getInclude().get(0).getSystem()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, vs.getCompose().getInclude().get(0).getSystem()); assertEquals("17787-3", vs.getCompose().getInclude().get(0).getConcept().get(0).getCode()); assertEquals("NM Thyroid gland Study report", vs.getCompose().getInclude().get(0).getConcept().get(0).getDisplay()); @@ -189,21 +197,21 @@ public class TerminologyLoaderSvcLoincTest { code = concepts.get("17787-3"); String propertyName = "rad-anatomic-location-region-imaged"; assertEquals(1, code.getCodingProperties(propertyName).size()); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, code.getCodingProperties(propertyName).get(0).getSystem()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, code.getCodingProperties(propertyName).get(0).getSystem()); assertEquals("LP199995-4", code.getCodingProperties(propertyName).get(0).getCode()); assertEquals("Neck", code.getCodingProperties(propertyName).get(0).getDisplay()); // RSNA Playbook Code Parts - Imaging Focus code = concepts.get("17787-3"); propertyName = "rad-anatomic-location-imaging-focus"; assertEquals(1, code.getCodingProperties(propertyName).size()); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, code.getCodingProperties(propertyName).get(0).getSystem()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, code.getCodingProperties(propertyName).get(0).getSystem()); assertEquals("LP206648-0", code.getCodingProperties(propertyName).get(0).getCode()); assertEquals("Thyroid gland", code.getCodingProperties(propertyName).get(0).getDisplay()); // RSNA Playbook Code Parts - Modality Type code = concepts.get("17787-3"); propertyName = "rad-modality-modality-type"; assertEquals(1, code.getCodingProperties(propertyName).size()); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, code.getCodingProperties(propertyName).get(0).getSystem()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, code.getCodingProperties(propertyName).get(0).getSystem()); assertEquals("LP208891-4", code.getCodingProperties(propertyName).get(0).getCode()); assertEquals("NM", code.getCodingProperties(propertyName).get(0).getDisplay()); @@ -214,8 +222,8 @@ public class TerminologyLoaderSvcLoincTest { assertEquals(1, conceptMap.getGroup().size()); group = conceptMap.getGroupFirstRep(); // all entries have the same source and target so these should be null - assertEquals(null, group.getSource()); - assertEquals(null, group.getTarget()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, group.getSource()); + assertEquals(LoincRsnaPlaybookHandler.RID_CS_URI, group.getTarget()); assertEquals("LP199995-4", group.getElement().get(0).getCode()); assertEquals("Neck", group.getElement().get(0).getDisplay()); assertEquals(1, group.getElement().get(0).getTarget().size()); @@ -230,8 +238,8 @@ public class TerminologyLoaderSvcLoincTest { assertEquals(1, conceptMap.getGroup().size()); group = conceptMap.getGroupFirstRep(); // all entries have the same source and target so these should be null - assertEquals(null, group.getSource()); - assertEquals(null, group.getTarget()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, group.getSource()); + assertEquals(LoincRsnaPlaybookHandler.RPID_CS_URI, group.getTarget()); assertEquals("24531-6", group.getElement().get(0).getCode()); assertEquals("US Retroperitoneum", group.getElement().get(0).getDisplay()); assertEquals(1, group.getElement().get(0).getTarget().size()); @@ -244,7 +252,7 @@ public class TerminologyLoaderSvcLoincTest { assertEquals(vs.getName(), LoincTop2000LabResultsUsHandler.TOP_2000_US_VS_NAME); assertEquals(vs.getUrl(), LoincTop2000LabResultsUsHandler.TOP_2000_US_VS_URI); assertEquals(1, vs.getCompose().getInclude().size()); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, vs.getCompose().getInclude().get(0).getSystem()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, vs.getCompose().getInclude().get(0).getSystem()); assertEquals(9, vs.getCompose().getInclude().get(0).getConcept().size()); assertEquals("2160-0", vs.getCompose().getInclude().get(0).getConcept().get(0).getCode()); assertEquals("Creatinine [Mass/volume] in Serum or Plasma", vs.getCompose().getInclude().get(0).getConcept().get(0).getDisplay()); @@ -256,13 +264,109 @@ public class TerminologyLoaderSvcLoincTest { assertEquals(vs.getName(), LoincTop2000LabResultsSiHandler.TOP_2000_SI_VS_NAME); assertEquals(vs.getUrl(), LoincTop2000LabResultsSiHandler.TOP_2000_SI_VS_URI); assertEquals(1, vs.getCompose().getInclude().size()); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URL, vs.getCompose().getInclude().get(0).getSystem()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, vs.getCompose().getInclude().get(0).getSystem()); assertEquals(9, vs.getCompose().getInclude().get(0).getConcept().size()); assertEquals("14682-9", vs.getCompose().getInclude().get(0).getConcept().get(0).getCode()); assertEquals("Creatinine [Moles/volume] in Serum or Plasma", vs.getCompose().getInclude().get(0).getConcept().get(0).getDisplay()); assertEquals("718-7", vs.getCompose().getInclude().get(0).getConcept().get(1).getCode()); assertEquals("Hemoglobin [Mass/volume] in Blood", vs.getCompose().getInclude().get(0).getConcept().get(1).getDisplay()); + // Universal lab order VS + vs = valueSets.get(LoincUniversalOrderSetHandler.VS_ID); + assertEquals(1, vs.getCompose().getInclude().size()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, vs.getCompose().getInclude().get(0).getSystem()); + assertEquals(9, vs.getCompose().getInclude().get(0).getConcept().size()); + assertEquals("42176-8", vs.getCompose().getInclude().get(0).getConcept().get(0).getCode()); + assertEquals("1,3 beta glucan [Mass/volume] in Serum", vs.getCompose().getInclude().get(0).getConcept().get(0).getDisplay()); + + // IEEE Medical Device Codes + conceptMap = conceptMaps.get(LoincIeeeMedicalDeviceCodeHandler.LOINC_IEEE_CM_ID); + ourLog.info(FhirContext.forR4().newXmlParser().setPrettyPrint(true).encodeResourceToString(conceptMap)); + assertEquals(LoincIeeeMedicalDeviceCodeHandler.LOINC_IEEE_CM_NAME, conceptMap.getName()); + assertEquals(LoincIeeeMedicalDeviceCodeHandler.LOINC_IEEE_CM_URI, conceptMap.getUrl()); + assertEquals(1, conceptMap.getGroup().size()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, conceptMap.getGroup().get(0).getSource()); + assertEquals(IHapiTerminologyLoaderSvc.IEEE_11073_10101_URI, conceptMap.getGroup().get(0).getTarget()); + assertEquals(7, conceptMap.getGroup().get(0).getElement().size()); + assertEquals("14749-6", conceptMap.getGroup().get(0).getElement().get(4).getCode()); + assertEquals("Glucose [Moles/volume] in Serum or Plasma", conceptMap.getGroup().get(0).getElement().get(4).getDisplay()); + assertEquals(2, conceptMap.getGroup().get(0).getElement().get(4).getTarget().size()); + assertEquals("160196", conceptMap.getGroup().get(0).getElement().get(4).getTarget().get(0).getCode()); + assertEquals("MDC_CONC_GLU_VENOUS_PLASMA", conceptMap.getGroup().get(0).getElement().get(4).getTarget().get(0).getDisplay()); + + // Imaging Document Codes + vs = valueSets.get(LoincImagingDocumentCodeHandler.VS_ID); + assertEquals(LoincImagingDocumentCodeHandler.VS_URI, vs.getUrl()); + assertEquals(LoincImagingDocumentCodeHandler.VS_NAME, vs.getName()); + assertEquals(1, vs.getCompose().getInclude().size()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, vs.getCompose().getInclude().get(0).getSystem()); + assertEquals(9, vs.getCompose().getInclude().get(0).getConcept().size()); + assertEquals("11525-3", vs.getCompose().getInclude().get(0).getConcept().get(0).getCode()); + assertEquals("US Pelvis Fetus for pregnancy", vs.getCompose().getInclude().get(0).getConcept().get(0).getDisplay()); + } + + @Test + public void testLoadLoincMandatoryFilesOnly() throws IOException { + addLoincMandatoryFilesToZip(myFiles); + + // Actually do the load + mySvc.loadLoinc(myFiles.getFiles(), details); + + verify(myTermSvcDstu3, times(1)).storeNewCodeSystemVersion(mySystemCaptor.capture(), myCsvCaptor.capture(), any(RequestDetails.class), myValueSetsCaptor.capture(), myConceptMapCaptor.capture()); + Map<String, TermConcept> concepts = extractConcepts(); + Map<String, ValueSet> valueSets = extractValueSets(); + Map<String, ConceptMap> conceptMaps = extractConceptMaps(); + + // Normal loinc code + TermConcept code = concepts.get("10013-1"); + assertEquals("10013-1", code.getCode()); + assertEquals("Elpot", code.getStringProperty("PROPERTY")); + assertEquals("Pt", code.getStringProperty("TIME_ASPCT")); + assertEquals("R' wave amplitude in lead I", code.getDisplay()); + + // No valuesets or conceptmaps get created + assertThat(valueSets.keySet(), empty()); + assertThat(conceptMaps.keySet(), empty()); + + } + + @Test + public void testLoadLoincMissingMandatoryFiles() throws IOException { + addLoincOptionalFilesToZip(myFiles); + + // Actually do the load + try { + mySvc.loadLoinc(myFiles.getFiles(), details); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals("Could not find the following mandatory files in input: [loinc.csv, MULTI-AXIAL_HIERARCHY.CSV]", e.getMessage()); + } + } + + + static void addLoincMandatoryFilesToZip(ZipCollectionBuilder theFiles) throws IOException { + theFiles.addFileZip("/loinc/", "loinc.csv", TerminologyLoaderSvcImpl.LOINC_FILE); + theFiles.addFileZip("/loinc/", "hierarchy.csv", TerminologyLoaderSvcImpl.LOINC_HIERARCHY_FILE); + } + + static void addLoincOptionalFilesToZip(ZipCollectionBuilder theFiles) throws IOException { + theFiles.addFileZip("/loinc/", "AnswerList_Beta_1.csv", TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_FILE); + theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_LINK_FILE, TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_LINK_FILE); + theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_PART_FILE, TerminologyLoaderSvcImpl.LOINC_PART_FILE); + theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_PART_LINK_FILE, TerminologyLoaderSvcImpl.LOINC_PART_LINK_FILE); + theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_PART_RELATED_CODE_MAPPING_FILE); + theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_DOCUMENT_ONTOLOGY_FILE); + theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_RSNA_PLAYBOOK_FILE); + theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_UNIVERSAL_LAB_ORDER_VALUESET_FILE); + theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_IEEE_MEDICAL_DEVICE_CODE_MAPPING_TABLE_CSV); + theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_IMAGING_DOCUMENT_CODES_FILE); + + /* + * Top 2000 files have versions in the filename so don't use the + * constant.. that way this is a better test + */ + theFiles.addFilePlain("/loinc/", "LOINC_1.6_Top2000CommonLabResultsSI.csv"); + theFiles.addFilePlain("/loinc/", "LOINC_1.6_Top2000CommonLabResultsUS.csv"); } @AfterClass @@ -270,22 +374,4 @@ public class TerminologyLoaderSvcLoincTest { TestUtil.clearAllStaticFieldsForUnitTest(); } - static void createLoincBundle(ZipCollectionBuilder theFiles) throws IOException { - theFiles.addFile("/loinc/", "loinc.csv", TerminologyLoaderSvcImpl.LOINC_FILE); - theFiles.addFile("/loinc/", "hierarchy.csv", TerminologyLoaderSvcImpl.LOINC_HIERARCHY_FILE); - theFiles.addFile("/loinc/", "AnswerList_Beta_1.csv", TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_FILE); - theFiles.addFile("/loinc/", TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_LINK_FILE, TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_LINK_FILE); - theFiles.addFile("/loinc/", TerminologyLoaderSvcImpl.LOINC_PART_FILE, TerminologyLoaderSvcImpl.LOINC_PART_FILE); - theFiles.addFile("/loinc/", TerminologyLoaderSvcImpl.LOINC_PART_LINK_FILE, TerminologyLoaderSvcImpl.LOINC_PART_LINK_FILE); - theFiles.addFile("/loinc/", TerminologyLoaderSvcImpl.LOINC_PART_RELATED_CODE_MAPPING_FILE); - theFiles.addFile("/loinc/", TerminologyLoaderSvcImpl.LOINC_DOCUMENT_ONTOLOGY_FILE); - theFiles.addFile("/loinc/", TerminologyLoaderSvcImpl.LOINC_RSNA_PLAYBOOK_FILE); - /* - * Top 2000 files have versions in the filename so don't use the - * constant.. that way this is a better test - */ - theFiles.addFile("/loinc/", "LOINC_1.6_Top2000CommonLabResultsSI.csv"); - theFiles.addFile("/loinc/", "LOINC_1.6_Top2000CommonLabResultsUS.csv"); - } - } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcSnomedCtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcSnomedCtTest.java index 50453809d98..61c9db478f4 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcSnomedCtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcSnomedCtTest.java @@ -20,8 +20,10 @@ import org.mockito.Captor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; +import java.io.InputStream; import java.util.*; import java.util.zip.ZipOutputStream; @@ -54,19 +56,32 @@ public class TerminologyLoaderSvcSnomedCtTest { myFiles = new ZipCollectionBuilder(); } - private List<byte[]> list(byte[]... theByteArray) { - return new ArrayList<>(Arrays.asList(theByteArray)); + private ArrayList<IHapiTerminologyLoaderSvc.FileDescriptor> list(byte[]... theByteArray) { + ArrayList<IHapiTerminologyLoaderSvc.FileDescriptor> retVal = new ArrayList<>(); + for (byte[] next : theByteArray) { + retVal.add(new IHapiTerminologyLoaderSvc.FileDescriptor() { + @Override + public String getFilename() { + return "aaa.zip"; } + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream(next); + } + }); + } + return retVal; } @Test public void testLoadSnomedCt() throws Exception { - myFiles.addFile("/sct/", "sct2_Concept_Full_INT_20160131.txt"); - myFiles.addFile("/sct/", "sct2_Concept_Full-en_INT_20160131.txt"); - myFiles.addFile("/sct/", "sct2_Description_Full-en_INT_20160131.txt"); - myFiles.addFile("/sct/", "sct2_Identifier_Full_INT_20160131.txt"); - myFiles.addFile("/sct/", "sct2_Relationship_Full_INT_20160131.txt"); - myFiles.addFile("/sct/", "sct2_StatedRelationship_Full_INT_20160131.txt"); - myFiles.addFile("/sct/", "sct2_TextDefinition_Full-en_INT_20160131.txt"); + myFiles.addFileZip("/sct/", "sct2_Concept_Full_INT_20160131.txt"); + myFiles.addFileZip("/sct/", "sct2_Concept_Full-en_INT_20160131.txt"); + myFiles.addFileZip("/sct/", "sct2_Description_Full-en_INT_20160131.txt"); + myFiles.addFileZip("/sct/", "sct2_Identifier_Full_INT_20160131.txt"); + myFiles.addFileZip("/sct/", "sct2_Relationship_Full_INT_20160131.txt"); + myFiles.addFileZip("/sct/", "sct2_StatedRelationship_Full_INT_20160131.txt"); + myFiles.addFileZip("/sct/", "sct2_TextDefinition_Full-en_INT_20160131.txt"); RequestDetails details = mock(RequestDetails.class); mySvc.loadSnomedCt(myFiles.getFiles(), details); @@ -102,14 +117,14 @@ public class TerminologyLoaderSvcSnomedCtTest { public void testLoadSnomedCtBadInput() throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ZipOutputStream zos = new ZipOutputStream(bos); - myFiles.addFile("/sct/", "sct2_StatedRelationship_Full_INT_20160131.txt"); + myFiles.addFileZip("/sct/", "sct2_StatedRelationship_Full_INT_20160131.txt"); zos.close(); ourLog.info("ZIP file has {} bytes", bos.toByteArray().length); RequestDetails details = mock(RequestDetails.class); try { - mySvc.loadSnomedCt(Collections.singletonList(bos.toByteArray()), details); + mySvc.loadSnomedCt(list(bos.toByteArray()), details); fail(); } catch (InvalidRequestException e) { assertEquals("Invalid input zip file, expected zip to contain the following name fragments: [Terminology/sct2_Description_Full-en, Terminology/sct2_Relationship_Full, Terminology/sct2_Concept_Full_] but found: []", e.getMessage()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index 25170572101..ca5dc8639b0 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -49,19 +49,19 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { cs.setResource(table); TermConcept parent = new TermConcept(); - parent.setCodeSystem(cs); + parent.setCodeSystemVersion(cs); parent.setCode("parent"); cs.getConcepts().add(parent); TermConcept child = new TermConcept(); - child.setCodeSystem(cs); + child.setCodeSystemVersion(cs); child.setCode("child"); parent.addChild(child, RelationshipTypeEnum.ISA); child.addChild(parent, RelationshipTypeEnum.ISA); try { - myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", cs); +// myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", , cs); fail(); } catch (InvalidRequestException e) { assertEquals("CodeSystem contains circular reference around code parent", e.getMessage()); @@ -194,7 +194,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermConcept parentB = new TermConcept(cs, "ParentB"); cs.getConcepts().add(parentB); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL,"SYSTEM NAME" , cs); return id; } @@ -213,7 +213,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermConcept parentA = new TermConcept(cs, "CS2"); cs.getConcepts().add(parentA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL_2, cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL_2,"SYSTEM NAME" , cs); return id; } @@ -327,7 +327,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermCodeSystemVersion cs = new TermCodeSystemVersion(); cs.setResource(table); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL,"SYSTEM NAME" , cs); // Update cs = new TermCodeSystemVersion(); @@ -336,7 +336,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { id = myCodeSystemDao.update(codeSystem, null, true, true, mySrd).getId().toUnqualified(); table = myResourceTableDao.findOne(id.getIdPartAsLong()); cs.setResource(table); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL,"SYSTEM NAME" , cs); // Try to update to a different resource codeSystem = new CodeSystem(); @@ -346,7 +346,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { table = myResourceTableDao.findOne(id.getIdPartAsLong()); cs.setResource(table); try { - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL,"SYSTEM NAME" , cs); fail(); } catch (UnprocessableEntityException e) { assertThat(e.getMessage(), containsString("Can not create multiple code systems with URI \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/")); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ZipCollectionBuilder.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ZipCollectionBuilder.java index d77fb7c55ba..9df85caef70 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ZipCollectionBuilder.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/ZipCollectionBuilder.java @@ -5,45 +5,85 @@ import org.apache.commons.lang3.Validate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class ZipCollectionBuilder { private static final Logger ourLog = LoggerFactory.getLogger(ZipCollectionBuilder.class); - private final ArrayList<byte[]> myFiles; + private final ArrayList<IHapiTerminologyLoaderSvc.FileDescriptor> myFiles; - public ZipCollectionBuilder() { + /** + * Constructor + */ + ZipCollectionBuilder() { myFiles = new ArrayList<>(); } - public void addFile(String theClasspathPrefix, String theClasspathFileName) throws IOException { - addFile(theClasspathPrefix, theClasspathFileName, theClasspathFileName); + /** + * Add file as a raw file + */ + public void addFilePlain(String theClasspathPrefix, String theClasspathFileName) throws IOException { + byte[] file = readFile(theClasspathPrefix, theClasspathFileName); + myFiles.add(new IHapiTerminologyLoaderSvc.FileDescriptor() { + @Override + public String getFilename() { + return theClasspathFileName; + } + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream(file); + } + }); } - public void addFile(String theClasspathPrefix, String theClasspathFileName, String theOutputFilename) throws IOException { + /** + * Add file as an entry inside a ZIP file + */ + public void addFileZip(String theClasspathPrefix, String theClasspathFileName) throws IOException { + addFileZip(theClasspathPrefix, theClasspathFileName, theClasspathFileName); + } + + public void addFileZip(String theClasspathPrefix, String theClasspathFileName, String theOutputFilename) throws IOException { ByteArrayOutputStream bos; bos = new ByteArrayOutputStream(); ZipOutputStream zos = new ZipOutputStream(bos); ourLog.info("Adding {} to test zip", theClasspathFileName); zos.putNextEntry(new ZipEntry("SnomedCT_Release_INT_20160131_Full/Terminology/" + theOutputFilename)); + zos.write(readFile(theClasspathPrefix, theClasspathFileName)); + zos.closeEntry(); + zos.close(); + ourLog.info("ZIP file has {} bytes", bos.toByteArray().length); + myFiles.add(new IHapiTerminologyLoaderSvc.FileDescriptor() { + @Override + public String getFilename() { + return "AAA.zip"; + } + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream(bos.toByteArray()); + } + }); + } + + private byte[] readFile(String theClasspathPrefix, String theClasspathFileName) throws IOException { String classpathName = theClasspathPrefix + theClasspathFileName; InputStream stream = getClass().getResourceAsStream(classpathName); Validate.notNull(stream, "Couldn't load " + classpathName); byte[] byteArray = IOUtils.toByteArray(stream); Validate.notNull(byteArray); - zos.write(byteArray); - zos.closeEntry(); - zos.close(); - ourLog.info("ZIP file has {} bytes", bos.toByteArray().length); - myFiles.add(bos.toByteArray()); + return byteArray; } - public ArrayList<byte[]> getFiles() { + public List<IHapiTerminologyLoaderSvc.FileDescriptor> getFiles() { return myFiles; } diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/ImagingDocumentCodes.csv b/hapi-fhir-jpaserver-base/src/test/resources/loinc/ImagingDocumentCodes.csv new file mode 100644 index 00000000000..b4289dbac6f --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/ImagingDocumentCodes.csv @@ -0,0 +1,10 @@ +"LOINC_NUM","LONG_COMMON_NAME" +"11525-3","US Pelvis Fetus for pregnancy" +"17787-3","NM Thyroid gland Study report" +"18744-3","Bronchoscopy study" +"18746-8","Colonoscopy study" +"18748-4","Diagnostic imaging study" +"18751-8","Endoscopy study" +"18753-4","Flexible sigmoidoscopy study" +"24531-6","US Retroperitoneum" +"24532-4","US Abdomen RUQ" diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/LoincIeeeMedicalDeviceCodeMappingTable.csv b/hapi-fhir-jpaserver-base/src/test/resources/loinc/LoincIeeeMedicalDeviceCodeMappingTable.csv new file mode 100644 index 00000000000..f436e08eba9 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/LoincIeeeMedicalDeviceCodeMappingTable.csv @@ -0,0 +1,10 @@ +LOINC_NUM,LOINC_LONG_COMMON_NAME,IEEE_CF_CODE10,IEEE_REFID,IEEE_DESCRIPTION,IEEE_DIM,IEEE_UOM_UCUM +11556-8,Oxygen [Partial pressure] in Blood,160116,MDC_CONC_PO2_GEN,,LMT-2L-2 LMT-2L-2,kPa mm[Hg] +11557-6,Carbon dioxide [Partial pressure] in Blood,160064,MDC_CONC_PCO2_GEN,,LMT-2L-2 LMT-2L-2,kPa mm[Hg] +11558-4,pH of Blood,160004,MDC_CONC_PH_GEN,,[pH],[pH] +12961-9,Urea nitrogen [Mass/volume] in Arterial blood,160080,MDC_CONC_UREA_ART,,ML-3 NL-3,mg/dL mmol/L +14749-6,Glucose [Moles/volume] in Serum or Plasma,160196,MDC_CONC_GLU_VENOUS_PLASMA,Plasma glucose concentration taken from venous,NL-3 ,mmol/L +14749-6,Glucose [Moles/volume] in Serum or Plasma,160368,MDC_CONC_GLU_UNDETERMINED_PLASMA,Plasma glucose concentration taken from undetermined sample source,NL-3 ,mmol/L +15074-8,Glucose [Moles/volume] in Blood,160020,MDC_CONC_GLU_GEN,,NL-3,mmol/L +15074-8,Glucose [Moles/volume] in Blood,160364,MDC_CONC_GLU_UNDETERMINED_WHOLEBLOOD,Whole blood glucose concentration taken from undetermined sample source,NL-3 ,mmol/L +17861-6,Calcium [Mass/volume] in Serum or Plasma,160024,MDC_CONC_CA_GEN,,ML-3,mg/dL diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/LoincUniversalLabOrdersValueSet.csv b/hapi-fhir-jpaserver-base/src/test/resources/loinc/LoincUniversalLabOrdersValueSet.csv new file mode 100644 index 00000000000..07e924385c2 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/LoincUniversalLabOrdersValueSet.csv @@ -0,0 +1,10 @@ +"LOINC_NUM","LONG_COMMON_NAME","ORDER_OBS" +"42176-8","1,3 beta glucan [Mass/volume] in Serum","Both" +"53835-5","1,5-Anhydroglucitol [Mass/volume] in Serum or Plasma","Both" +"31019-3","10-Hydroxycarbazepine [Mass/volume] in Serum or Plasma","Both" +"6765-2","17-Hydroxypregnenolone [Mass/volume] in Serum or Plasma","Both" +"1668-3","17-Hydroxyprogesterone [Mass/volume] in Serum or Plasma","Both" +"32854-2","17-Hydroxyprogesterone [Presence] in DBS","Both" +"49054-0","25-Hydroxycalciferol [Mass/volume] in Serum or Plasma","Both" +"62292-8","25-Hydroxyvitamin D2+25-Hydroxyvitamin D3 [Mass/volume] in Serum or Plasma","Both" +"44907-4","5-Hydroxyindoleacetate panel - 24 hour Urine","Order" diff --git a/hapi-fhir-jpaserver-elasticsearch/pom.xml b/hapi-fhir-jpaserver-elasticsearch/pom.xml new file mode 100644 index 00000000000..8b966ec6188 --- /dev/null +++ b/hapi-fhir-jpaserver-elasticsearch/pom.xml @@ -0,0 +1,23 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>ca.uhn.hapi.fhir</groupId> + <artifactId>hapi-deployable-pom</artifactId> + <version>3.3.0-SNAPSHOT</version> + <relativePath>../hapi-deployable-pom/pom.xml</relativePath> + </parent> + + <artifactId>hapi-fhir-jpaserver-elasticsearch</artifactId> + <packaging>jar</packaging> + + <name>HAPI FHIR JPA Server - ElasticSearch Integration</name> + + <dependencies> + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-search-elasticsearch</artifactId> + </dependency> + </dependencies> + +</project> diff --git a/hapi-fhir-jpaserver-elasticsearch/src/main/java/ca/uhn/fhir/jpa/search/ElasticsearchMappingProvider.java b/hapi-fhir-jpaserver-elasticsearch/src/main/java/ca/uhn/fhir/jpa/search/ElasticsearchMappingProvider.java new file mode 100644 index 00000000000..12f25874116 --- /dev/null +++ b/hapi-fhir-jpaserver-elasticsearch/src/main/java/ca/uhn/fhir/jpa/search/ElasticsearchMappingProvider.java @@ -0,0 +1,41 @@ +package ca.uhn.fhir.jpa.search; + +import org.hibernate.search.elasticsearch.analyzer.definition.ElasticsearchAnalysisDefinitionRegistryBuilder; +import org.hibernate.search.elasticsearch.analyzer.definition.spi.ElasticsearchAnalysisDefinitionProvider; + +public class ElasticsearchMappingProvider implements ElasticsearchAnalysisDefinitionProvider { + + @Override + public void register(ElasticsearchAnalysisDefinitionRegistryBuilder builder) { + builder.analyzer("autocompleteEdgeAnalyzer") + .withTokenizer("pattern_all") + .withTokenFilters("lowercase", "stop", "edgengram_3_50"); + builder.tokenizer("pattern_all").type("pattern").param("pattern", "(.*)").param("group", "1"); + builder.tokenFilter("edgengram_3_50") + .type("edgeNGram") + .param("min_gram", "3") + .param("max_gram", "50"); + + builder.analyzer("autocompletePhoneticAnalyzer") + .withTokenizer("standard") + .withTokenFilters("standard", "stop", "snowball_english", "phonetic_doublemetaphone"); + builder.tokenFilter("phonetic_doublemetaphone") + .type("phonetic") + .param("encoder", "double_metaphone"); + builder.tokenFilter("snowball_english").type("snowball").param("language", "English"); + + builder.analyzer("autocompleteNGramAnalyzer") + .withTokenizer("standard") + .withTokenFilters("word_delimiter", "lowercase", "ngram_3_20"); + builder.tokenFilter("ngram_3_20") + .type("nGram") + .param("min_gram", "3") + .param("max_gram", "20"); + + builder.analyzer("standardAnalyzer").withTokenizer("standard").withTokenFilters("lowercase"); + + builder.analyzer("exactAnalyzer").withTokenizer("standard"); + + builder.analyzer("conceptParentPidsAnalyzer").withTokenizer("whitespace"); + } +} diff --git a/hapi-fhir-jpaserver-example/README.md b/hapi-fhir-jpaserver-example/README.md index a6d538db2ae..a3176abd3d1 100644 --- a/hapi-fhir-jpaserver-example/README.md +++ b/hapi-fhir-jpaserver-example/README.md @@ -41,3 +41,17 @@ Use this command to start the container: `docker run -d --name hapi-fhir-jpaserver-example -p 8080:8080 hapi-fhir/hapi-fhir-jpaserver-example` Note: with this command data is persisted across container restarts, but not after removal of the container. Use a docker volume mapping on /var/lib/jetty/target to achieve this. + +#### Using ElasticSearch as the search engine instead of the default Apache Lucene +1. Install ElasticSearch server and the phonetic plugin + * Download ElasticSearch from https://www.elastic.co/downloads/elasticsearch + * ```cd {your elasticsearch directory}``` + * ```bin/plugin install analysis-phonetic``` + * start ElasticSearch server: ```./bin/elasticsearch``` +2. Replace configuration in web.xml + * replace the configuration class ```ca.uhn.fhir.jpa.demo.FhirServerConfig``` in web.xml by ```ca.uhn.fhir.jpa.demo.elasticsearch.FhirServerConfig``` +3. Start server by runing: ```mvn jetty:run``` +4. Limitations: + * Hibernate search are not compatible with all ElasticSearch version. If you are using Hibernate search: 5.6 or 5.7, the compatible ElasticSearch version is 2.0 - 2.4. If you are using Hibernate search: 5.8 or 5.9, the compatible ElasticSearch version is + 2.0 - 5.6. + * Please check all the limitations in the reference documentation: https://docs.jboss.org/hibernate/search/5.7/reference/en-US/html_single/#elasticsearch-limitations before use the integration. diff --git a/hapi-fhir-jpaserver-example/pom.xml b/hapi-fhir-jpaserver-example/pom.xml index aa67698c5e6..eb08b41f12f 100644 --- a/hapi-fhir-jpaserver-example/pom.xml +++ b/hapi-fhir-jpaserver-example/pom.xml @@ -74,6 +74,12 @@ <version>${project.version}</version> </dependency> + <dependency> + <groupId>ca.uhn.hapi.fhir</groupId> + <artifactId>hapi-fhir-jpaserver-elasticsearch</artifactId> + <version>${project.version}</version> + </dependency> + <!-- This dependency is used for the "FHIR Tester" web app overlay --> <dependency> <groupId>ca.uhn.hapi.fhir</groupId> diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/elasticsearch/FhirServerConfig.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/elasticsearch/FhirServerConfig.java new file mode 100644 index 00000000000..cea6a123b7a --- /dev/null +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/elasticsearch/FhirServerConfig.java @@ -0,0 +1,111 @@ +package ca.uhn.fhir.jpa.demo.elasticsearch; + +import java.util.Properties; +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; + +import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; +import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.jpa.search.ElasticsearchMappingProvider; +import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; +import org.apache.commons.dbcp2.BasicDataSource; +import org.hibernate.jpa.HibernatePersistenceProvider; +import org.hibernate.search.elasticsearch.cfg.ElasticsearchEnvironment; +import org.springframework.beans.factory.annotation.Autowire; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +/** + * This is the configuration file for the example server integrating the ElasticSearch engine. + */ +@Configuration +@EnableTransactionManagement() +public class FhirServerConfig extends BaseJavaConfigDstu3 { + + /** + * Configure FHIR properties around the the JPA server via this bean + */ + @Bean() + public DaoConfig daoConfig() { + DaoConfig retVal = new DaoConfig(); + retVal.setAllowMultipleDelete(true); + return retVal; + } + + /** + * The following bean configures the database connection. The 'url' property value of "jdbc:derby:directory:jpaserver_derby_files;create=true" indicates that the server should save resources in a + * directory called "jpaserver_derby_files". + * + * A URL to a remote database could also be placed here, along with login credentials and other properties supported by BasicDataSource. + */ + @Bean(destroyMethod = "close") + public DataSource dataSource() { + BasicDataSource retVal = new BasicDataSource(); + retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver()); + retVal.setUrl("jdbc:derby:directory:target/jpaserver_derby_files;create=true"); + retVal.setUsername(""); + retVal.setPassword(""); + return retVal; + } + + @Bean() + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean(); + retVal.setPersistenceUnitName("HAPI_PU"); + retVal.setDataSource(dataSource()); + retVal.setPackagesToScan("ca.uhn.fhir.jpa.entity"); + retVal.setPersistenceProvider(new HibernatePersistenceProvider()); + retVal.setJpaProperties(jpaProperties()); + return retVal; + } + + private Properties jpaProperties() { + Properties extraProperties = new Properties(); + extraProperties.put("hibernate.dialect", org.hibernate.dialect.DerbyTenSevenDialect.class.getName()); + extraProperties.put("hibernate.format_sql", "true"); + extraProperties.put("hibernate.show_sql", "false"); + extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.jdbc.batch_size", "20"); + extraProperties.put("hibernate.cache.use_query_cache", "false"); + extraProperties.put("hibernate.cache.use_second_level_cache", "false"); + extraProperties.put("hibernate.cache.use_structured_entries", "false"); + extraProperties.put("hibernate.cache.use_minimal_puts", "false"); + + // the belowing properties are used for ElasticSearch integration + extraProperties.put(ElasticsearchEnvironment.ANALYZER_DEFINITION_PROVIDER, ElasticsearchMappingProvider.class.getName()); + extraProperties.put("hibernate.search.default.indexmanager", "elasticsearch"); + extraProperties.put("hibernate.search.default.elasticsearch.host", "http://127.0.0.1:9200"); + extraProperties.put("hibernate.search.default.elasticsearch.index_schema_management_strategy", "CREATE"); + extraProperties.put("hibernate.search.default.elasticsearch.index_management_wait_timeout", "10000"); + extraProperties.put("hibernate.search.default.elasticsearch.required_index_status", "yellow"); + return extraProperties; + } + + /** + * This interceptor adds some pretty syntax highlighting in responses when a browser is detected + */ + @Bean(autowire = Autowire.BY_TYPE) + public IServerInterceptor responseHighlighterInterceptor() { + ResponseHighlighterInterceptor retVal = new ResponseHighlighterInterceptor(); + return retVal; + } + + @Bean(autowire = Autowire.BY_TYPE) + public IServerInterceptor subscriptionSecurityInterceptor() { + SubscriptionsRequireManualActivationInterceptorDstu3 retVal = new SubscriptionsRequireManualActivationInterceptorDstu3(); + return retVal; + } + + @Bean() + public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager retVal = new JpaTransactionManager(); + retVal.setEntityManagerFactory(entityManagerFactory); + return retVal; + } + +} diff --git a/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt b/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt index 962e59816c6..a8fe4c2edd0 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt +++ b/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt @@ -106,4 +106,34 @@ where res_id in ( ) +drop table hfj_history_tag cascade constraints; +drop table hfj_forced_id cascade constraints; +drop table HFJ_SUBSCRIPTION_STATS cascade constraints; +drop table hfj_res_link cascade constraints; +drop table hfj_spidx_coords cascade constraints; +drop table hfj_spidx_date cascade constraints; +drop table hfj_spidx_number cascade constraints; +drop table hfj_spidx_quantity cascade constraints; +drop table hfj_spidx_string cascade constraints; +drop table hfj_spidx_token cascade constraints; +drop table hfj_spidx_uri cascade constraints; +drop table hfj_res_tag cascade constraints; +drop table hfj_search_result cascade constraints; +drop table hfj_search_include cascade constraints; +drop table hfj_search cascade constraints; +drop table hfj_res_param_present cascade constraints; +drop table hfj_idx_cmp_string_uniq cascade constraints; +drop table hfj_subscription_stats cascade constraints; +drop table trm_concept_property cascade constraints; +drop table trm_concept_pc_link cascade constraints; +drop table trm_concept cascade constraints; +drop table trm_codesystem_ver cascade constraints; +drop table trm_codesystem cascade constraints; +DROP TABLE hfj_resource CASCADE CONSTRAINTS; +DROP TABLE hfj_res_ver CASCADE CONSTRAINTS; +drop table cdr_audit_evt_target_module cascade constraints; +drop table cdr_audit_evt_target_res cascade constraints; +drop table cdr_audit_evt_target_user cascade constraints; +drop table cdr_xact_log_step cascade constraints; +drop table cdr_xact_log cascade constraints; diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java index e564b337c23..184ae74ac78 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java @@ -8,10 +8,9 @@ import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; -import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3; import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; -import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4; +import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.rest.api.EncodingEnum; @@ -29,13 +28,11 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; -import org.springframework.web.cors.CorsConfiguration; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -123,7 +120,7 @@ public class TestRestfulServer extends RestfulServer { JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class)); confProvider.setImplementationDescription(implDesc); setServerConformanceProvider(confProvider); - plainProviders.add(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class)); + plainProviders.add(myAppCtx.getBean(TerminologyUploaderProvider.class)); break; } case "R4": { @@ -141,7 +138,7 @@ public class TestRestfulServer extends RestfulServer { JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao, myAppCtx.getBean(DaoConfig.class)); confProvider.setImplementationDescription(implDesc); setServerConformanceProvider(confProvider); - plainProviders.add(myAppCtx.getBean(TerminologyUploaderProviderR4.class)); + plainProviders.add(myAppCtx.getBean(TerminologyUploaderProvider.class)); break; } default: diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 465649c1ae2..af543ba6bd1 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -91,7 +91,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> + <argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> </configuration> </plugin> </plugins> diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java index 957644059cf..266f187d83f 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/auth/RuleBuilder.java @@ -362,6 +362,8 @@ public class RuleBuilder implements IAuthRuleBuilder { for (IIdType next : theOwners) { validateOwner(next); } + myInCompartmentName = theCompartmentName; + myInCompartmentOwners = theOwners; myClassifierType = ClassifierTypeEnum.IN_COMPARTMENT; return finished(); } diff --git a/hapi-fhir-structures-dstu/pom.xml b/hapi-fhir-structures-dstu/pom.xml index cf739d6e948..cca4934514d 100644 --- a/hapi-fhir-structures-dstu/pom.xml +++ b/hapi-fhir-structures-dstu/pom.xml @@ -186,7 +186,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> + <argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> </configuration> </plugin> <plugin> diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index f15bbf9b9e0..c6448786624 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -255,7 +255,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> + <argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> </configuration> </plugin> <plugin> diff --git a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/DefaultProfileValidationSupport.java b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/DefaultProfileValidationSupport.java index ebbfbc2a0dd..a25c4a6fd78 100644 --- a/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/DefaultProfileValidationSupport.java +++ b/hapi-fhir-structures-dstu2.1/src/main/java/org/hl7/fhir/dstu2016may/hapi/validation/DefaultProfileValidationSupport.java @@ -207,7 +207,7 @@ public class DefaultProfileValidationSupport implements IValidationSupport { private Map<String, StructureDefinition> provideStructureDefinitionMap(FhirContext theContext) { Map<String, StructureDefinition> structureDefinitions = myStructureDefinitions; if (structureDefinitions == null) { - structureDefinitions = new HashMap<String, StructureDefinition>(); + structureDefinitions = new HashMap<>(); loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu2016may/model/profile/profiles-resources.xml"); loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/dstu2016may/model/profile/profiles-types.xml"); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java index e1d1d4772e2..1614a2115db 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java @@ -31,12 +31,13 @@ import org.mockito.Mockito; import org.mockito.internal.stubbing.answers.ThrowsException; import org.slf4j.LoggerFactory; -import java.io.IOException; import java.io.StringReader; import java.util.*; +import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.*; +import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.*; import static org.mockito.Mockito.*; @@ -44,6 +45,33 @@ public class JsonParserDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserDstu2Test.class); private static FhirContext ourCtx = FhirContext.forDstu2(); + private void assertExtensionMetadata( + BaseResource resource, + String url, + boolean isModifier, + Class<?> expectedType, + String expectedValue) { + ExtensionDt extension = (ExtensionDt) resource.getResourceMetadata().get(new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey(url)); + assertThat(extension.getValue(), instanceOf(expectedType)); + assertThat(extension.isModifier(), equalTo(isModifier)); + assertThat(extension.getValueAsPrimitive().getValueAsString(), equalTo(expectedValue)); + } + + private void assertParsedResourcesExtensionMetadata(ProcedureRequest resource) { + ExtensionDt payment = (ExtensionDt) resource.getResourceMetadata().get( + new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey("http://fhir.sjanic.com/procedureRequest/requiresPatientPayment")); + assertThat(payment.isModifier(), equalTo(true)); + assertThat(((BooleanDt) payment.getValue()).getValue(), equalTo(true)); + + TimestampFields timestampFields = new TimestampFields(resource); + assertThat(timestampFields.user.getReference().getIdPart(), equalTo("sjanic")); + assertThat(timestampFields.instance.getValue(), equalTo(new InstantDt("2012-01-01T13:00:00Z").getValue())); + assertThat(timestampFields.organization.getReference().getIdPart(), equalTo("sjanic_org")); + assertThat(timestampFields.role.getCodingFirstRep().getSystem(), equalTo("sjanic")); + assertThat(timestampFields.role.getCodingFirstRep().getCode(), equalTo("Doctor")); + } + + @Test public void testBaseUrlFooResourceCorrectlySerializedInExtensionValueReference() { String refVal = "http://my.org/FooBar"; @@ -68,7 +96,7 @@ public class JsonParserDstu2Test { * See #544 */ @Test - public void testBundleStitchReferencesByUuid() throws Exception { + public void testBundleStitchReferencesByUuid() { ca.uhn.fhir.model.dstu2.resource.Bundle bundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); DocumentManifest dm = new DocumentManifest(); @@ -145,6 +173,7 @@ public class JsonParserDstu2Test { assertEquals("myName", ((StringDt) newPatient.getUndeclaredExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue()); } + @Test public void testCustomUrlExtensioninBundle() { final String expected = "{\"resourceType\":\"Bundle\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://www.example.com/petname\",\"valueString\":\"myName\"}]}}]}"; @@ -217,7 +246,7 @@ public class JsonParserDstu2Test { } @Test - public void testEncodeAndParseExtensions() throws Exception { + public void testEncodeAndParseExtensions() { Patient patient = new Patient(); patient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setSystem("urn:example").setValue("7000135"); @@ -363,9 +392,9 @@ public class JsonParserDstu2Test { assertEquals(2, gotLabels.size()); - IdDt label = (IdDt) gotLabels.get(0); + IdDt label = gotLabels.get(0); assertEquals("http://foo/Profile1", label.getValue()); - label = (IdDt) gotLabels.get(1); + label = gotLabels.get(1); assertEquals("http://foo/Profile2", label.getValue()); tagList = ResourceMetadataKeyEnum.TAG_LIST.get(parsed); @@ -386,7 +415,7 @@ public class JsonParserDstu2Test { HumanNameDt name = p.addName(); name.addFamily().setValue(null).addUndeclaredExtension(new ExtensionDt(false, "http://foo", new StringDt("FOOEXT0"))); name.getFamily().get(0).setElementSpecificId("f0"); - name.addFamily().setValue("V1").addUndeclaredExtension((ExtensionDt) new ExtensionDt(false, "http://foo", new StringDt("FOOEXT1"))); + name.addFamily().setValue("V1").addUndeclaredExtension(new ExtensionDt(false, "http://foo", new StringDt("FOOEXT1"))); name.getFamily().get(1).setElementSpecificId("f1"); name.getFamily().get(1).getUndeclaredExtensions().get(0).setElementSpecificId("ext1id"); name.addFamily(); // this one shouldn't get encoded @@ -513,7 +542,6 @@ public class JsonParserDstu2Test { ca.uhn.fhir.model.dstu2.resource.Bundle b = new ca.uhn.fhir.model.dstu2.resource.Bundle(); b.getText().setDiv(""); b.getText().getStatus().setValueAsString(""); - ; Entry e = b.addEntry(); e.setResource(new Patient()); @@ -616,7 +644,6 @@ public class JsonParserDstu2Test { c = new Conformance(); c.getAcceptUnknownElement().setValueAsEnum(UnknownContentCodeEnum.UNKNOWN_ELEMENTS); - ; c.getAcceptUnknownElement().addUndeclaredExtension(false, "http://foo", new StringDt("AAA")); encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(c); @@ -770,7 +797,7 @@ public class JsonParserDstu2Test { } @Test - public void testEncodeNarrativeSuppressed() throws Exception { + public void testEncodeNarrativeSuppressed() { Patient patient = new Patient(); patient.setId("Patient/1/_history/1"); patient.getText().setDiv("<div>THE DIV</div>"); @@ -788,6 +815,7 @@ public class JsonParserDstu2Test { assertThat(encoded, containsString("maritalStatus")); } + /** * See #537 */ @@ -815,6 +843,69 @@ public class JsonParserDstu2Test { assertThat(encoded, stringContainsInOrder("LEVEL02", "LEVEL03", "LEVEL04", "LEVEL01")); } + @Test + public void testEncodeResourceWithExtensionMetadata() { + ProcedureRequest procedureRequest = new ProcedureRequest(); + procedureRequest.setStatus(ProcedureRequestStatusEnum.ACCEPTED); + ExtensionDt timestamp = new ExtensionDt(false, "http://fhir.sjanic.com/timestamp"); + timestamp.addUndeclaredExtension(false, "http://fhir.sjanic.com/timestamp/user", new ResourceReferenceDt("sjanic")); + timestamp.addUndeclaredExtension(false, "http://fhir.sjanic.com/timestamp/instance", new InstantDt("2012-01-01T13:00:00Z")); + timestamp.addUndeclaredExtension(false, "http://fhir.sjanic.com/timestamp/organization", new ResourceReferenceDt("sjanic_org")); + timestamp.addUndeclaredExtension(false, "http://fhir.sjanic.com/timestamp/role", new CodeableConceptDt().addCoding(new CodingDt("sjanic", "Doctor").setDisplay("Doctorin"))); + ExtensionDt payment = new ExtensionDt(true, "http://fhir.sjanic.com/procedureRequest/requiresPatientPayment", new BooleanDt(true)); + procedureRequest.getResourceMetadata().put(new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey(timestamp.getUrl()), timestamp); + procedureRequest.getResourceMetadata().put(new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey(payment.getUrl()), payment); + + String json = ourCtx.newJsonParser().encodeResourceToString(procedureRequest); + + // @formatter:off + assertThat(json, stringContainsInOrder( + "\"meta\":{" + + "\"extension\":[" + + "{" + + "\"url\":\"http://fhir.sjanic.com/timestamp\"," + + "\"extension\":[" + + "{" + + "\"url\":\"http://fhir.sjanic.com/timestamp/user\"," + + "\"valueReference\":{" + + "\"reference\":\"sjanic\"" + + "}" + + "}," + + "{" + + "\"url\":\"http://fhir.sjanic.com/timestamp/instance\"," + + "\"valueInstant\":\"2012-01-01T13:00:00Z\"" + + "}," + + "{" + + "\"url\":\"http://fhir.sjanic.com/timestamp/organization\"," + + "\"valueReference\":{" + + "\"reference\":\"sjanic_org\"" + + "}" + + "}," + + "{" + + "\"url\":\"http://fhir.sjanic.com/timestamp/role\"," + + "\"valueCodeableConcept\":{" + + "\"coding\":[" + + "{" + + "\"system\":\"sjanic\"," + + "\"code\":\"Doctor\"," + + "\"display\":\"Doctorin\"" + + "}" + + "]" + + "}" + + "}" + + "]" + + "}" + + "]," + + "\"modifierExtension\":[" + + "{" + + "\"url\":\"http://fhir.sjanic.com/procedureRequest/requiresPatientPayment\"," + + "\"valueBoolean\":true" + + "}" + + "]" + + "},")); + // @formatter:on + } + @Test public void testEncodeSummary() { Patient patient = new Patient(); @@ -881,7 +972,7 @@ public class JsonParserDstu2Test { // see #241 @Test - public void testEncodeThenParseShouldNotAddSpuriousId() throws Exception { + public void testEncodeThenParseShouldNotAddSpuriousId() { Condition condition = new Condition().setVerificationStatus(ConditionVerificationStatusEnum.CONFIRMED); ca.uhn.fhir.model.dstu2.resource.Bundle bundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); ca.uhn.fhir.model.dstu2.resource.Bundle.Entry entry = new ca.uhn.fhir.model.dstu2.resource.Bundle.Entry(); @@ -896,7 +987,7 @@ public class JsonParserDstu2Test { } @Test - public void testEncodeWithDontEncodeElements() throws Exception { + public void testEncodeWithDontEncodeElements() { Patient patient = new Patient(); patient.setId("123"); @@ -1037,6 +1128,7 @@ public class JsonParserDstu2Test { assertEquals("2011-01-01", condition.getDateRecordedElement().getValueAsString()); } + /** * #65 */ @@ -1054,7 +1146,7 @@ public class JsonParserDstu2Test { } @Test - public void testNamespacePreservationEncode() throws Exception { + public void testNamespacePreservationEncode() { //@formatter:off String input = "<Patient xmlns=\"http://hl7.org/fhir\" xmlns:xhtml=\"http://www.w3.org/1999/xhtml\">" + "<text>" + @@ -1076,7 +1168,7 @@ public class JsonParserDstu2Test { } @Test - public void testNamespacePreservationParse() throws Exception { + public void testNamespacePreservationParse() { String input = "{\"resourceType\":\"Patient\",\"text\":{\"div\":\"<xhtml:div xmlns:xhtml=\\\"http://www.w3.org/1999/xhtml\\\"><xhtml:img src=\\\"foo\\\"/>@fhirabend</xhtml:div>\"}}"; Patient parsed = ourCtx.newJsonParser().parseResource(Patient.class, input); @@ -1393,7 +1485,7 @@ public class JsonParserDstu2Test { } @Test - public void testParseAndEncodeComments() throws IOException { + public void testParseAndEncodeComments() { //@formatter:off String input = "{\n" + " \"resourceType\": \"Patient\",\n" + @@ -1626,7 +1718,7 @@ public class JsonParserDstu2Test { } @Test - public void testParseMetadata() throws Exception { + public void testParseMetadata() { //@formatter:off String bundle = "{\n" + " \"resourceType\" : \"Bundle\",\n" + @@ -1839,6 +1931,20 @@ public class JsonParserDstu2Test { assertEquals("Patient", reincarnatedPatient.getId().getResourceType()); } + @Test + public void testParseResourceWithExtensionMetadata() throws Exception { + String input = IOUtils.toString(getClass().getResourceAsStream("/procedure-request.json")); + IParser parser = ourCtx.newJsonParser(); + IParserErrorHandler peh = mock(IParserErrorHandler.class); + parser.setParserErrorHandler(peh); + + ProcedureRequest p = parser.parseResource(ProcedureRequest.class, input); + + ArgumentCaptor<String> capt = ArgumentCaptor.forClass(String.class); + verify(peh, Mockito.never()).unknownElement(Mockito.isNull(IParseLocation.class), capt.capture()); + assertParsedResourcesExtensionMetadata(p); + } + /** * See #207 */ @@ -1873,19 +1979,13 @@ public class JsonParserDstu2Test { Patient p = parser.parseResource(Patient.class, input); ArgumentCaptor<String> capt = ArgumentCaptor.forClass(String.class); - verify(peh, times(4)).unknownElement(Mockito.isNull(IParseLocation.class), capt.capture()); - - //@formatter:off - List<String> strings = capt.getAllValues(); - assertThat(strings, contains( - "extension", - "extension", - "modifierExtension", - "modifierExtension" - )); - //@formatter:off + verify(peh, times(0)).unknownElement(Mockito.isNull(IParseLocation.class), capt.capture()); assertEquals("Smith", p.getName().get(0).getGiven().get(0).getValue()); + assertExtensionMetadata(p, "fhir-request-method", false, StringDt.class, "POST"); + assertExtensionMetadata(p, "fhir-request-uri", false, UriDt.class, "Patient"); + assertExtensionMetadata(p, "modified-fhir-request-method", true, StringDt.class, "POST"); + assertExtensionMetadata(p, "modified-fhir-request-uri", true, UriDt.class, "Patient"); } @Test @@ -1989,4 +2089,25 @@ public class JsonParserDstu2Test { public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); } + + private static final class TimestampFields { + ResourceReferenceDt user; + InstantDt instance; + ResourceReferenceDt organization; + CodeableConceptDt role; + + TimestampFields(BaseResource resource) { + ExtensionDt timestamp = (ExtensionDt) resource.getResourceMetadata().get( + new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey("http://fhir.sjanic.com/timestamp")); + + Map<String, ExtensionDt> timestampFields = new HashMap<>(timestamp.getExtension().size()); + for (ExtensionDt extensionDt : timestamp.getExtension()) { + timestampFields.put(extensionDt.getUrl(), extensionDt); + } + user = ((ResourceReferenceDt) timestampFields.get("http://fhir.sjanic.com/timestamp/user").getValue()); + instance = (InstantDt) timestampFields.get("http://fhir.sjanic.com/timestamp/instance").getValue(); + organization = (ResourceReferenceDt) timestampFields.get("http://fhir.sjanic.com/timestamp/organization").getValue(); + role = (CodeableConceptDt) timestampFields.get("http://fhir.sjanic.com/timestamp/role").getValue(); + } + } } diff --git a/hapi-fhir-structures-dstu2/src/test/resources/patient1.json b/hapi-fhir-structures-dstu2/src/test/resources/patient1.json index 6cc128a2684..dbdd0c34abe 100644 --- a/hapi-fhir-structures-dstu2/src/test/resources/patient1.json +++ b/hapi-fhir-structures-dstu2/src/test/resources/patient1.json @@ -1,35 +1,35 @@ -{ - "id": "73b551fb-46f5-4fb8-b735-2399344e9717", - "meta": { - "extension": [ - { - "url": "fhir-request-method", - "valueString": "POST" - }, - { - "url": "fhir-request-uri", - "valueUri": "Patient" - } - ], - "modifierExtension": [ - { - "url": "fhir-request-method", - "valueString": "POST" - }, - { - "url": "fhir-request-uri", - "valueUri": "Patient" - } - ], - "versionId": "01e5253d-d258-494c-8d8e-f22ad6d8f19b", - "lastUpdated": "2016-02-20T11:01:56.155Z" - }, - "name": [ - { - "given": [ - "Smith" - ] - } - ], - "resourceType": "Patient" -} +{ + "id": "73b551fb-46f5-4fb8-b735-2399344e9717", + "meta": { + "extension": [ + { + "url": "fhir-request-method", + "valueString": "POST" + }, + { + "url": "fhir-request-uri", + "valueUri": "Patient" + } + ], + "modifierExtension": [ + { + "url": "modified-fhir-request-method", + "valueString": "POST" + }, + { + "url": "modified-fhir-request-uri", + "valueUri": "Patient" + } + ], + "versionId": "01e5253d-d258-494c-8d8e-f22ad6d8f19b", + "lastUpdated": "2016-02-20T11:01:56.155Z" + }, + "name": [ + { + "given": [ + "Smith" + ] + } + ], + "resourceType": "Patient" +} diff --git a/hapi-fhir-structures-dstu2/src/test/resources/procedure-request.json b/hapi-fhir-structures-dstu2/src/test/resources/procedure-request.json new file mode 100644 index 00000000000..3595d3c5908 --- /dev/null +++ b/hapi-fhir-structures-dstu2/src/test/resources/procedure-request.json @@ -0,0 +1,47 @@ +{ + "resourceType": "ProcedureRequest", + "meta": { + "extension": [ + { + "url": "http://fhir.sjanic.com/timestamp", + "extension": [ + { + "url": "http://fhir.sjanic.com/timestamp/user", + "valueReference": { + "reference": "sjanic" + } + }, + { + "url": "http://fhir.sjanic.com/timestamp/instance", + "valueInstant": "2012-01-01T13:00:00Z" + }, + { + "url": "http://fhir.sjanic.com/timestamp/organization", + "valueReference": { + "reference": "sjanic_org" + } + }, + { + "url": "http://fhir.sjanic.com/timestamp/role", + "valueCodeableConcept": { + "coding": [ + { + "system": "sjanic", + "code": "Doctor", + "display": "Doctorin" + } + ] + } + } + ] + } + ], + "modifierExtension": [ + { + "url": "http://fhir.sjanic.com/procedureRequest/requiresPatientPayment", + "valueBoolean": true + } + ] + }, + "status": "accepted" +} diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 8c7ab9c5de2..f3aa9371780 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -59,7 +59,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> + <argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> </configuration> </plugin> <plugin> diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/IValidationSupport.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/IValidationSupport.java index afcdaa401a6..222a1bfdd70 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/IValidationSupport.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/ctx/IValidationSupport.java @@ -89,7 +89,7 @@ public interface IValidationSupport @Override CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay); - public class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, IssueSeverity> { + class CodeValidationResult extends IContextValidationSupport.CodeValidationResult<ConceptDefinitionComponent, IssueSeverity> { public CodeValidationResult(ConceptDefinitionComponent theNext) { super(theNext); diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/DeviceStatus.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/DeviceStatus.java index d5ccb36916d..9ae2ccf98af 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/DeviceStatus.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/DeviceStatus.java @@ -1,105 +1,125 @@ -package org.hl7.fhir.dstu3.model.codesystems; - -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - -*/ - -// Generated on Sat, Mar 25, 2017 21:03-0400 for FHIR v3.0.0 - - -import org.hl7.fhir.exceptions.FHIRException; - -public enum DeviceStatus { - - /** - * The Device is available for use. Note: This means for *implanted devices* the device is implanted in the patient. - */ - ACTIVE, - /** - * The Device is no longer available for use (e.g. lost, expired, damaged). Note: This means for *implanted devices* the device has been removed from the patient. - */ - INACTIVE, - /** - * The Device was entered in error and voided. - */ - ENTEREDINERROR, - /** - * The status of the device has not been determined. - */ - UNKNOWN, - /** - * added to help the parsers - */ - NULL; - public static DeviceStatus fromCode(String codeString) throws FHIRException { - if (codeString == null || "".equals(codeString)) - return null; - if ("active".equals(codeString)) - return ACTIVE; - if ("inactive".equals(codeString)) - return INACTIVE; - if ("entered-in-error".equals(codeString)) - return ENTEREDINERROR; - if ("unknown".equals(codeString)) - return UNKNOWN; - throw new FHIRException("Unknown DeviceStatus code '"+codeString+"'"); - } - public String toCode() { - switch (this) { - case ACTIVE: return "active"; - case INACTIVE: return "inactive"; - case ENTEREDINERROR: return "entered-in-error"; - case UNKNOWN: return "unknown"; - default: return "?"; - } - } - public String getSystem() { - return "http://hl7.org/fhir/device-status"; - } - public String getDefinition() { - switch (this) { - case ACTIVE: return "The Device is available for use. Note: This means for *implanted devices* the device is implanted in the patient."; - case INACTIVE: return "The Device is no longer available for use (e.g. lost, expired, damaged). Note: This means for *implanted devices* the device has been removed from the patient."; - case ENTEREDINERROR: return "The Device was entered in error and voided."; - case UNKNOWN: return "The status of the device has not been determined."; - default: return "?"; - } - } - public String getDisplay() { - switch (this) { - case ACTIVE: return "Active"; - case INACTIVE: return "Inactive"; - case ENTEREDINERROR: return "Entered in Error"; - case UNKNOWN: return "Unknown"; - default: return "?"; - } - } - - -} - +package org.hl7.fhir.dstu3.model.codesystems; + +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*/ + +// Generated on Sat, Mar 25, 2017 21:03-0400 for FHIR v3.0.0 + + +import org.hl7.fhir.exceptions.FHIRException; + +public enum DeviceStatus { + + /** + * The Device is available for use. Note: This means for *implanted devices* the device is implanted in the patient. + */ + ACTIVE, + /** + * The Device is no longer available for use (e.g. lost, expired, damaged). Note: This means for *implanted devices* the device has been removed from the patient. + */ + INACTIVE, + /** + * The Device was entered in error and voided. + */ + ENTEREDINERROR, + /** + * The status of the device has not been determined. + */ + UNKNOWN, + /** + * added to help the parsers + */ + NULL; + + public String getDefinition() { + switch (this) { + case ACTIVE: + return "The Device is available for use. Note: This means for *implanted devices* the device is implanted in the patient."; + case INACTIVE: + return "The Device is no longer available for use (e.g. lost, expired, damaged). Note: This means for *implanted devices* the device has been removed from the patient."; + case ENTEREDINERROR: + return "The Device was entered in error and voided."; + case UNKNOWN: + return "The status of the device has not been determined."; + default: + return "?"; + } + } + + public String getDisplay() { + switch (this) { + case ACTIVE: + return "Active"; + case INACTIVE: + return "Inactive"; + case ENTEREDINERROR: + return "Entered in Error"; + case UNKNOWN: + return "Unknown"; + default: + return "?"; + } + } + + public String getSystem() { + return "http://hl7.org/fhir/device-status"; + } + + public String toCode() { + switch (this) { + case ACTIVE: + return "active"; + case INACTIVE: + return "inactive"; + case ENTEREDINERROR: + return "entered-in-error"; + case UNKNOWN: + return "unknown"; + default: + return "?"; + } + } + + public static DeviceStatus fromCode(String codeString) throws FHIRException { + if (codeString == null || "".equals(codeString)) + return null; + if ("active".equals(codeString)) + return ACTIVE; + if ("inactive".equals(codeString)) + return INACTIVE; + if ("entered-in-error".equals(codeString)) + return ENTEREDINERROR; + if ("unknown".equals(codeString)) + return UNKNOWN; + throw new FHIRException("Unknown DeviceStatus code '" + codeString + "'"); + } + + +} + diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/DeviceStatusEnumFactory.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/DeviceStatusEnumFactory.java index 64e4d361f1c..f30bda65932 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/DeviceStatusEnumFactory.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/model/codesystems/DeviceStatusEnumFactory.java @@ -1,70 +1,70 @@ -package org.hl7.fhir.dstu3.model.codesystems; - -/* - Copyright (c) 2011+, HL7, Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - * Neither the name of HL7 nor the names of its contributors may be used to - endorse or promote products derived from this software without specific - prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - -*/ - -// Generated on Sat, Mar 25, 2017 21:03-0400 for FHIR v3.0.0 - - -import org.hl7.fhir.dstu3.model.EnumFactory; - -public class DeviceStatusEnumFactory implements EnumFactory<DeviceStatus> { - - public DeviceStatus fromCode(String codeString) throws IllegalArgumentException { - if (codeString == null || "".equals(codeString)) - return null; - if ("active".equals(codeString)) - return DeviceStatus.ACTIVE; - if ("inactive".equals(codeString)) - return DeviceStatus.INACTIVE; - if ("entered-in-error".equals(codeString)) - return DeviceStatus.ENTEREDINERROR; - if ("unknown".equals(codeString)) - return DeviceStatus.UNKNOWN; - throw new IllegalArgumentException("Unknown DeviceStatus code '"+codeString+"'"); - } - - public String toCode(DeviceStatus code) { - if (code == DeviceStatus.ACTIVE) - return "active"; - if (code == DeviceStatus.INACTIVE) - return "inactive"; - if (code == DeviceStatus.ENTEREDINERROR) - return "entered-in-error"; - if (code == DeviceStatus.UNKNOWN) - return "unknown"; - return "?"; - } - - public String toSystem(DeviceStatus code) { - return code.getSystem(); - } - -} - +package org.hl7.fhir.dstu3.model.codesystems; + +/* + Copyright (c) 2011+, HL7, Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of HL7 nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +*/ + +// Generated on Sat, Mar 25, 2017 21:03-0400 for FHIR v3.0.0 + + +import org.hl7.fhir.dstu3.model.EnumFactory; + +public class DeviceStatusEnumFactory implements EnumFactory<DeviceStatus> { + + public DeviceStatus fromCode(String codeString) throws IllegalArgumentException { + if (codeString == null || "".equals(codeString)) + return null; + if ("active".equals(codeString)) + return DeviceStatus.ACTIVE; + if ("inactive".equals(codeString)) + return DeviceStatus.INACTIVE; + if ("entered-in-error".equals(codeString)) + return DeviceStatus.ENTEREDINERROR; + if ("unknown".equals(codeString)) + return DeviceStatus.UNKNOWN; + throw new IllegalArgumentException("Unknown DeviceStatus code '" + codeString + "'"); + } + + public String toCode(DeviceStatus code) { + if (code == DeviceStatus.ACTIVE) + return "active"; + if (code == DeviceStatus.INACTIVE) + return "inactive"; + if (code == DeviceStatus.ENTEREDINERROR) + return "entered-in-error"; + if (code == DeviceStatus.UNKNOWN) + return "unknown"; + return "?"; + } + + public String toSystem(DeviceStatus code) { + return code.getSystem(); + } + +} + diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/param/ReferenceParamTest.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/param/ReferenceParamTest.java index e6c108a1951..a61aa706ac2 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/param/ReferenceParamTest.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/param/ReferenceParamTest.java @@ -19,9 +19,61 @@ public class ReferenceParamTest { rp.setValueAsQueryToken(ourCtx, null, null, "Location/123"); assertEquals("Location", rp.getResourceType()); assertEquals("123", rp.getIdPart()); + assertEquals("Location/123", rp.getValue()); assertEquals(null, rp.getQueryParameterQualifier()); } + + @Test + public void testWithResourceType_AbsoluteUrl() { + + ReferenceParam rp = new ReferenceParam(); + rp.setValueAsQueryToken(ourCtx, null, null, "http://a.b/c/d/e"); + assertEquals("d", rp.getResourceType()); + assertEquals("e", rp.getIdPart()); + assertEquals("http://a.b/c/d/e", rp.getValue()); + assertEquals(null, rp.getQueryParameterQualifier()); + + } + + @Test + public void testWithNoResourceTypeAsQualifierAndChain() { + + ReferenceParam rp = new ReferenceParam(); + rp.setValueAsQueryToken(ourCtx, null, ".name", "FOO"); + assertEquals(null, rp.getResourceType()); + assertEquals("FOO", rp.getIdPart()); + assertEquals("FOO", rp.getValue()); + assertEquals(".name", rp.getQueryParameterQualifier()); + assertEquals("name", rp.getChain()); + + } + + @Test + public void testWithNoResourceTypeAsQualifierAndChain_RelativeUrl() { + + ReferenceParam rp = new ReferenceParam(); + rp.setValueAsQueryToken(ourCtx, null, ".name", "Patient/1233"); + assertEquals(null, rp.getResourceType()); + assertEquals("Patient/1233", rp.getIdPart()); + assertEquals("Patient/1233", rp.getValue()); + assertEquals(".name", rp.getQueryParameterQualifier()); + assertEquals("name", rp.getChain()); + + } + + @Test + public void testWithNoResourceTypeAsQualifierAndChain_AbsoluteUrl() { + + ReferenceParam rp = new ReferenceParam(); + rp.setValueAsQueryToken(ourCtx, null, ".name", "http://something.strange/a/b/c"); + assertEquals(null, rp.getResourceType()); + assertEquals("http://something.strange/a/b/c", rp.getIdPart()); + assertEquals("http://something.strange/a/b/c", rp.getValue()); + assertEquals(".name", rp.getQueryParameterQualifier()); + assertEquals("name", rp.getChain()); + + } @Test public void testWithResourceTypeAsQualifier() { @@ -30,6 +82,34 @@ public class ReferenceParamTest { rp.setValueAsQueryToken(ourCtx, null, ":Location", "123"); assertEquals("Location", rp.getResourceType()); assertEquals("123", rp.getIdPart()); + assertEquals("Location/123", rp.getValue()); + assertEquals(null, rp.getQueryParameterQualifier()); + + } + + // TODO: verify this behavior is correct. If type is explicitly specified (i.e. :Location), should it be + // an error if it gets overriden by the resourceType in the url? + @Test + public void testWithResourceTypeAsQualifier_RelativeUrl() { + + ReferenceParam rp = new ReferenceParam(); + rp.setValueAsQueryToken(ourCtx, null, ":Location", "Patient/123"); + assertEquals("Patient", rp.getResourceType()); + assertEquals("123", rp.getIdPart()); + assertEquals("Patient/123", rp.getValue()); + assertEquals(null, rp.getQueryParameterQualifier()); + + } + + // TODO: verify this behavior is correct. Same case as testWithResourceTypeAsQualifier_RelativeUrl() + @Test + public void testWithResourceTypeAsQualifier_AbsoluteUrl() { + + ReferenceParam rp = new ReferenceParam(); + rp.setValueAsQueryToken(ourCtx, null, ":Location", "http://a.b/c/d/e"); + assertEquals("d", rp.getResourceType()); + assertEquals("e", rp.getIdPart()); + assertEquals("http://a.b/c/d/e", rp.getValue()); assertEquals(null, rp.getQueryParameterQualifier()); } @@ -42,11 +122,51 @@ public class ReferenceParamTest { rp.setValueAsQueryToken(ourCtx, null, ":Location.name", "FOO"); assertEquals("Location", rp.getResourceType()); assertEquals("FOO", rp.getIdPart()); + assertEquals("Location/FOO", rp.getValue()); assertEquals(":Location.name", rp.getQueryParameterQualifier()); assertEquals("name", rp.getChain()); } + @Test + public void testWithResourceTypeAsQualifierAndChain_IdentifierUrlAndValue() { + + ReferenceParam rp = new ReferenceParam(); + rp.setValueAsQueryToken(ourCtx, null, ":Patient.identifier", "http://hey.there/a/b|123"); + assertEquals("Patient", rp.getResourceType()); + assertEquals("http://hey.there/a/b|123", rp.getIdPart()); + assertEquals("Patient/http://hey.there/a/b|123", rp.getValue()); + assertEquals(":Patient.identifier", rp.getQueryParameterQualifier()); + assertEquals("identifier", rp.getChain()); + + } + + @Test + public void testWithResourceTypeAsQualifierAndChain_IdentifierUrlOnly() { + + ReferenceParam rp = new ReferenceParam(); + rp.setValueAsQueryToken(ourCtx, null, ":Patient.identifier", "http://hey.there/a/b|"); + assertEquals("Patient", rp.getResourceType()); + assertEquals("http://hey.there/a/b|", rp.getIdPart()); + assertEquals("Patient/http://hey.there/a/b|", rp.getValue()); + assertEquals(":Patient.identifier", rp.getQueryParameterQualifier()); + assertEquals("identifier", rp.getChain()); + + } + + @Test + public void testWithResourceTypeAsQualifierAndChain_ValueOnlyNoUrl() { + + ReferenceParam rp = new ReferenceParam(); + rp.setValueAsQueryToken(ourCtx, null, ":Patient.identifier", "|abc"); + assertEquals("Patient", rp.getResourceType()); + assertEquals("|abc", rp.getIdPart()); + assertEquals("Patient/|abc", rp.getValue()); + assertEquals(":Patient.identifier", rp.getQueryParameterQualifier()); + assertEquals("identifier", rp.getChain()); + + } + @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index dddf38d6594..2391a4c7341 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -254,7 +254,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> + <argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> </configuration> </plugin> <plugin> diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index f69d32900b5..819b6e9446c 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -244,7 +244,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> + <argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> </configuration> </plugin> <plugin> diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/ValidationSupportChain.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/ValidationSupportChain.java index 0ab5873784d..9d63c85bdb2 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/ValidationSupportChain.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/ctx/ValidationSupportChain.java @@ -111,15 +111,15 @@ public class ValidationSupportChain implements IValidationSupport { @Override public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) { - ourLog.info("Validating code {} in chain with {} items", theCode, myChain.size()); + ourLog.debug("Validating code {} in chain with {} items", theCode, myChain.size()); for (IValidationSupport next : myChain) { if (next.isCodeSystemSupported(theCtx, theCodeSystem)) { CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay); - ourLog.info("Chain item {} returned outcome {}", next, result.isOk()); + ourLog.debug("Chain item {} returned outcome {}", next, result.isOk()); return result; } else { - ourLog.info("Chain item {} does not support code system {}", next, theCodeSystem); + ourLog.debug("Chain item {} does not support code system {}", next, theCodeSystem); } } return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay); diff --git a/hapi-fhir-utilities/pom.xml b/hapi-fhir-utilities/pom.xml index 2031fff370c..46ea5d99fed 100644 --- a/hapi-fhir-utilities/pom.xml +++ b/hapi-fhir-utilities/pom.xml @@ -104,7 +104,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> + <argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> </configuration> </plugin> <plugin> diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 671336e6554..e2e0acb930a 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -228,7 +228,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> - <argLine>${argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> + <argLine>@{argLine} -Dfile.encoding=UTF-8 -Xmx712m</argLine> </configuration> </plugin> <plugin> diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java index 3a3932c0f26..e30d2267e74 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java @@ -126,15 +126,15 @@ public class ValidationSupportChain implements IValidationSupport { @Override public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) { - ourLog.info("Validating code {} in chain with {} items", theCode, myChain.size()); + ourLog.debug("Validating code {} in chain with {} items", theCode, myChain.size()); for (IValidationSupport next : myChain) { if (next.isCodeSystemSupported(theCtx, theCodeSystem)) { CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay); - ourLog.info("Chain item {} returned outcome {}", next, result.isOk()); + ourLog.debug("Chain item {} returned outcome {}", next, result.isOk()); return result; } else { - ourLog.info("Chain item {} does not support code system {}", next, theCodeSystem); + ourLog.debug("Chain item {} does not support code system {}", next, theCodeSystem); } } return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay); diff --git a/pom.xml b/pom.xml index 07106c9fbe2..bc2bf27d268 100644 --- a/pom.xml +++ b/pom.xml @@ -390,7 +390,18 @@ <id>Tastelezz</id> <name>Gaetano Gallo</name> </developer> - + <developer> + <id>sjanic</id> + <name>sjanic</name> + </developer> + <developer> + <id>c-schuler</id> + <name>Chris Schuler</name> + </developer> + <developer> + <id>javajeff</id> + <name>Jeff Chung</name> + </developer> </developers> <licenses> @@ -950,6 +961,11 @@ <artifactId>hibernate-search-orm</artifactId> <version>${hibernate_search_version}</version> </dependency> + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-search-elasticsearch</artifactId> + <version>${hibernate_search_version}</version> + </dependency> <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> @@ -1942,6 +1958,7 @@ <module>hapi-fhir-jaxrsserver-base</module> <module>hapi-fhir-jaxrsserver-example</module> <module>hapi-fhir-jpaserver-base</module> + <module>hapi-fhir-jpaserver-elasticsearch</module> <module>hapi-fhir-jpaserver-example</module> <module>restful-server-example</module> <module>restful-server-example-test</module> @@ -1955,6 +1972,7 @@ <module>examples</module> <module>example-projects/hapi-fhir-base-example-embedded-ws</module> <module>example-projects/hapi-fhir-standalone-overlay-example</module> + <module>example-projects/hapi-fhir-jpaserver-cds-example</module> <module>hapi-fhir-jacoco</module> <module>hapi-fhir-igpacks</module> <module>hapi-fhir-spring-boot</module> diff --git a/src/changes/changes.xml b/src/changes/changes.xml index c545a6268eb..8869cddb77e 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -200,6 +200,46 @@ The client LoggingInterceptor now includes the number of milliseconds spent performing each call that is logged. </action> + <action type="add" issue="786"> + ReferenceParam has been enhanced to properly return the resource type to + user code in a server via the ReferenceType#getResourceType() method + if the client has specified a reference parameter with + a resource type. Thanks to @CarthageKing for the pull request! + </action> + <action type="add" issue="776"> + An entry has been added to ResourceMetadataKeyEnum which allows extensions + to be placed in the resource metadata section in DSTU2 resource (this is + possible already in DSTU3+ resources as Meta is a normal model type, but + the older structures worked a bit differently. Thanks to GitHub user + sjanic for the contribution! + </action> + <action type="add" issue="791"> + An example project has een contributed which shows how to use the CQL + framework in a server with HAPI FHIR JPA. Thanks to Chris Schuler + for the pull request! + </action> + <action type="add" issue="798"> + A new module has been contributed called hapi-fhir-jpaserver-elasticsearch + which adds support for Elasticsearch instead of raw Lucene for fulltext + indexing. Testing help on this would be appreciated! Thanks to + Jiajing Liang for the pull request! + </action> + <action type="fix" issue="800"> + JAX-RS server now supports R4 and DSTU2_1 FHIR versions, which were + previously missing. Thanks to Clayton Bodendein for the pull + request! + </action> + <action type="fix" issue="806"> + AuthorizationInterceptor did not correctly handle authorization against + against a compartment where the compartment owner was specified + as a list of IDs. Thanks to Jiajing Liang for the pull request! + </action> + <action type="add" issue="812"> + REST HOOK subscriptions in the JPA server now support having + an empty/missing Subscription.channel.payload value, which + is supported according to the FHIR specification. Thanks + to Jeff Chung for the pull request! + </action> </release> <release version="3.2.0" date="2018-01-13"> <action type="add"> diff --git a/src/site/xdoc/download.xml.vm b/src/site/xdoc/download.xml.vm index ea3ef92ef48..f58e9b4c953 100644 --- a/src/site/xdoc/download.xml.vm +++ b/src/site/xdoc/download.xml.vm @@ -273,7 +273,7 @@ </tr> <tr><td style="text-align: center; font-size: 1.2em; background: #DDE; padding: 3px;" colspan="2">Validation</td></tr> <tr> - <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-validator</td> + <td style="font-weight: bold; white-space: nowrap;">hapi-fhir-validation</td> <td> This module contains the FHIR Profile Validator, which is used to validate resource instances against FHIR Profiles (StructureDefinitions,