Custom Report Schema
Custom reports define tabular views of clinical data for a patient cohort. They are referenced from Custom Dashboards by identifier. Five report types are available, each with its own root element and a schemaVersion="3" attribute.
The <rows> element contains a patient query that defines the cohort. See the Patient Query Language reference for full documentation of population sources, code lists, variables, and rule expressions.
| Report type | Root element | Row type |
|---|---|---|
| Patient report | <patientReport> | One row per matching patient |
| Appointment report | <appointmentReport> | One row per matching appointment |
| Prescription report | <prescriptionReport> | One row per matching prescription |
| Prescription issue report | <prescriptionIssueReport> | One row per matching prescription issue |
| Activity report | <activityReport> | One row per matching clinical activity |
Example
<?xml version="1.0" encoding="UTF-8"?>
<patientReport schemaVersion="3">
<name>Patients with Diabetes</name>
<rows>
<patientQuery>
<name>Diabetes cohort</name>
<description>Patients with an active diabetes code.</description>
<population>
<patientQueryReference>ALL</patientQueryReference>
</population>
<codeLists>
<codeList>
<name>DIABETES</name>
<codeListDefinition>
<rules>
<inclusion><< 73211009 |Diabetes mellitus|</inclusion>
</rules>
</codeListDefinition>
</codeList>
</codeLists>
<variables>
<codedEntryVariable name="DIABETES_DAT" code="DIABETES"
select="clinically_relevant_date"
order="clinically_relevant_date"
take="latest"/>
</variables>
<rules>
<rule>
<if>DIABETES_DAT NOTEQ NULL</if>
<then>select</then>
<else>reject</else>
</rule>
</rules>
</patientQuery>
</rows>
<columns>
<demographicInformationColumn value="patientName"/>
<demographicInformationColumn value="patientNhsNumber"/>
<demographicInformationColumn value="patientDateOfBirth"/>
<demographicInformationColumn value="namedGP"/>
</columns>
</patientReport>
To reference a patient query already published to Medicus rather than embedding one inline, use patientQueryReference inside population:
<rows>
<patientQuery>
<name>Diabetes cohort</name>
<description>Patients matched by the published diabetes query.</description>
<population>
<patientQueryReference>my-org/diabetes-patients-2025</patientQueryReference>
</population>
<codeLists/>
<variables/>
<rules>
<rule>
<if>true</if>
<then>select</then>
<else>reject</else>
</rule>
</rules>
</patientQuery>
</rows>
Worked examples
These examples are checked automatically against the current schema (npm run check-schema-examples, wired into prebuild) - if a schema change breaks one, the build fails rather than the example silently going stale.
Patient report: T2DM patients without a statin
Patients aged 40-79 with a Type 2 Diabetes diagnosis and no record of a statin prescription:
<?xml version="1.0" encoding="UTF-8"?>
<patientReport schemaVersion="3">
<name>T2DM Without Statin</name>
<rows>
<patientQuery>
<name>T2DM without statin</name>
<description>Patients aged 40-79 with T2DM and no statin prescription</description>
<population>
<patientQueryReference>ALL</patientQueryReference>
</population>
<codeLists>
<codeList>
<name>T2DM_COD</name>
<codeListDefinition>
<rules>
<inclusion><< 44054006 |Diabetes mellitus type 2|</inclusion>
<exclusion><< 46635009 |Diabetes mellitus type 1|</exclusion>
</rules>
</codeListDefinition>
</codeList>
<codeList>
<name>STATIN_COD</name>
<fixedCodeList>
<codes>
<code>375864007</code>
<code>373541007</code>
<code>108600003</code>
</codes>
</fixedCodeList>
</codeList>
</codeLists>
<variables>
<codedEntryVariable name="T2DM_DAT" code="T2DM_COD" select="clinically_relevant_date" order="clinically_relevant_date" take="latest"/>
<codedEntryVariable name="STATIN_DAT" code="STATIN_COD" select="clinically_relevant_date" order="clinically_relevant_date" take="latest"/>
<patientAge name="AGE" atDate="today"/>
</variables>
<rules>
<rule>
<if>T2DM_DAT NOTEQ NULL AND AGE GTEQ 40 AND AGE LTEQ 79</if>
<then>next-rule</then>
<else>reject</else>
</rule>
<rule>
<if>STATIN_DAT EQ NULL</if>
<then>select</then>
<else>reject</else>
</rule>
</rules>
</patientQuery>
</rows>
<columns>
<demographicInformationColumn value="patientName" headerName="Patient"/>
<demographicInformationColumn value="patientNhsNumber" headerName="NHS Number"/>
<demographicInformationColumn value="patientAge" headerName="Age"/>
<demographicInformationColumn value="namedGP"/>
</columns>
</patientReport>
Activity report: flu vaccinations
Every flu vaccination activity across all patients:
<?xml version="1.0" encoding="UTF-8"?>
<activityReport schemaVersion="3">
<name>Flu Vaccinations</name>
<rows>
<patientQuery>
<name>Flu vaccination activity</name>
<description>Every flu vaccination activity across all patients</description>
<population>
<patientQueryReference>ALL</patientQueryReference>
</population>
<codeLists>
<codeList>
<name>CRITERION_1_COD</name>
<codeListDefinition>
<rules>
<inclusion><< 86198006 |Influenza vaccination|</inclusion>
</rules>
</codeListDefinition>
</codeList>
</codeLists>
<variables>
<codedEntryVariable name="CRITERION_1_DAT" code="CRITERION_1_COD" select="clinically_relevant_date" order="clinically_relevant_date" take="all"/>
</variables>
<rules>
<rule>
<if>TRUE EQ TRUE</if>
<then>select</then>
<else>reject</else>
</rule>
</rules>
</patientQuery>
</rows>
<columns>
<demographicInformationColumn value="patientName"/>
<codedEntryValueColumn value="activityDate"/>
<codedEntryValueColumn value="activity"/>
<codedEntryValueColumn headerName="Activity Description" value="codeDescription"/>
</columns>
</activityReport>
Activity report: diabetes activity (nested population)
Annual review and HbA1c activity, restricted to patients already confirmed to have a diabetes diagnosis via a nested inline patient query in population:
<?xml version="1.0" encoding="UTF-8"?>
<activityReport schemaVersion="3">
<name>Diabetes Activity</name>
<rows>
<patientQuery>
<name>Diabetes annual review activity</name>
<description>Annual review and HbA1c activity for patients with a diabetes diagnosis</description>
<population>
<patientQuery>
<name>Diabetes cohort</name>
<description>Patients with a diabetes diagnosis</description>
<population>
<patientQueryReference>ALL</patientQueryReference>
</population>
<codeLists>
<codeList>
<name>DM_COD</name>
<codeListDefinition>
<rules>
<inclusion><< 73211009 |Diabetes mellitus|</inclusion>
</rules>
</codeListDefinition>
</codeList>
</codeLists>
<variables>
<codedEntryVariable name="DM_DAT" code="DM_COD" select="clinically_relevant_date" order="clinically_relevant_date" take="latest"/>
</variables>
<rules>
<rule>
<if>DM_DAT NOTEQ NULL</if>
<then>select</then>
<else>reject</else>
</rule>
</rules>
</patientQuery>
</population>
<codeLists>
<codeList>
<name>CRITERION_1_COD</name>
<codeListDefinition>
<rules>
<inclusion><< 314764000 |Diabetes mellitus annual review|</inclusion>
</rules>
</codeListDefinition>
</codeList>
<codeList>
<name>CRITERION_2_COD</name>
<codeListDefinition>
<rules>
<inclusion><< 43396009 |Haemoglobin A1c measurement|</inclusion>
</rules>
</codeListDefinition>
</codeList>
</codeLists>
<variables>
<codedEntryVariable name="CRITERION_1_DAT" code="CRITERION_1_COD" select="clinically_relevant_date" order="clinically_relevant_date" take="all"/>
<codedEntryVariable name="CRITERION_2_DAT" code="CRITERION_2_COD" select="clinically_relevant_date" order="clinically_relevant_date" take="all"/>
</variables>
<rules>
<rule>
<if>TRUE EQ TRUE</if>
<then>select</then>
<else>reject</else>
</rule>
</rules>
</patientQuery>
</rows>
<columns>
<demographicInformationColumn value="patientName"/>
<demographicInformationColumn value="patientNhsNumber"/>
<demographicInformationColumn value="patientDateOfBirth"/>
<codedEntryValueColumn value="activityDate"/>
<codedEntryValueColumn value="activity"/>
<codedEntryValueColumn headerName="Activity Description" value="codeDescription"/>
</columns>
</activityReport>
Schema
Common schema (custom-report-common.xsd)
All five report types share this common schema. Each report-type XSD includes it and adds only its own root element.
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<!--
Compound practitioner identifier list: one or more type:value pairs separated by commas.
type: a lowercase-hyphenated identifier type (e.g. medicus-staff-id, gb-gmc-number)
value: the identifier value (no commas or whitespace)
Example: medicus-staff-id:abc-123,gb-gmc-number:7057629
-->
<xs:simpleType name="compoundPractitionerType">
<xs:restriction base="xs:string">
<xs:pattern value="[a-z][a-z0-9-]*:[^,\s]+(,[a-z][a-z0-9-]*:[^,\s]+)*"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="nonEmptyTrimmedString">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="reportNameType">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="reportIdentifierType">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="runtimeValueNameType">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="yyyymmddType">
<xs:restriction base="xs:string">
<xs:pattern value="\d{8}"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="codedEntryValueType">
<xs:restriction base="xs:string">
<xs:enumeration value="activityDate"/>
<xs:enumeration value="activity"/>
<xs:enumeration value="codeDescription"/>
<xs:enumeration value="activityTime"/>
<xs:enumeration value="activityEndDate"/>
<xs:enumeration value="activityStatus"/>
<xs:enumeration value="value"/>
<xs:enumeration value="bodySite"/>
<xs:enumeration value="route"/>
<xs:enumeration value="batchNumber"/>
<xs:enumeration value="expiryDate"/>
<xs:enumeration value="responsiblePractitioner"/>
<xs:enumeration value="responsiblePractitionerIdentifier"/>
<xs:enumeration value="responsibleOrganizationOdsCode"/>
<xs:enumeration value="activityPerformedHere"/>
<xs:enumeration value="responsibleOrganisation"/>
<xs:enumeration value="encounterType"/>
<xs:enumeration value="encounterTypeCode"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="demographicInformationValueType">
<xs:restriction base="xs:string">
<xs:enumeration value="patientName"/>
<xs:enumeration value="patientPrimaryIdentifier"/>
<xs:enumeration value="patientUuid"/>
<xs:enumeration value="patientNhsNumber"/>
<xs:enumeration value="patientDateOfBirth"/>
<xs:enumeration value="patientAge"/>
<xs:enumeration value="patientGender"/>
<xs:enumeration value="patientRegistrationType"/>
<xs:enumeration value="patientRegistrationDate"/>
<xs:enumeration value="patientCurrentRegistrationType"/>
<xs:enumeration value="namedGP"/>
<xs:enumeration value="homeAddress"/>
<xs:enumeration value="homePhoneNumber"/>
<xs:enumeration value="mobilePhoneNumber"/>
<xs:enumeration value="emailAddress"/>
<xs:enumeration value="currentRegistrationStartDate"/>
<xs:enumeration value="dateOfDeath"/>
<xs:enumeration value="prescriptionsDispensedByPractice"/>
<xs:enumeration value="careHomeResident"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="runtimeValueType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="name" type="runtimeValueNameType" use="required"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="patientQueryRuntimeValueType">
<xs:sequence>
<xs:element name="runtimeValue" type="runtimeValueType" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="name" type="runtimeValueNameType" use="required"/>
</xs:complexType>
<xs:complexType name="patientQueryRuntimeValuesType">
<xs:sequence>
<xs:element name="patientQueryRuntimeValue" type="patientQueryRuntimeValueType" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="demographicInformationColumnType">
<xs:attribute name="value" type="demographicInformationValueType" use="required"/>
<xs:attribute name="headerName" type="xs:string" use="optional"/>
<xs:attribute name="useAsFilter" type="xs:string" use="optional"/>
</xs:complexType>
<xs:complexType name="codedEntryValueColumnType">
<xs:attribute name="value" type="codedEntryValueType" use="required"/>
<xs:attribute name="headerName" type="xs:string" use="optional"/>
<xs:attribute name="useAsFilter" type="xs:string" use="optional"/>
</xs:complexType>
<xs:complexType name="anyElementType" mixed="true">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
<xs:anyAttribute processContents="skip"/>
</xs:complexType>
<xs:complexType name="dateColumnType" mixed="true">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
<xs:attribute name="headerName" type="xs:string" use="optional"/>
<xs:attribute name="useAsFilter" type="xs:string" use="optional"/>
</xs:complexType>
<xs:complexType name="derivedValueColumnType" mixed="true">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
<xs:attribute name="headerName" type="xs:string" use="optional"/>
<xs:attribute name="useAsFilter" type="xs:string" use="optional"/>
<xs:attribute name="basePatientQuery" type="xs:string" use="optional"/>
</xs:complexType>
<xs:complexType name="valueColumnType" mixed="true">
<xs:sequence>
<xs:any minOccurs="0" maxOccurs="unbounded" processContents="skip"/>
</xs:sequence>
<xs:attribute name="headerName" type="xs:string" use="optional"/>
<xs:attribute name="useAsFilter" type="xs:string" use="optional"/>
</xs:complexType>
<xs:simpleType name="prescriptionValueType">
<xs:restriction base="xs:string">
<xs:enumeration value="prescriptionType"/>
<xs:enumeration value="prescriptionStatus"/>
<xs:enumeration value="prescriptionProductCode"/>
<xs:enumeration value="prescriptionProductDescription"/>
<xs:enumeration value="prescriptionStartDate"/>
<xs:enumeration value="prescriptionExpectedEndDate"/>
<xs:enumeration value="prescriptionLastIssueDate"/>
<xs:enumeration value="prescriptionDiscontinuedDate"/>
<xs:enumeration value="prescriptionAuthorisedDate"/>
<xs:enumeration value="prescriptionAuthorisedByPractitioner"/>
<xs:enumeration value="prescriptionDosageInstruction"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="prescriptionIssueValueType">
<xs:restriction base="xs:string">
<xs:enumeration value="prescriptionIssueProductCode"/>
<xs:enumeration value="prescriptionIssueProductDescription"/>
<xs:enumeration value="prescriptionIssueQuantity"/>
<xs:enumeration value="prescriptionIssueDate"/>
<xs:enumeration value="prescriptionIssueMethod"/>
<xs:enumeration value="prescriptionIssueNumber"/>
<xs:enumeration value="prescriptionIssueStatus"/>
<xs:enumeration value="prescriptionIssueSupplyEndDate"/>
<xs:enumeration value="prescriptionIssueIssuedByPractitioner"/>
<xs:enumeration value="prescriptionIssueExpectedDaysSupply"/>
<xs:enumeration value="prescriptionIssueIssuingOrganisation"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="prescriptionValueColumnType">
<xs:attribute name="value" type="prescriptionValueType" use="required"/>
<xs:attribute name="headerName" type="xs:string" use="optional"/>
<xs:attribute name="useAsFilter" type="xs:string" use="optional"/>
</xs:complexType>
<xs:complexType name="prescriptionIssueValueColumnType">
<xs:attribute name="value" type="prescriptionIssueValueType" use="required"/>
<xs:attribute name="headerName" type="xs:string" use="optional"/>
<xs:attribute name="useAsFilter" type="xs:string" use="optional"/>
</xs:complexType>
<xs:simpleType name="appointmentValueType">
<xs:restriction base="xs:string">
<xs:enumeration value="appointmentDate"/>
<xs:enumeration value="appointmentEndDateTime"/>
<xs:enumeration value="appointmentStatus"/>
<xs:enumeration value="appointmentTypeName"/>
<xs:enumeration value="appointmentServiceName"/>
<xs:enumeration value="appointmentPractitionerName"/>
<xs:enumeration value="appointmentDeliveryMode"/>
<xs:enumeration value="appointmentDurationSeconds"/>
<xs:enumeration value="appointmentBookedDateTime"/>
<xs:enumeration value="appointmentArrivedDateTime"/>
<xs:enumeration value="appointmentCancelledDateTime"/>
<xs:enumeration value="appointmentRescheduledDateTime"/>
<xs:enumeration value="appointmentNhsSlotType"/>
<xs:enumeration value="appointmentSiteName"/>
<xs:enumeration value="appointmentRoomName"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="appointmentValueColumnType">
<xs:attribute name="value" type="appointmentValueType" use="required"/>
<xs:attribute name="headerName" type="xs:string" use="optional"/>
<xs:attribute name="useAsFilter" type="xs:string" use="optional"/>
</xs:complexType>
<xs:complexType name="columnsType">
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element name="demographicInformationColumn" type="demographicInformationColumnType"/>
<xs:element name="codedEntryValueColumn" type="codedEntryValueColumnType"/>
<xs:element name="dateColumn" type="dateColumnType"/>
<xs:element name="derivedValueColumn" type="derivedValueColumnType"/>
<xs:element name="valueColumn" type="valueColumnType"/>
<xs:element name="prescriptionValueColumn" type="prescriptionValueColumnType"/>
<xs:element name="prescriptionIssueValueColumn" type="prescriptionIssueValueColumnType"/>
<xs:element name="appointmentValueColumn" type="appointmentValueColumnType"/>
</xs:choice>
</xs:complexType>
<!-- rowsType: exactly one <patientQuery> -->
<xs:complexType name="rowsType">
<xs:sequence>
<xs:element name="patientQuery" type="inlinePatientQueryType"/>
</xs:sequence>
</xs:complexType>
<!-- inlinePatientQueryType: full query (no flat definedPatientIds form, use patientIdList inside population instead) -->
<xs:complexType name="inlinePatientQueryType">
<xs:sequence>
<!-- required: PatientQueryDefinition::validateXmlElementsRequirements() throws
"Name not found" / "Description not found" at runtime when either is absent. -->
<xs:element name="name" type="xs:string"/>
<xs:element name="description" type="xs:string"/>
<xs:element name="population" type="populationType"/>
<xs:element name="codeLists" type="codeListsType"/>
<xs:element name="variables" type="variablesType"/>
<xs:element name="rules" type="rulesType"/>
</xs:sequence>
</xs:complexType>
<!-- populationType: one of four population sources -->
<xs:complexType name="populationType">
<xs:choice>
<xs:element name="patientQueryReference" type="nonEmptyTrimmedString"/>
<xs:element name="patientQuery" type="embeddedPatientQueryType"/>
<xs:element name="patientReportReference" type="nonEmptyTrimmedString"/>
<xs:element name="patientIdList" type="patientIdListType"/>
</xs:choice>
</xs:complexType>
<!-- embeddedPatientQueryType: recursive inline patient query (no schemaVersion attribute) -->
<xs:complexType name="embeddedPatientQueryType">
<xs:sequence>
<!-- required: see inlinePatientQueryType above, same runtime check applies to embedded/population sub-queries. -->
<xs:element name="name" type="xs:string"/>
<xs:element name="description" type="xs:string"/>
<xs:element name="population" type="populationType"/>
<xs:element name="codeLists" type="codeListsType"/>
<xs:element name="variables" type="variablesType"/>
<xs:element name="rules" type="rulesType"/>
</xs:sequence>
</xs:complexType>
<!-- patientIdListType: one or more patient UUIDs -->
<xs:complexType name="patientIdListType">
<xs:sequence>
<xs:element name="patientId" type="nonEmptyTrimmedString" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- variablesType: zero or more variable declarations -->
<xs:complexType name="variablesType">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="fixedDate" type="fixedDateVariableType"/>
<xs:element name="codedEntryVariable" type="codedEntryDateVariableType"/>
<xs:element name="derivedFixedDate" type="derivedFixedDateType"/>
<xs:element name="patientAge" type="patientAgeVariableType"/>
<xs:element name="patientEntryDate" type="patientEntryDateVariableType"/>
<xs:element name="patientClinicalSex" type="namedVariableType"/>
<xs:element name="patientRegistrationVariable" type="patientRegistrationVariableType"/>
<xs:element name="patientNamedGp" type="namedVariableType"/>
<xs:element name="patientQuery" type="patientQueryVariableType"/>
<xs:element name="prescriptionVariable" type="prescriptionVariableType"/>
<xs:element name="prescriptionIssueVariable" type="prescriptionIssueVariableType"/>
<xs:element name="appointmentVariable" type="appointmentVariableType"/>
</xs:choice>
</xs:complexType>
<!-- namedVariableType: attribute-only variable element -->
<xs:complexType name="namedVariableType">
<xs:anyAttribute processContents="skip"/>
</xs:complexType>
<!--
patientAgeVariableType: unlike the other namedVariableType uses, PatientQueryPatientAge
throws a runtime \DomainException ("… is missing atDate attribute") when atDate is absent,
so, unlike patientClinicalSex/patientNamedGp, this one needs its attributes enumerated.
-->
<xs:complexType name="patientAgeVariableType">
<xs:attribute name="name" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="atDate" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="type" type="xs:string" use="optional"/>
</xs:complexType>
<!-- patientQueryVariableType: references another patient query as a variable -->
<xs:complexType name="patientQueryVariableType">
<xs:attribute name="name" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="queryIdentifier" type="nonEmptyTrimmedString" use="required"/>
</xs:complexType>
<xs:complexType name="fixedDateVariableType">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="name" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="runtimeValue" type="xs:string" use="optional"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="codedEntryDateVariableType">
<xs:attribute name="name" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="code" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="select" use="required"/>
<xs:attribute name="order" use="required"/>
<xs:attribute name="take" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="latest"/>
<xs:enumeration value="earliest"/>
<xs:enumeration value="all"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="value1" type="xs:string" use="optional"/>
<xs:attribute name="value2" type="xs:string" use="optional"/>
<xs:attribute name="performedAtLocalOrganisation" type="xs:string" use="optional"/>
<xs:attribute name="clinicallyRelevantDate" type="xs:string" use="optional"/>
<xs:attribute name="encounterTypeCode" type="xs:string" use="optional"/>
</xs:complexType>
<xs:complexType name="prescriptionVariableType">
<xs:attribute name="name" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="productCode" type="nonEmptyTrimmedString" use="optional"/>
<xs:attribute name="select" use="required"/>
<xs:attribute name="order" use="required"/>
<xs:attribute name="take" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="latest"/>
<xs:enumeration value="earliest"/>
<xs:enumeration value="all"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="type" type="xs:string" use="optional"/>
<xs:attribute name="endDate" type="xs:string" use="optional"/>
<xs:attribute name="status" type="xs:string" use="optional"/>
<xs:attribute name="lastIssueDate" type="xs:string" use="optional"/>
<xs:attribute name="startDate" type="xs:string" use="optional"/>
<xs:attribute name="discontinuedDate" type="xs:string" use="optional"/>
<xs:attribute name="authorisingPractitioner" type="compoundPractitionerType" use="optional"/>
</xs:complexType>
<xs:complexType name="prescriptionIssueVariableType">
<xs:attribute name="name" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="productCode" type="nonEmptyTrimmedString" use="optional"/>
<xs:attribute name="select" use="required"/>
<xs:attribute name="order" use="required"/>
<xs:attribute name="take" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="latest"/>
<xs:enumeration value="earliest"/>
<xs:enumeration value="all"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="issueDate" type="xs:string" use="optional"/>
<xs:attribute name="issueMethod" type="xs:string" use="optional"/>
<xs:attribute name="quantityValue" type="xs:string" use="optional"/>
<xs:attribute name="quantityUnitCode" type="xs:string" use="optional"/>
<xs:attribute name="supplyEndDate" type="xs:string" use="optional"/>
<xs:attribute name="issueStatus" type="xs:string" use="optional"/>
<xs:attribute name="issuedByPractitioner" type="compoundPractitionerType" use="optional"/>
</xs:complexType>
<xs:complexType name="appointmentVariableType">
<xs:attribute name="name" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="select" use="required"/>
<xs:attribute name="order" use="required"/>
<xs:attribute name="take" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="latest"/>
<xs:enumeration value="earliest"/>
<xs:enumeration value="all"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="status" type="xs:string" use="optional"/>
<xs:attribute name="appointmentType" type="xs:string" use="optional"/>
<xs:attribute name="appointmentService" type="xs:string" use="optional"/>
<xs:attribute name="deliveryMode" type="xs:string" use="optional"/>
<xs:attribute name="intendedStartDateTime" type="xs:string" use="optional"/>
<xs:attribute name="intendedEndDateTime" type="xs:string" use="optional"/>
<xs:attribute name="bookedDateTime" type="xs:string" use="optional"/>
<xs:attribute name="practitioner" type="compoundPractitionerType" use="optional"/>
<xs:attribute name="nhsNationalSlotTypeCategory" type="xs:string" use="optional"/>
<xs:attribute name="appointmentSite" type="xs:string" use="optional"/>
<xs:attribute name="appointmentRoom" type="xs:string" use="optional"/>
</xs:complexType>
<!--
patientRegistrationVariableType: the v3 source of truth for any registration data
referenced by query rules or demographic registration columns.
Replaces the v2 pairing of <patientRegistrationType> + <patientEntryDate patientField="registration_date|…">.
-->
<xs:complexType name="patientRegistrationVariableType">
<xs:attribute name="name" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="select" use="required">
<xs:simpleType>
<!-- Comma-separated list of registration columns. Each token must be one of
id, patient_id, start_date, end_date, registration_type,
prescriptions_dispensed_by_practice, care_home_resident. -->
<xs:restriction base="xs:string">
<xs:pattern value="(\s*(id|patient_id|start_date|end_date|registration_type|prescriptions_dispensed_by_practice|care_home_resident)\s*)(,\s*(id|patient_id|start_date|end_date|registration_type|prescriptions_dispensed_by_practice|care_home_resident)\s*)*"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="order" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="id"/>
<xs:enumeration value="patient_id"/>
<xs:enumeration value="start_date"/>
<xs:enumeration value="end_date"/>
<xs:enumeration value="registration_type"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="take" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="latest"/>
<xs:enumeration value="earliest"/>
<xs:enumeration value="all"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="startDate" type="xs:string" use="optional"/>
<xs:attribute name="endDate" type="xs:string" use="optional"/>
<xs:attribute name="registrationType" type="xs:string" use="optional"/>
<xs:attribute name="prescriptionsDispensedByPractice" use="optional">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="TRUE"/>
<xs:enumeration value="FALSE"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="careHomeResident" use="optional">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="TRUE"/>
<xs:enumeration value="FALSE"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
<!--
patientEntryDateVariableType: restricts patientField to the non-registration cases.
Registration-related fields (registration_date, deregistration_date, int_*, temp_*) were removed in v3;
use <patientRegistrationVariable> for those.
-->
<xs:complexType name="patientEntryDateVariableType">
<xs:attribute name="name" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="patientField" use="required">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="date_of_birth"/>
<xs:enumeration value="death_date"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="logic" type="xs:string" use="optional"/>
</xs:complexType>
<xs:complexType name="derivedFixedDateType">
<xs:attribute name="name" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="fixedDateName" type="nonEmptyTrimmedString" use="required"/>
<xs:attribute name="operation" type="nonEmptyTrimmedString" use="required"/>
</xs:complexType>
<!-- codeListsType: zero or more code list entries (each wrapped in a <codeList> element) -->
<xs:complexType name="codeListsType">
<xs:sequence>
<xs:element name="codeList" type="codeListEntryType" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<!-- codeListEntryType: wrapper containing <name> and one of three payload types -->
<xs:complexType name="codeListEntryType">
<xs:sequence>
<xs:element name="name" type="nonEmptyTrimmedString"/>
<xs:choice>
<xs:element name="codeListReference" type="nonEmptyTrimmedString"/>
<xs:element name="codeListDefinition" type="codeListDefinitionBodyType"/>
<xs:element name="fixedCodeList" type="fixedCodeListType"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="codeListDefinitionBodyType">
<xs:sequence>
<xs:element name="rules" type="codeListRulesType"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="fixedCodeListType">
<xs:sequence>
<xs:element name="codes">
<xs:complexType>
<xs:sequence>
<xs:element name="code" type="nonEmptyTrimmedString" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:complexType name="codeListRulesType">
<xs:choice minOccurs="1" maxOccurs="unbounded">
<xs:element name="inclusion" type="nonEmptyTrimmedString"/>
<xs:element name="exclusion" type="nonEmptyTrimmedString"/>
</xs:choice>
</xs:complexType>
<!-- rulesType / ruleType: one or more if/then/else rules -->
<xs:complexType name="rulesType">
<xs:sequence>
<xs:element name="rule" type="ruleType" minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="ruleType">
<xs:sequence>
<xs:element name="if" type="ruleExpressionType"/>
<xs:element name="then" type="thenValueType"/>
<xs:element name="else" type="elseValueType"/>
</xs:sequence>
</xs:complexType>
<!-- ruleExpressionType: must not end with whitespace -->
<xs:simpleType name="ruleExpressionType">
<xs:restriction base="xs:string">
<xs:minLength value="1"/>
<xs:pattern value=".*\S"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="thenValueType">
<xs:restriction base="xs:string">
<xs:enumeration value="next-rule"/>
<xs:enumeration value="select"/>
<xs:enumeration value="reject"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="elseValueType">
<xs:restriction base="xs:string">
<xs:enumeration value="next-rule"/>
<xs:enumeration value="select"/>
<xs:enumeration value="reject"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
Patient report (custom-patient-report.xsd)
<?xml version="1.0" encoding="UTF-8"?>
<!--
XSD for v3 patient reports validation
==== Example Schema =====
<patientReport schemaVersion="3">
<name>…</name>
<rows>
<patientQuery>
<population>
<patientQueryReference>ALL</patientQueryReference>
OR
<patientQueryReference>nhs-england/GMS_REG</patientQueryReference>
OR
<patientQuery> <- another full inline query
<population>…</population>
<codeLists>…</codeLists>
<variables>…</variables>
<rules>…</rules>
</patientQuery>
OR
<patientReportReference>report-uuid</patientReportReference>
OR
<patientIdList>
<patientId>uuid-1</patientId>
<patientId>uuid-2</patientId>
</patientIdList>
</population>
<codeLists>
<codeList>
<name>MY_COD</name>
<codeListReference>nhs-england/MY_COD</codeListReference>
</codeList>
<codeList>
<name>ECL_COD</name>
<codeListDefinition>
<rules>
<inclusion><< 73211009 |Diabetes mellitus|</inclusion>
</rules>
</codeListDefinition>
</codeList>
</codeLists>
<variables>…</variables>
<rules>…</rules>
</patientQuery>
</rows>
<columns>…</columns>
</patientReport>
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:include schemaLocation="custom-report-common.xsd"/>
<!--Verify root of report has required tags/attributes -->
<xs:element name="patientReport">
<xs:complexType>
<xs:sequence>
<!-- reportNameType caps at 255 chars to match the custom_report.name DB column. -->
<xs:element name="name" type="reportNameType"/>
<xs:element name="rows" type="rowsType"/>
<xs:element name="columns" type="columnsType"/>
</xs:sequence>
<xs:attribute name="schemaVersion" use="required">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:enumeration value="3"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
Appointment report (custom-appointment-report.xsd)
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:include schemaLocation="custom-report-common.xsd"/>
<xs:element name="appointmentReport">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="reportNameType"/>
<xs:element name="rows" type="rowsType"/>
<xs:element name="columns" type="columnsType"/>
</xs:sequence>
<xs:attribute name="schemaVersion" use="required">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:enumeration value="3"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
Prescription report (custom-prescription-report.xsd)
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:include schemaLocation="custom-report-common.xsd"/>
<xs:element name="prescriptionReport">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="reportNameType"/>
<xs:element name="rows" type="rowsType"/>
<xs:element name="columns" type="columnsType"/>
</xs:sequence>
<xs:attribute name="schemaVersion" use="required">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:enumeration value="3"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
Prescription issue report (custom-prescription-issue-report.xsd)
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:include schemaLocation="custom-report-common.xsd"/>
<xs:element name="prescriptionIssueReport">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="reportNameType"/>
<xs:element name="rows" type="rowsType"/>
<xs:element name="columns" type="columnsType"/>
</xs:sequence>
<xs:attribute name="schemaVersion" use="required">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:enumeration value="3"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
Activity report (custom-activity-report.xsd)
<?xml version="1.0" encoding="UTF-8"?>
<!--
XSD for v3 activity reports validation
Example:
<activityReport schemaVersion="3">
<name>My Activity Report</name>
<rows>
<patientQuery>
<name>My Activity Report</name>
<description>My Activity Report</description>
<population>
<patientQuery>
<name>My Activity Report-population</name>
<description>My Activity Report-population</description>
<population>
<patientQueryReference>ALL</patientQueryReference>
</population>
<codeLists/>
<variables>...</variables>
<rules>...</rules>
</patientQuery>
</population>
<codeLists>
<codeList>
<name>CRITERIA_1_COD</name>
<codeListDefinition>
<rules>
<inclusion><< 73211009 |Diabetes mellitus|</inclusion>
</rules>
</codeListDefinition>
</codeList>
</codeLists>
<variables>
<codedEntryVariable name="CRITERIA_1_DAT" code="CRITERIA_1_COD"
select="clinically_relevant_date"
order="clinically_relevant_date"
take="all"/>
</variables>
<rules>
<rule>
<if>TRUE EQ TRUE</if>
<then>select</then>
<else>reject</else></rule>
</rules>
</patientQuery>
</rows>
<columns>
<demographicInformationColumn value="patientName"/>
<demographicInformationColumn value="activityDate"/>
<demographicInformationColumn value="activity"/>
</columns>
</activityReport>
-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:include schemaLocation="custom-report-common.xsd"/>
<xs:element name="activityReport">
<xs:complexType>
<xs:sequence>
<!-- reportNameType caps at 255 chars to match the custom_report.name DB column. -->
<xs:element name="name" type="reportNameType"/>
<xs:element name="rows" type="rowsType"/>
<xs:element name="columns" type="columnsType"/>
</xs:sequence>
<xs:attribute name="schemaVersion" use="required">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:enumeration value="3"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>
Column reference
Computed value columns
Unlike the enumerated column types below, dateColumn, valueColumn, and derivedValueColumn don't take a fixed value attribute. Instead, their content is an inline patient-query-like structure (see Patient Query Language) that computes a value per patient at report run time - useful for showing a derived date, a computed value, or a value from a sub-query that isn't covered by the other column types.
| Element | Attributes | Description |
|---|---|---|
dateColumn | headerName, useAsFilter | Renders a computed date value |
valueColumn | headerName, useAsFilter | Renders a computed non-date value |
derivedValueColumn | headerName, useAsFilter, basePatientQuery | Renders a value derived from another variable's result |
basePatientQuery (only on derivedValueColumn) references one or more comma-separated published patient query identifiers to merge into the column's inline query, the same mechanism Custom Dashboards inline tabs use for patientQueryReference.
Demographic columns (demographicInformationColumn)
value | Description |
|---|---|
patientName | Patient full name |
patientPrimaryIdentifier | Primary identifier |
patientUuid | Medicus internal UUID |
patientNhsNumber | NHS number |
patientDateOfBirth | Date of birth |
patientAge | Current age |
patientGender | Gender |
patientRegistrationType | Registration type |
patientRegistrationDate | Registration date |
patientCurrentRegistrationType | Current registration type |
namedGP | Named GP |
homeAddress | Home address |
homePhoneNumber | Home phone |
mobilePhoneNumber | Mobile phone |
emailAddress | Email address |
currentRegistrationStartDate | Current registration start date |
dateOfDeath | Date of death |
prescriptionsDispensedByPractice | Whether dispensed by practice |
careHomeResident | Whether care home resident |
Coded entry columns (codedEntryValueColumn)
value | Description |
|---|---|
activityDate | Date of the coded entry |
activity | Activity description |
codeDescription | SNOMED CT description |
activityTime | Time of the coded entry |
activityEndDate | End date |
activityStatus | Status |
value | Numeric observation value |
bodySite | Body site |
route | Administration route |
batchNumber | Batch number |
expiryDate | Expiry date |
responsiblePractitioner | Responsible practitioner name |
responsiblePractitionerIdentifier | Responsible practitioner identifier |
responsibleOrganizationOdsCode | Responsible organisation ODS code |
activityPerformedHere | Whether performed at the local organisation |
responsibleOrganisation | Responsible organisation name |
encounterType | Encounter type description |
encounterTypeCode | Encounter type SNOMED CT concept ID |
Appointment columns (appointmentValueColumn)
value | Description |
|---|---|
appointmentDate | Intended start date/time |
appointmentEndDateTime | Intended end date/time |
appointmentStatus | Status |
appointmentTypeName | Appointment type name |
appointmentServiceName | Service name |
appointmentPractitionerName | Practitioner name |
appointmentDeliveryMode | Delivery mode |
appointmentDurationSeconds | Duration in seconds |
appointmentBookedDateTime | When booked |
appointmentArrivedDateTime | When patient arrived |
appointmentCancelledDateTime | When cancelled |
appointmentRescheduledDateTime | When rescheduled |
appointmentNhsSlotType | NHS GPAD slot type category |
appointmentSiteName | Site name |
appointmentRoomName | Room name |
Prescription columns (prescriptionValueColumn)
value | Description |
|---|---|
prescriptionType | Type (acute, repeat, etc.) |
prescriptionStatus | Status |
prescriptionProductCode | SNOMED / dm+d code |
prescriptionProductDescription | Product description |
prescriptionStartDate | Start date |
prescriptionExpectedEndDate | Expected end date |
prescriptionLastIssueDate | Last issue date |
prescriptionDiscontinuedDate | Discontinued date |
prescriptionAuthorisedDate | Authorised date |
prescriptionAuthorisedByPractitioner | Authorising practitioner |
prescriptionDosageInstruction | Dosage instructions |
Prescription issue columns (prescriptionIssueValueColumn)
value | Description |
|---|---|
prescriptionIssueProductCode | SNOMED / dm+d code |
prescriptionIssueProductDescription | Product description |
prescriptionIssueQuantity | Quantity |
prescriptionIssueDate | Issue date |
prescriptionIssueMethod | Issue method |
prescriptionIssueNumber | Issue number in sequence |
prescriptionIssueStatus | Issue status |
prescriptionIssueSupplyEndDate | Supply end date |
prescriptionIssueIssuedByPractitioner | Issuing practitioner |
prescriptionIssueExpectedDaysSupply | Expected days supply |
prescriptionIssueIssuingOrganisation | Issuing organisation |