Multiple provider feature appointment delete issuea (#7900)
[openemr.git] / src / Cqm / Generator.php
blob198a17b89e060537000edfa24125babdf708a999
1 <?php
3 namespace OpenEMR\Cqm;
5 use Laminas\Code\Generator\ClassGenerator;
6 use Laminas\Code\Generator\DocBlock\Tag\PropertyTag;
7 use Laminas\Code\Generator\DocBlockGenerator;
8 use Laminas\Code\Generator\FileGenerator;
9 use Laminas\Code\Generator\PropertyGenerator;
11 class Generator
13 protected static $type_lookup = [
14 'System.DateTime' => 'BaseTypes\\DateTime',
15 'System.Date' => 'BaseTypes\\Date',
16 'System.Integer' => 'BaseTypes\\Integer',
17 'System.Quantity' => 'BaseTypes\\Quantity',
18 'System.Code' => 'BaseTypes\\Code',
19 'QDM.Identifier' => 'BaseTypes\\Identifier',
20 'System.Any' => 'BaseTypes\\Any',
21 'interval<System.DateTime>' => 'BaseTypes\\Interval',
22 'interval<System.Quantity>' => 'BaseTypes\\Interval',
23 'list<QDM.Component>' => 'array',
24 'System.String' => 'string',
25 'list<QDM.Id>' => 'array',
26 'list<QDM.ResultComponent>' => 'array',
27 'list<QDM.FacilityLocation>' => 'array',
28 'list<QDM.DiagnosisComponent>' => 'array',
29 'list<System.String>' => 'array',
30 'list<System.Code>' => 'array',
31 'System.Decimal' => 'BaseTypes\\Float',
32 'System.Time' => 'BaseTypes\\Time',
33 'System.Concept' => 'BaseTypes\\Any'
36 protected static $default_value_lookup = [
37 'list<QDM.Component>' => [],
38 'list<QDM.Id>' => [],
39 'list<QDM.ResultComponent>' => [],
40 'list<QDM.FacilityLocation>' => [],
41 'list<QDM.DiagnosisComponent>' => [],
42 'list<System.String>' => [],
43 'list<System.Code>' => [],
44 'System.String' => ''
47 public function execute()
49 $datatypes = [];
50 $extends = [];
51 $hqmfOid_to_datatype_map = [];
52 $oids_file = __DIR__ . '/oids_qdm_5.5.json';
53 $modelinfo_file = __DIR__ . '/qdm-modelinfo-5.5.xml';
54 $modelinfo = simplexml_load_string(file_get_contents($modelinfo_file));
55 $oids = json_decode(file_get_contents($oids_file), true);
57 // Grab QDM version as defined in the modelinfo file
58 $qdm_version = (string)$modelinfo->xpath('//ns4:modelInfo')[0]->attributes()->version;
60 // Loop through each typeInfo node (each of these is a QDM datatype)
61 foreach ($modelinfo->xpath('//ns4:typeInfo') as $type) {
62 // Grab the name of this QDM datatype
63 $name_parts = explode('.', (string)$type->attributes()->name);
64 $datatype_name = $name_parts[count($name_parts) - 1];
66 // Reject irrelevant datatypes
67 if (
68 strpos($datatype_name, 'Negative') ||
69 strpos($datatype_name, 'Positive') ||
70 strpos($datatype_name, 'QDMBaseType')
71 ) {
72 continue;
75 // Grab the QDM attributes for this datatype
76 $attributes = [];
77 foreach ($type->xpath('./ns4:element') as $attribute) {
78 // Grab the name of this QDM datatype attribute
79 $attribute_name = (string)$attribute->attributes()->name;
81 // Grab the type of this QDM datatype attribute
82 $attribute_type = (string)$attribute->attributes()->type ? (string)$attribute->attributes()->type : 'System.Any';
84 if (empty($attribute_name) || empty($attribute_type)) {
85 continue;
88 if (isset(self::$default_value_lookup[$attribute_type])) {
89 $default_value = self::$default_value_lookup[$attribute_type];
90 } else {
91 $default_value = null;
94 // Store name and type
95 $attributes[] = [
96 'name' => $attribute_name,
97 'type' => $attribute_type,
98 'default' => $default_value
102 // Add the label as qdmTitle
103 $qdm_title = (string)$type->attributes()->label;
104 if (empty($qdm_title)) {
105 // If there's no label, check if there is a "positive" profile
106 $positive_profile_element = $modelinfo->xpath("/ns4:modelInfo/ns4:typeInfo[@xsi:type='ns4:ProfileInfo'][@identifier='Positive$datatype_name']")[0];
107 if ($positive_profile_element !== null) {
108 $positive_profile = $positive_profile_element->attributes();
109 if (!empty((string)$positive_profile->label)) {
110 $qdm_title = (string)$positive_profile->label;
115 if (!empty($qdm_title)) {
116 $attributes[] = [
117 'name' => 'qdmTitle',
118 'type' => 'System.String',
119 'default' => $qdm_title
123 $datatype_base_type = (string)$type->attributes()->baseType;
124 if (!empty($datatype_base_type)) {
125 if (isset(self::$type_lookup[$datatype_base_type])) {
126 $datatype_base_name = self::$type_lookup[$datatype_base_type];
127 } else {
128 $base_name_parts = explode('.', (string)$type->attributes()->baseType);
129 $datatype_base_name = $base_name_parts[count($base_name_parts) - 1];
131 $extends[$datatype_name] = $datatype_base_name;
134 // Add the extra info that is manually maintained in the "oids" file
135 $extra_info = null;
136 if (isset($oids[$this->underscore($datatype_name)])) {
137 $extra_info = $oids[$this->underscore($datatype_name)];
139 if ($extra_info !== null) {
140 if (isset($extra_info['hqmf_oid'])) {
141 $attributes[] = [
142 'name' => 'hqmfOid',
143 'type' => 'System.String',
144 'default' => $extra_info['hqmf_oid']
147 if (isset($extra_info['qrda_oid'])) {
148 $attributes[] = [
149 'name' => 'qrdaOid',
150 'type' => 'System.String',
151 'default' => $extra_info['qrda_oid']
154 if (isset($extra_info['qdm_category'])) {
155 $attributes[] = [
156 'name' => 'qdmCategory',
157 'type' => 'System.String',
158 'default' => $extra_info['qdm_category']
161 if (isset($extra_info['qdm_status'])) {
162 $attributes[] = [
163 'name' => 'qdmStatus',
164 'type' => 'System.String',
165 'default' => $extra_info['qdm_status']
168 if (!empty($extra_info['qrda_oid'])) {
169 $hqmfOid_to_datatype_map[$extra_info['qrda_oid']] = $datatype_name;
173 // Add the qdmVersion attribute unless the base type is one that will provide it
174 if (!in_array((string)$type->attributes()->baseType, ['QDM.QDMBaseType', 'QDM.Entity', 'QDM.Component'])) {
175 $attributes[] = [
176 'name' => 'qdmVersion',
177 'type' => 'System.String',
178 'default' => $qdm_version,
182 $datatypes[$datatype_name] = $attributes;
185 // Generate PHP models
186 $base_module = "OpenEMR\\Cqm\\Qdm";
187 foreach ($datatypes as $datatype => $attributes) {
188 $class = new ClassGenerator();
189 $docblock = DocBlockGenerator::fromArray([
190 'shortDescription' => "{$base_module}\\{$datatype}",
191 'longDescription' => 'This is a class generated with Laminas\Code\Generator.',
192 'tags' => [
194 'name' => 'QDM Version',
195 'description' => $qdm_version,
198 'name' => 'author',
199 'description' => 'Ken Chapple <ken@mi-squared.com>',
202 'name' => 'license',
203 'description' => 'https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3',
208 $class->setName($datatype)
209 ->setNamespaceName($base_module)
210 ->setDocblock($docblock);
212 if (isset($extends[$datatype])) {
213 $class->setExtendedClass($base_module . '\\' . $extends[$datatype]);
216 // Add each property along with a docblock that specifies the type
217 foreach ($attributes as $attribute) {
218 $property = new PropertyGenerator();
219 $property->setDocBlock(DocBlockGenerator::fromArray([
220 'shortDescription' => '',
221 'longDescription' => '',
222 'tags' => [
223 new PropertyTag(
224 $attribute['name'],
225 [self::$type_lookup[$attribute['type']]]
228 ]));
229 $property->setName($attribute['name']);
230 if (isset($attribute['default'])) {
231 $property->setDefaultValue($attribute['default']);
233 $class->addPropertyFromGenerator($property);
236 // For cqm-execution, each model must have the _type field set in this format with QDM:: prefix
237 $class->addProperty('_type', "QDM::$datatype");
239 // Special cases
240 if ($datatype == 'Patient') {
241 $class->addTrait('Traits\\PatientExtension');
244 if ($datatype == 'QDMBaseType') {
245 $class->setExtendedClass('\\OpenEMR\\Cqm\\Qdm\\BaseTypes\\DataElement');
248 $file = FileGenerator::fromArray([
249 'classes' => [$class]
251 $code = $file->generate();
252 $qdm_dir = __DIR__ . DIRECTORY_SEPARATOR . 'Qdm';
253 if (!file_exists($qdm_dir)) {
254 mkdir($qdm_dir);
256 $filename = $qdm_dir . DIRECTORY_SEPARATOR . $datatype . '.php';
257 if (false === file_put_contents($filename, $code)) {
258 error_log("Error writing to QDM Model file: `$filename`");
262 file_put_contents(__DIR__ . '/hqmfOid_to_datatype_map.json', json_encode($hqmfOid_to_datatype_map));
265 public function underscore($input)
267 return strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $input));