|
1 | 1 | /// Generates kpi class
|
2 |
| -Class SQLKPI.Generator [ Abstract ] |
| 2 | +Class SQLKPI.Generator Extends %RegisteredObject |
3 | 3 | {
|
4 | 4 |
|
5 |
| -/// This XData definition defines the kpi. |
6 |
| -XData kpi |
7 |
| -{ |
8 |
| -<kpi |
9 |
| -xmlns="http://www.intersystems.com/deepsee/kpi" |
10 |
| - name="!!!1!!!" sourceType="sql" |
11 |
| - sql="!!!2!!!"> |
12 |
| -} |
| 5 | +Property kpiClass As %Dictionary.ClassDefinition; |
| 6 | + |
| 7 | +Property properties As %ListOfDataTypes; |
| 8 | + |
| 9 | +Property filters As %ListOfObjects; |
| 10 | + |
| 11 | +Property config As %ZEN.proxyObject; |
| 12 | + |
| 13 | +Property query As %String; |
| 14 | + |
| 15 | +Property generated As %Boolean [ InitialExpression = {$$$NO} ]; |
13 | 16 |
|
14 |
| -/// do ##class(SQLKPI.Generator).generateKPIClass("SELECT name, type FROM Sample.Per", "KPIName", "try.kpi") |
15 |
| -ClassMethod generateKPIClass(sql As %String, name As %String, class As %String) |
| 17 | +/// This callback method is invoked by the <METHOD>%New</METHOD> method to |
| 18 | +/// provide notification that a new instance of an object is being created. |
| 19 | +/// |
| 20 | +/// <P>If this method returns an error then the object will not be created. |
| 21 | +/// <p>It is passed the arguments provided in the %New call. |
| 22 | +/// When customizing this method, override the arguments with whatever variables and types you expect to receive from %New(). |
| 23 | +/// For example, if you're going to call %New, passing 2 arguments, %OnNew's signature could be: |
| 24 | +/// <p>Method %OnNew(dob as %Date = "", name as %Name = "") as %Status |
| 25 | +/// If instead of returning a %Status code this returns an oref and this oref is a subclass of the current |
| 26 | +/// class then this oref will be the one returned to the caller of %New method. |
| 27 | +Method %OnNew(query As %String = "", config As %ZEN.proxyObject, properties As %ListOfDataTypes, filters As %ListOfObjects, ByRef sc As %Status) As %Status [ Private, ServerOnly = 1 ] |
16 | 28 | {
|
17 |
| - #dim sc As %Status = $$$OK |
| 29 | + |
| 30 | + if (query = "") quit $$$ERROR(5001, "SQLKPI can not exist without a sql query.") |
| 31 | + |
| 32 | + if ('$data(config)) |
| 33 | + || (config.packageName = "") |
| 34 | + || (config.className = "") |
| 35 | + || (config.KPIName = "") |
| 36 | + { |
| 37 | + set sc = $$$ERROR(5001, "Error with KPI configuratin.") |
| 38 | + } |
18 | 39 |
|
19 |
| - if '##class(%Dictionary.ClassDefinition).%ExistsId(class) { |
20 |
| - set classObj = ##class(%Dictionary.ClassDefinition).%New(class) |
21 |
| - set classObj.GeneratedBy = "SQLKPI.Generator" |
22 |
| - set classObj.Super = "%DeepSee.KPI" |
23 |
| - set classObj.Description = "This kpi class was automatically generated by SQLkpi utility." |
| 40 | + if ('$data(properties)) set properties = ##class(%ListOfDataTypes).%New() |
| 41 | + if ('$data(filters)) set filters = ##class(%ListOfObjects).%New() |
| 42 | + |
| 43 | + set fullName = config.packageName_"."_config.className |
| 44 | + |
| 45 | + if '##class(%Dictionary.ClassDefinition).%ExistsId(fullName) { |
| 46 | + set ..kpiClass = ##class(%Dictionary.ClassDefinition).%New() |
24 | 47 | } else {
|
25 |
| - set classObj = ##class(%Dictionary.ClassDefinition).%OpenId(class) |
26 |
| - do classObj.XDatas.Clear() |
| 48 | + set sc = $$$ERROR(5001,"Class with this name already exists.") |
27 | 49 | }
|
28 | 50 |
|
29 |
| - set kpi = ##class(%Dictionary.XDataDefinition).%New(class _ ":kpi") |
| 51 | + set ..query = query |
| 52 | + set ..config = config |
| 53 | + set ..properties = properties |
| 54 | + set ..filters = filters |
| 55 | + |
| 56 | + quit $$$OK |
| 57 | +} |
| 58 | + |
| 59 | +Method generateKPIClass() As %Status |
| 60 | +{ |
| 61 | + set (sc,sc1,sc2,sc3, SC) = $$$OK |
| 62 | + set fullName = ..config.packageName_"."_..config.className |
| 63 | + |
| 64 | + do ..kpiClass.NameSet(fullName) |
| 65 | + do ..kpiClass.SuperSet("%DeepSee.KPI") |
| 66 | + do ..kpiClass.GeneratedBySet("SQLKPI.Generator") |
| 67 | + |
| 68 | + set descripton = "This kpi class was automatically generated by SQLKPI utility."_$char(10)_..config.description |
| 69 | + |
| 70 | + set descList = $listFromString(descripton, $char(10)) |
| 71 | + for i = 2:1:$listLength(descList) set $list(descList,i) = "/// "_$list(descList,i) |
| 72 | + set descripton = $listToString(descList, $char(10)) |
| 73 | + |
| 74 | + do ..kpiClass.DescriptionSet(descripton) |
| 75 | + |
| 76 | + set domain = ##class(%Dictionary.ParameterDefinition).%New(fullName_":DOMAIN") |
| 77 | + set domain.Default = ..config.domain |
| 78 | + //set sc2 = domain.%Save() |
| 79 | + do ..kpiClass.Parameters.Insert(domain) |
| 80 | + |
| 81 | + set resource = ##class(%Dictionary.ParameterDefinition).%New(fullName_":RESOURCE") |
| 82 | + set resource.Default = ..config.resource |
| 83 | + //set sc3 = resource.%Save() |
| 84 | + do ..kpiClass.Parameters.Insert(resource) |
| 85 | + |
| 86 | + |
| 87 | + set kpiXData = ..generateXData(sc) |
| 88 | + do ..kpiClass.XDatas.Insert(kpiXData) |
| 89 | + |
| 90 | + set onSQLMethod = ..generateFilterMethod(sc) |
| 91 | + do ..kpiClass.Methods.Insert(onSQLMethod) |
| 92 | + |
| 93 | + set sc1 = ..kpiClass.%Save() |
| 94 | + |
| 95 | + set SC = $$$ADDSC(sc, $$$ADDSC(sc1, $$$ADDSC(sc2, sc3))) |
| 96 | + |
| 97 | + if $$$ISOK(SC) set ..generated = $$$YES |
| 98 | + |
| 99 | + quit SC |
| 100 | +} |
| 101 | + |
| 102 | +Method compileGeneratedKPI() As %Status |
| 103 | +{ |
| 104 | + set sc = $$$OK |
| 105 | + set fullName = ..config.packageName_"."_..config.className |
| 106 | + |
| 107 | + if ('..generated) quit $$$ERROR(5001,"You are trying to compile a non-existent class.") |
| 108 | + set sc = $system.OBJ.Compile(fullName, "cuks /displaylog=0 /displayerror=0") |
| 109 | + quit sc |
| 110 | +} |
| 111 | + |
| 112 | +Method generateXData(ByRef sc As %Status) As %Dictionary.XDataDefinition |
| 113 | +{ |
| 114 | + set sc = $$$OK |
| 115 | + set fullName = ..config.packageName_"."_..config.className |
| 116 | + |
| 117 | + set kpi = ##class(%Dictionary.XDataDefinition).%New(fullName_":KPI") |
30 | 118 | set kpi.XMLNamespace = "http://www.intersystems.com/deepsee/kpi"
|
31 | 119 |
|
32 |
| - set header = ##class(%Dictionary.XDataDefinition).IDKEYOpen($classname(), "kpi").Data.Read(10000) |
33 |
| - do ..ReplaceRegexp(.header, "!!!1!!!", name) |
34 |
| - do ..ReplaceRegexp(.header, "!!!2!!!", sql) |
35 |
| - do kpi.Data.WriteLine(header) |
36 |
| - |
37 |
| - set st = ##class(%SQL.Statement).%New() |
38 |
| - set sc = st.%Prepare(sql) |
39 |
| - quit:$$$ISERR(sc) sc |
40 |
| - |
41 |
| - #dim result As %SQL.StatementResult |
42 |
| - set result = st.%Execute() |
43 |
| - |
44 |
| - #dim metadata As SQL.StatementMetadata |
45 |
| - set metadata = result.%GetMetadata() |
46 |
| - set columnCount = metadata.columns.Count() |
47 |
| - for i=1:1:columnCount { |
48 |
| - #dim column As %SQL.StatementColumn |
49 |
| - set column = metadata.columns.GetAt(i) |
50 |
| - do kpi.Data.WriteLine("<property name=""" _ column.colName _ """/>") |
| 120 | + set writer = ##class(%XML.Writer).%New() |
| 121 | + do writer.OutputToString() |
| 122 | + |
| 123 | + set elem = ##class(%XML.Element).%New("kpi") |
| 124 | + do elem.AddAttribute("xmlns","http://www.intersystems.com/deepsee/kpi") |
| 125 | + do elem.AddAttribute("name",..config.KPIName) |
| 126 | + do elem.AddAttribute("sourceType","sql") |
| 127 | + do elem.AddAttribute("caption",..config.KPICaption) |
| 128 | + do elem.AddAttribute("sql",..query) |
| 129 | + |
| 130 | + do writer.RootElement(elem) |
| 131 | + kill elem |
| 132 | + |
| 133 | + for i=1:1:..properties.Count() { |
| 134 | + set elem = ##class(%XML.Element).%New("property") |
| 135 | + do elem.AddAttribute("name",..properties.GetAt(i)) |
| 136 | + do elem.AddAttribute("columnNo",i+1) |
| 137 | + |
| 138 | + do writer.Element(elem) |
| 139 | + do writer.EndElement() |
| 140 | + kill elem |
| 141 | + } |
| 142 | + |
| 143 | + for i=1:1:..filters.Count() { |
| 144 | + set elem = ##class(%XML.Element).%New("filter") |
| 145 | + do elem.AddAttribute("name",..filters.GetAt(i).name) |
| 146 | + do elem.AddAttribute("filterProperty",..filters.GetAt(i).filterProperty) |
| 147 | + do elem.AddAttribute("multiSelect",$case(..filters.GetAt(i).multiSelect, 1:"true", :"false")) |
| 148 | + do elem.AddAttribute("dependsOn",..filters.GetAt(i).dependsOn) |
| 149 | + do elem.AddAttribute("displayName",..filters.GetAt(i).displayName) |
| 150 | + |
| 151 | + if ..filters.GetAt(i).searchType = "day" { |
| 152 | + do elem.AddAttribute("searchType",..filters.GetAt(i).searchType) |
| 153 | + } else { |
| 154 | + if (..filters.GetAt(i).runtime) { |
| 155 | + do elem.AddAttribute("sql",..filters.GetAt(i).sql) |
| 156 | + } else { |
| 157 | + do elem.AddAttribute("valueList",..filters.GetAt(i).valueList) |
| 158 | + do elem.AddAttribute("displayList",..filters.GetAt(i).displayList) |
| 159 | + } |
| 160 | + } |
| 161 | + |
| 162 | + do writer.Element(elem) |
| 163 | + do writer.EndElement() |
| 164 | + kill elem |
51 | 165 | }
|
52 |
| - do kpi.Data.Write("</kpi>") |
53 | 166 |
|
54 |
| - do classObj.XDatas.Insert(kpi) |
| 167 | + do writer.EndRootElement() |
| 168 | + set simplePrettify = $replace(writer.GetXMLString(), "><",">"_$char(10)_"<") |
| 169 | + do kpi.Data.Write(simplePrettify) |
| 170 | + |
| 171 | + quit kpi |
| 172 | +} |
| 173 | + |
| 174 | +Method generateFilterMethod(ByRef sc As %Status) As %Dictionary.MethodDefinition |
| 175 | +{ |
| 176 | + set sc = $$$OK |
| 177 | + set fullName = ..config.packageName_"."_..config.className |
| 178 | + |
| 179 | + set method = ##class(%Dictionary.MethodDefinition).%New(fullName_":%OnGetSQL") |
| 180 | + set method.ReturnType = "%Status" |
| 181 | + set method.FormalSpec = "&pSQL:%String" |
| 182 | + set method.Description = "Return an SQL statement to execute." |
| 183 | + |
| 184 | + set implementation = "" |
| 185 | + if (..filters.Count() > 0) { |
| 186 | + set tSql = $zconvert(..query, "l") |
| 187 | + set parsed = $$$YES |
| 188 | + set diffPart = "" |
| 189 | + set where = $$$NO |
| 190 | + |
| 191 | + if (..isSingleInString(tSql,"select ")) { |
| 192 | + if ($find(tSql,"where ")) { |
| 193 | + if (..isSingleInString(tSql,"where ")) { |
| 194 | + set diffPart = " set tSql = $zconvert(pSQL,""l"")"_$char(10) |
| 195 | + _ " set tPos = $find(tSql,""where "")-1" |
| 196 | + set where = $$$YES |
| 197 | + } else { |
| 198 | + set parsed = $$$NO |
| 199 | + } |
| 200 | + |
| 201 | + } elseIf ($find(tSql,"group by ")) { |
| 202 | + if (..isSingleInString(tSql,"group by ")) { |
| 203 | + set diffPart = " set tSql = $zconvert(pSQL,""l"")"_$char(10) |
| 204 | + _ " set tPos = $find(tSql,""group by "")-$length(""group by "")-1" |
| 205 | + } else { |
| 206 | + set parsed = $$$NO |
| 207 | + } |
| 208 | + |
| 209 | + } elseIf ($find(tSql,"order by ")) { |
| 210 | + if (..isSingleInString(tSql,"order by ")) { |
| 211 | + set diffPart = " set tSql = $zconvert(pSQL,""l"")"_$char(10) |
| 212 | + _ " set tPos = $find(tSql,""order by "")-$length(""order by "")-1" |
| 213 | + } else { |
| 214 | + set parsed = $$$NO |
| 215 | + } |
| 216 | + |
| 217 | + } else { |
| 218 | + set diffPart = " set pSQL = pSQL_"" """_$char(10) |
| 219 | + _ " set tPos = $length(pSQL)" |
| 220 | + } |
| 221 | + } else { |
| 222 | + set parsed = $$$NO |
| 223 | + } |
| 224 | + |
| 225 | + if (parsed) { |
| 226 | + set implementation = implementation_$char(10)_diffPart_$char(10) |
| 227 | + _ " if $isObject(..%filterValues) {"_$char(10) |
| 228 | + _ " set tWHERE = """"" |
| 229 | + |
| 230 | + for i=1:1:..filters.Count() { |
| 231 | + set implementation = implementation_$char(10) |
| 232 | + _ " if (..%filterValues."""_..filters.GetAt(i).name_""" '= """") {"_$char(10) |
| 233 | + _ " set tWHERE = tWHERE _ $select(tWHERE="""":"""",1:"" AND "") _ ..%GetSQLForFilter("""_..filters.GetAt(i).filterProperty_""","""_..filters.GetAt(i).name_""")"_$char(10) |
| 234 | + _ " }" |
| 235 | + } |
| 236 | + |
| 237 | + set implementation = implementation_$char(10) |
| 238 | + _ " if (tWHERE '= """") {" |
| 239 | + if (where) { |
| 240 | + set implementation = implementation_$char(10) |
| 241 | + _ " set tWHERE = "" ""_tWHERE_"" AND """ |
| 242 | + } else { |
| 243 | + set implementation = implementation_$char(10) |
| 244 | + _ " set tWHERE = "" WHERE ""_tWHERE_"" """ |
| 245 | + } |
| 246 | + |
| 247 | + set implementation = implementation_$char(10) |
| 248 | + _ " set $extract(pSQL,tPos) = tWHERE"_$char(10) |
| 249 | + _ " }"_$char(10) |
| 250 | + _ " }" |
| 251 | + |
| 252 | + } else { |
| 253 | + set implementation = " quit $$$ERROR(5001, ""We can not parse your query to apply filters. " |
| 254 | + _"Please implement filtering yourself."")" |
| 255 | + } |
| 256 | + } |
| 257 | + set implementation = implementation_$char(10)_" quit $$$OK" |
55 | 258 |
|
56 |
| - set sc = classObj.%Save() |
57 |
| - return:$$$ISERR(sc) sc |
58 |
| - return $System.OBJ.Compile(class, "cuks /displaylog=0 /displayerror=0") |
| 259 | + do method.Implementation.Write(implementation) |
| 260 | + |
| 261 | + quit method |
59 | 262 | }
|
60 | 263 |
|
61 |
| -/// Replaces all occurances of Pattern with ReplacePattern. |
62 |
| -/// w $System.Status.GetErrorText(##class(SQLKPI.Generator).ReplaceRegexp(.Text)) |
63 |
| -ClassMethod ReplaceRegexp(ByRef Text, Pattern As %String = "", ReplacePattern As %String = "") As %Status |
| 264 | +ClassMethod isSingleInString(string As %String, substring As %String) As %Boolean |
64 | 265 | {
|
65 |
| - #Dim Matcher As %Regex.Matcher = ##class(%Regex.Matcher).%New(Pattern, Text) |
66 |
| - Set Text = Matcher.ReplaceAll(ReplacePattern) |
67 |
| - Quit Matcher.Status |
| 266 | + set tPos1 = $find(string, substring) |
| 267 | + set tPos2 = $length(string) + $length(substring) + 2 - $find($reverse(string),$reverse(substring)) |
| 268 | + |
| 269 | + if tPos1 = tPos2 {quit $$$YES} |
| 270 | + else {quit $$$NO} |
68 | 271 | }
|
69 | 272 |
|
70 | 273 | }
|
|
0 commit comments