go to post Eduard Lebedyuk · Jan 2, 2017 You can define your own device of terminal type (also see System - Configuration - Devices you can see other defined Cache terminals) :
go to post Eduard Lebedyuk · Dec 23, 2016 Here's an idea on how to do it without triggers altogether. 1. Set IsLeader property only in case member is a leader. So its 1 or NULL. 2. Add unique index on (Team, IsLeader). Unique index can have any number of NULL records. 3. If you try to add more than one leader, you'll get an error: ERROR #5808: Key not unique: Utils.TeamMember:IsLeaderIndex:^Utils.TeamMemberI("IsLeaderIndex"," 1"," 1") [%SaveData+14^Utils.TeamMember.1:USER] Sample code: Class Utils.TeamMember Extends %Persistent { Property Team As %String; Property Member As %String; Property IsLeader(VALUELIST = ",1"); Index IsLeaderIndex On (Team, IsLeader) [ Unique ]; /// do ##class(Utils.TeamMember).Test() ClassMethod Test(AddTwoLeaders = {$$$YES}) { do ..%KillExtent() write $System.Status.GetErrorText(..Add(1, "Alice")) write $System.Status.GetErrorText(..Add(1, "Bob")) write $System.Status.GetErrorText(..Add(1, "Clover")) write $System.Status.GetErrorText(..Add(1, "Dave", 1)) if AddTwoLeaders { write $System.Status.GetErrorText(..Add(1, "Helen", 1)) } } ClassMethod Add(Team, Member, IsLeader = "") { set obj = ..%New() set obj.Team = Team set obj.Member = Member set obj.IsLeader = IsLeader quit obj.%Save() } }
go to post Eduard Lebedyuk · Dec 20, 2016 You should have FHIR package, which includes dtls to transform hl7 messages into json or xml. You can either use that or write your own solution based on that.
go to post Eduard Lebedyuk · Dec 9, 2016 How do you propose it should be handled instead?You provided two ways, by which you can control string/number output, and they seem to cover most of the cases.
go to post Eduard Lebedyuk · Dec 8, 2016 Hello.I have provided more comprehensive documentation for Ensemble Workflow REST API project.Do you think there's something else I need to add to it?
go to post Eduard Lebedyuk · Dec 6, 2016 is the import smart enough to figure out if a row already existsAs you can't specify an ID column during import, then no, SQL import wizard would only insert new rows.is there some other utility that can check for keys first?You can:export/import underlying global(s)use 3rd party SQL database explorers to generate UPDATE DDL statements from your data (DataGrip can do it for example), and then import this DDL into new namespace
go to post Eduard Lebedyuk · Nov 16, 2016 I think you need a persistent email message class, which you can customize as you need.
go to post Eduard Lebedyuk · Nov 13, 2016 1. Get list of all items in production (via Ens.Director:getProductionItems)2. Iterate over items local array and for each item:Get list of settings for an item (via Ens.Director:GetItemSettings)Check if ReplyCodeActions is a setting for current item, if it is get it's value.Write into any structure the pairs Item:ReplyCodeActionsValue Structure can be anything you want:Custom classDynamic object%List%ListOfDataTypesThat mainly depends on what do you want to do with this information later.
go to post Eduard Lebedyuk · Nov 4, 2016 Ceiling, floor, $NORMALIZE. Examples: >Write $SYSTEM.SQL.CEILING(.1) 1 >Write $SYSTEM.SQL.CEILING(1.2) 2 >Write $SYSTEM.SQL.CEILING(1.7) 2 >Write $SYSTEM.SQL.FLOOR(.1) 0 >Write $SYSTEM.SQL.FLOOR(1.2) 1 >Write $SYSTEM.SQL.FLOOR(1.7) 1 >Write $NORMALIZE(.1, 0) 0 >Write $NORMALIZE(1.2, 0) 1 >Write $NORMALIZE(1.7, 0) 2
go to post Eduard Lebedyuk · Oct 31, 2016 This line causes an error: Write tMessage.Name,! Your class does not have Name property, so it causes an error. The following method works : /// Do ##class(Testing.Messages.Session).test() ClassMethod test() { Set messagedata = "<?xml version=""1.0"" encoding=""UTF-8""?><session><sessionId>124364</sessionId><cabinet>demo</cabinet><eventType>IN</eventType><eventTime>20161006160154</eventTime><login>test</login><loginFirstName>test</loginFirstName><loginLastName>test</loginLastName></session>" Set reader = ##class(%XML.Reader).%New() Set status = reader.OpenString(messagedata) Do reader.Rewind() If $$$ISERR(status) {do $System.Status.DisplayError(status)} // Associate a class name with the XML element name Do reader.CorrelateRoot("Testing.Messages.Session") // Read objects from xml data While (reader.Next(.tMessage,.status)) { Do:$$$ISERR(status) $System.Status.DisplayError(status) Write tMessage.sessionId,! } } Terminal: USER >do ##class(Testing.Messages.Session).test() 124364
go to post Eduard Lebedyuk · Oct 18, 2016 Disclaimer. I am not familiar with EDI.Some solutions would be:Get official EDI 271 xsd schema files and import them into CachéAlternatively you can read EDI 271 specification/check examples and write your own classesThen you need a dtl to transform incoming message into your new classes. If there's a lot of them maybe it would be better to write one generic transformer or write a transformer generator based on class (which would generate transformation method based on class properties).After that you can transform your class object into json via several ways:Old json via %ZEN.proxyObject/%ZEN.auxillary.jsonProviderNew json via dynamic objects
go to post Eduard Lebedyuk · Oct 17, 2016 The CALLIN^%ZSTART entry point gets executed when an external program begins or completes a callin. You can add logging there to see if something initiates callin.
go to post Eduard Lebedyuk · Oct 11, 2016 You can write a dtl transformation or a generic business process which accepts object, dynamically gets a list of properties to convert (All %String properties? Some kind of a dictionary? Pattern?) and strips diacritic signs from them.
go to post Eduard Lebedyuk · Sep 27, 2016 We have a google calendar (and map) integration for Caché as a part of System of training courses grant project. Check out Stc.Google package there, especially Stc.Google.Calendar class.
go to post Eduard Lebedyuk · Sep 27, 2016 Yes. You can use cursors for that. In the following example rowlist contains list of affected ids. You can get it all at the end or get individual ids right before or after the update, or even decide on the update based on id/val values: Class User.NewClass1 Extends %Persistent { Property val; /// do ##class(User.NewClass1).Test() ClassMethod Test() { do ..%KillExtent() &sql(INSERT INTO NewClass1 SET val = 0) &sql(INSERT INTO NewClass1 SET val = 3) set rowlist = "" &sql(DECLARE NewClass1 CURSOR FOR SELECT %ID,val INTO :id, :val FROM NewClass1) &sql(OPEN NewClass1) for { &sql(FETCH NewClass1) quit:SQLCODE'=0 set val2 = val*2 write "Processing id: ", id,! set rowlist = rowlist _ $lb(id) &sql(UPDATE NewClass1 SET val = :val2 WHERE CURRENT OF NewClass1) } &sql(CLOSE NewClass1) zw rowlist } } It would output in a terminal: >do ##class(User.NewClass1).Test() Processing id: 1 Processing id: 2 rowlist=$lb("1","2") Documentation: WHERE CURRENT OFSQL Cursors
go to post Eduard Lebedyuk · Sep 27, 2016 Here's an example: Class User.NewClass1 Extends %Persistent { Property streams As list Of %Stream.GlobalCharacter; ClassMethod Test() { do ..%KillExtent() set obj = ..%New() set stream1 = ##class(%Stream.GlobalCharacter).%New() do stream1.WriteLine("Hi") set stream2 = ##class(%Stream.GlobalCharacter).%New() do stream2.WriteLine(123) do obj.streams.Insert(stream1) do obj.streams.Insert(stream2) write $System.Status.GetErrorText(obj.%Save()) kill set obj = ..%OpenId(1) for i=1:1:obj.streams.Count() { write "Stream #", i, ": ", obj.streams.GetAt(i).Read($$$MaxCacheInt) } } }
go to post Eduard Lebedyuk · Sep 20, 2016 In the underlying SQL write SELECT SUBSTRING(Comments, 1, 100) As Comments, ... FROM TABLE to get first 100 characters from the stream. More info.
go to post Eduard Lebedyuk · Sep 19, 2016 IF we use cookies, they will be stored in the Session Cookie Path.Cookie has a property named path. Whed browser determines, does the cookie apply to a current page, it checks if the cookie path is less or equal to current URL.I'm thinking that this login cookie would be used somehow if the Login Cookie is selected? Or not used? It would be used, if checked.what does happen if the Login Cookie is selected in the web application?Login Cookies hold information about the most recently logged-in user. If you want to keep your users from having to log in too often, but you want your applications to remain distinct and unconnected, use Login Cookies. For Login Cookies, place each application in a separate session. Then authentication is shared only when an application is entered for the first time. Login Cookies applications do not form a group. So after login, changes in authentication in one application do not affect the other applications. Documentation.What could we store in a cookie? Can we possibly find out if a second tab has been opened by using a cookie?You can store text values in cookie. I suggest you read wiki article on them.
go to post Eduard Lebedyuk · Sep 12, 2016 Here are some thoughts:Write Source Control Hook that implemets OnAfterSave method and checks there (or maybe some other entry point). There are several sample source control hook classes, check them out.Use ^rINDEXCLASS global (key - class name in uppercase) - it contains some basic information such as modification time (1st position) and hash (13th position). You can monitor it and if time or hash changes then record the new class version.Use lock table to see what classes are currently being editedUse $$$defClassKeyGet macros (see %Dictionary.ClassDefinition/%Dictionary.CompiledClass definitions, they use these macros a lot) to get info about modification time/hash and the changes themselves%Compiler.UDL.TextServices to get the class textI would have done it like this:Background process monitors ^rINDEXCLASS globalUpon finding changes get the current class code via %Compiler.UDL.TextServices class, GetTextAsStream methodWrite this information into your own class (classname, timestamp, user, classtext, diff, previousversion)Modification of system classes is not a very good idea:They are lost on updateUser may not have access required to install these changesThat being said, the best solution in my opinion is to setup your critical/production systems in such a way, that developers do not have direct write access to them. Each of the developers have their own environment where they can do whatever and then commit it to source control system. Continuous integration solution (or one developer or a script) then uploads the code (after it passes the tests) to the production server. There are several source control/continuous integration systems available for Caché.It is a good idea that a change can only be done via source control commit. All other changes simply should not exist.
go to post Eduard Lebedyuk · Sep 6, 2016 There is a beforeunload HTTP event which is fired when the window, the document and its resources are about to be unloaded. Handle this event in your js code and from there you can send an HTTP request to a server (I usually use GET /webapp/logout), which would do something like this: ClassMethod logout() As %Status { #dim %session As %CSP.Session set st = %session.Logout(1) set %session.EndSession = 1 return st }