Skip to content

Commit fd84a8b

Browse files
committed
[ADD] Pre-alpha version
1 parent 63040b0 commit fd84a8b

35 files changed

+2957
-407
lines changed

README.md

-2
This file was deleted.

SQLKPI/AbstractREST.cls

+21
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1+
Include (%occErrors, %ZEN.Utils)
2+
13
Class SQLKPI.AbstractREST Extends %CSP.REST
24
{
35

46
Parameter HandleCorsRequest = 1;
57

68
Parameter UseSession As Integer = 1;
79

10+
/// Specifies the default character set for the page. This can be overriden using the
11+
/// &lt;CSP:CONTENT CHARSET=&gt; tag, or by setting the <b>%response</b>.CharSet property
12+
/// in the <method>OnPreHTTP</method> method. If this parameter is not specified, then
13+
/// for the default charset is utf-8.
14+
Parameter CHARSET = "UTF-8";
15+
816
/// This method takes a status, renders it as jason (if requested) and outputs the result
917
ClassMethod outputStatus(pSC As %Status) As %Status [ Internal ]
1018
{
@@ -104,5 +112,18 @@ ClassMethod convertRequestBody() As %Status
104112
return $$$OK
105113
}
106114

115+
/// Issue an '500' error and give some indication as to what occurred
116+
ClassMethod Http500(pE As %Exception.AbstractException) As %Status
117+
{
118+
// we are expecting status
119+
#; Set the response Http status
120+
set %response.Status="500 Internal Server Error"
121+
122+
#; Return a helpful error string
123+
write "{""ERROR"":"_$$$ZENJSSTR($System.Status.GetErrorText(pE.AsStatus(),%session.Language))_"}"
124+
125+
quit $$$OK
126+
}
127+
107128
}
108129

SQLKPI/Generator.cls

+253-50
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,273 @@
11
/// Generates kpi class
2-
Class SQLKPI.Generator [ Abstract ]
2+
Class SQLKPI.Generator Extends %RegisteredObject
33
{
44

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} ];
1316

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 ]
1628
{
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+
}
1839

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()
2447
} 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.")
2749
}
2850

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")
30118
set kpi.XMLNamespace = "http://www.intersystems.com/deepsee/kpi"
31119

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
51165
}
52-
do kpi.Data.Write("</kpi>")
53166

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"
55258

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
59262
}
60263

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
64265
{
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}
68271
}
69272

70273
}

0 commit comments

Comments
 (0)