go to post Jill Goldberg · Dec 15, 2017 Thanks Blake! Please report your results to this thread - I think this information will be useful to lots of people.--Jill
go to post Jill Goldberg · Dec 13, 2017 Another approach, which would give you all patients 13 years old and older in the current calendar year is to write something like this:SELECT COUNT(*) FROM HSAA.Patient WHERE (DATEPART('yy',CURRENT_DATE) - DATEPART('yy',BirthDate)) >= 13This is just an example and there are probably different ways to write this to ensure you're executing the query in the most efficient way possible (if that is important for your needs).
go to post Jill Goldberg · Jun 19, 2017 As Eduard mentioned there are a couple of different approaches, depending on what your needs are. Here are some simple classes to use as examples: PetOwner has a name, state and a list of PetsPets has two properties: type and size Class Demo.PetOwner extends %Persistent { Property Name As %String; Property State As %String; Property Pets As list of Pet; } Class Demo.Pet extends %SerialObject { Property Type As %String; Property Size As %String; } Running a simple query produces results with embedded characters and no easy way to parse the list of Pets: SELECT Name, State, Pets FROM Demo.PetOwner One option is to use a stored function to return a single value when querying a specific PetOwner: /// SQL Function to get pet info /// Return each pet info as a comma delimited string where type and size /// are separated by ^ ClassMethod PetInfo(id As %String = "") As %String [SqlProc] { Try { Set sc = $$$OK Set petInfo = "" // Quit if no id Quit:(id="") // Open the PetOwner #dim owner As Demo.PetOwner Set owner = ##class(Demo.PetOwner).%OpenId(id) #dim pet As Demo.Pet Set petCount = owner.Pets.Count() For i=1:1:petCount { Set pet = owner.Pets.GetAt(i) Set petInfo = petInfo _ pet.Type _ "^" _ pet.Size _ "," } // Remove trailing , Set petInfo = $Extract(petInfo,1,*-1) } Catch(ex) { Set sc = ex.AsStatus() } Return petInfo } If PetInfo method is in the Demo.Test class, the stored function can be used like this to get a comma-delimited string of pet information where each component of the pet information is separated by a "^": SELECT Name, State, Demo.Test_PetInfo(ID) from Demo.PetOwner Another option is to write a custom class query that is called as a stored procedure to return the results as a resultset: CALL Demo.Test_GetOwnersWithPetDetails() The GetOwnersWithPetDetails method is in the class Demo.Test and information about how to write a custom class query can be found in the Caché documentation. Class Demo.Test extends %RegisteredObject { /// Create a custom query to handle getting all pet info /// Assume maximum of three pets per PetOwner Query GetOwnersWithPetDetails() As %Query(CONTAINID=1,ROWSPEC="ID:%Integer,Name:%String,State:%String,PetType1:%String,PetSize1:%String,PetType2:%String,PetSize2:%String,PetType3:%String,PetSize3:%String") [SqlProc] { } /// Query initialization code goes here ClassMethod GetOwnersWithPetDetailsExecute(ByRef qHandle As %Binary) As %Status { Try { Set sc = $$$OK // Use a cursor to a query of the PetOwner table to the fetch method // The cursor is a public variable! // Declare the cursor &sql(DECLARE DemoC1 CURSOR FOR SELECT ID FROM Demo.PetOwner ORDER BY ID) // Open the cursor &sql(OPEN DemoC1) Set qHandle="" } Catch (ex) { Set sc = ex.AsStatus() } Return sc } ClassMethod GetOwnersWithPetDetailsFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer=0) As %Status [PlaceAfter = GetOwnersWithPetDetailsExecute ] { Try { Set sc = $$$OK &sql(FETCH DemoC1 INTO :ownerID) // Assume maximum of three pets per PetOwner If (SQLCODE = 0) { // Initialize pet variables in case some are null Set PetType1 = "" Set PetSize1 = "" Set PetType2 = "" Set PetSize2 = "" Set PetType3 = "" Set PetSize3 = "" // Open the PetOwner with id from the cursor #dim owner As Demo.PetOwner Set owner = ##class(Demo.PetOwner).%OpenId(ownerID) Set Name = owner.Name Set State = owner.State Set numPets = owner.Pets.Count() #dim pet As Demo.Pet If (numPets>=1) { Set pet = owner.Pets.GetAt(1) Set PetType1 = pet.Type Set PetSize1 = pet.Size } If (numPets>=2) { Set pet = owner.Pets.GetAt(2) Set PetType2 = pet.Type Set PetSize2 = pet.Size } If (numPets=3) { Set pet = owner.Pets.GetAt(3) Set PetType3 = pet.Type Set PetSize3 = pet.Size } Set Row = $ListBuild(ownerID,Name,State,PetType1,PetSize1,PetType2,PetSize2,PetType3,PetSize3) Set AtEnd = 0 } ElseIf (SQLCODE = 100) { Set Row = "" Set AtEnd = 1 } Else { Set sc = $System.Status.Error(5001,"SQL fetch error: "_SQLCODE) Quit } } Catch (ex) { Set sc = ex.AsStatus() } Return sc } ClassMethod GetOwnersWithPetDetailsClose(ByRef qHandle As %Binary) As %Status [PlaceAfter = GetOwnersWithPetDetailsExecute ] { Try { Set sc = $$$OK &sql(CLOSE DemoC1) } Catch (ex) { Set sc = ex.AsStatus() } Return sc } }
go to post Jill Goldberg · Apr 20, 2017 Thanks Chris, this is very helpful! I had to make a few adjustments due to my configuration, but I'm posting here in case it's useful to others who are new to some of these areas. Setting this up in HealthShare (or other environments with normal security) Since I set this up in a HealthShare environment, which is installed with normal security by default, I needed to adjust the web applications. I created a new namespace and database for WIDGETDIRECT, along with a new security resource. The resource (%DB_WIDGETDIRECT) has no public permissions set. Note, however, the system automatically creates a role (with the same name) that has RW privileges on that resource. So to get the REST service to work, I changed the web application by going to the Application Roles tab and adding the %DB_WIDGETDIRECT role to the application roles. I know this isn't the correct way to handle security in a production environment, but I needed to do this to get this example to work. Getting REST to work with an Apache web server I am using a full Apache web server on my machine (Mac OS X) to service several instances of HealthShare. So, for example, to access the Management Portal using Apache my URL looks something like this: http://localhost/infoexchange/csp/sys/%25CSP.Portal.Home.zen There were two things required to get this entire exercise to work correctly: 1. In my httpd.config file I had something like this to force the CSP Gateway to handle all files: <Location "/infoexchange/"> CSPFileTypes * </Location> When I tried calling the REST service directly using this: /infoexchange/widgetsdirect/rest/jill I kept getting "URL not found errors". However, when I tried this it worked correctly: /infoexchange/widgetsdirect/rest/jill.txt It turns out that using CSPFileTypes is valid only for URLs with an extension (obvious now and to those who are expert in this area, but that's not me). So I added one line to my httpd.conf file to force the CSP Gateway to evaluate any URL with this location and all worked perfectly! <Location "/infoexchange/"> CSPFileTypes * CSP On </Location> 2. Since my URL for the REST service includes information about my instance, I needed to update the widgetmaster.js file to include that in the $http.get() command: $http.get('/infoexchange/widgetsdirect/rest/Jill')
go to post Jill Goldberg · Apr 19, 2017 This is very helpful, thank you! One problem is that a few of the images seem to be missing from the post (the standard "image not found" icon is shown).
go to post Jill Goldberg · Sep 6, 2016 Thanks Eduard and Dmitry. The class %Api.Atelier does not exist in 2016.1.1, which is consistent with Dmitry's response that I must use 2016.2.
go to post Jill Goldberg · Feb 26, 2016 Thank you Stefan and Ben!I agree - I'd rather get too many notifications than too few right now.
go to post Jill Goldberg · Feb 24, 2016 I see the same (bad) behavior with Chrome and Safari on OS X and Chrome on Windows. But my main concern is the fact that I can't access any of the other settings (I just see them flash by very quickly as the page is loading).