YTWJSONE ;SLC/KCM - Simple Editor for JSON Instrument Spec ; 7/20/2018
 ;;5.01;MENTAL HEALTH;**141,172**;Dec 30, 1994;Build 10
 ;
 ; Usage: D EN^YTWJSONE        edit entry spec JSON & update checksum
 ;        D VALIDATE^YTWJSONE  checks recorded checksum against JSON actual
 ;
EN ; edit instrument Entry Specification
 K ^TMP("YTQ-EDIT",$J)
 N SPEC,TESTNM,DONE,ERRS,CRCOLD,CRCNEW
 S SPEC=$$LOOKUP() Q:'SPEC
 S TESTNM=$P(^YTT(601.71,+^YTT(601.712,SPEC,0),0),U)
 S CRCOLD=$P(^YTT(601.712,SPEC,0),U,3)
 M ^TMP("YTQ-EDIT",$J)=^YTT(601.712,SPEC,1)
 S DONE=0 F  D  Q:DONE
 . K ERRS S DONE=1
 . D EDIT(TESTNM)
 . I '$$HASMODS(SPEC) W !,"Nothing changed" QUIT
 . D CHKSPEC($NA(^TMP("YTQ-EDIT",$J)),.ERRS,.CRCNEW)
 . I $G(ERRS) S DONE=$S($$EOP:0,1:1) QUIT
 . D SAVE712(SPEC,CRCNEW)
 . W !,"Changes saved.  Old Checksum: ",CRCOLD,"  New Checksum: ",CRCNEW
 K ^TMP("YTQ-EDIT",$J)
 Q
LOOKUP() ; return 601.712 IEN for selected instrument
 N DIC,X,Y
 S DIC="^YTT(601.712,",DIC(0)="AEMQ" D ^DIC
 Q +Y
 ;
EDIT(TESTNM) ; edit copy of document in ^TMP("YTQ-EDIT",$J,
 N DIC,DDWAUTO,DDWFLAGS,DWLW,DWPK,DIWETXT,DIWESUB
 S DDWAUTO=1,DDWFLAGS="M"
 S DIC="^TMP(""YTQ-EDIT"","_$J_","
 S DWLW=132,DWPK=1,DIWETXT=TESTNM,DIWESUB="Entry Specification"
 D EN^DIWE
 Q
HASMODS(SPEC) ; return 1 if edited version differs from 601.712
 N I,CHANGES,ORIG,EDIT
 S CHANGES=0
 S I=0 F  S I=$O(^YTT(601.712,SPEC,1,I)) Q:'I  D  Q:CHANGES
 . S ORIG=$G(^YTT(601.712,SPEC,1,I,0))
 . S EDIT=$G(^TMP("YTQ-EDIT",$J,I,0))
 . I ORIG'=EDIT S CHANGES=1
 Q CHANGES
 ;
OKSAVE() ; return 1 if OK to save changes
 N DIR,X,Y,DTOUT,DUOUT,DIRUT,DIROUT
 S DIR("A")="Save changes",DIR(0)="Y",DIR("B")="YES" D ^DIR
 Q +Y
 ;
EOP() ; return 1 if continue, 0 if exit
 N DIR,X,Y,DTOUT,DUOUT,DIRUT,DIROUT
 S DIR(0)="E" D ^DIR
 Q +Y
 ;
CHKSPEC(SRC,ERRS,CRC) ; parses JSON and calculates checksum
 ; .ERRS: returns 1 if JSON parses without errors
 ;  .CRC: returns checksum
 K ^TMP("YTQ-JSON",$J),^TMP("YTQ-TREE",$J),^TMP("YTQ-LIST",$J)
 N JSON,TREE,LIST
 S JSON=$NA(^TMP("YTQ-JSON",$J))
 S TREE=$NA(^TMP("YTQ-TREE",$J))
 S LIST=$NA(^TMP("YTQ-LIST",$J))
 D FIXLF(SRC,JSON)
 D CHKJSON(JSON,TREE,.ERRS) I $G(ERRS) S CRC=0 QUIT
 D NAMEVAL(TREE,LIST)
 S CRC=$$NVCRC(LIST)
 K ^TMP("YTQ-JSON",$J),^TMP("YTQ-TREE",$J),^TMP("YTQ-LIST",$J)
 Q
FIXLF(SRC,DEST) ; Load spec from SRC, cleaning up line feeds
 ; SPEC: reference to global with original JSON
 ; DEST: reference to global root for destination JSON
 N I,J,X,Y
 S (I,J)=0 F  S I=$O(@SRC@(I)) Q:'I  S X=^(I,0) D
 . S J=J+1,@DEST@(J)=X
 . I (($L(X)-$L($TR(X,"""","")))#2) D  ; check for odd number of quotes
 . . F  S I=I+1 Q:'$D(@SRC@(I,0))  D  Q:Y[""""
 . . . S Y=@SRC@(I,0)
 . . . S @DEST@(J)=@DEST@(J)_Y
 Q
CHKJSON(JSON,TREE,ERRS) ; decode JSON and display any errors
 ;    JSON: reference to JSON global
 ;    TREE: reference to TREE global
 ; .ERRORS: return errors found or 0
 N ERRORS
 D DECODE^XLFJSON(JSON,TREE,"ERRORS")
 I $G(ERRORS(0)) D
 . S ERRS=ERRORS(0)
 . W !,"ERRORS found while parsing JSON document"
 . N I S I=0 F  S I=$O(ERRORS(I)) Q:'I  W !,?2,ERRORS(I)
 . W !!,"Returning to editor..."
 Q
 ;
NAMEVAL(TREE,LIST) ; Convert TREE to external object name/value list in LIST
 ; TREE: reference to global containing documents tree structure
 ; LIST: reference to global where @LIST@(name)=value will be saved
 ; NOTE -- resulting names will collate as string, so the order may be odd
 ;         for example, obj[1], obj[10], obj[11], obj[2], etc.
 N ROOT,LROOT,SB,SL,S,X,I,NM,VAL,ADDQ
 S ROOT=$E(TREE,1,$L(TREE)-1),LROOT=$L(ROOT) ; drop last ")" for compare
 S X=TREE,SB=$QL(TREE)+1                     ; begin at subscript after root 
 F  S X=$Q(@X) Q:$E(X,1,LROOT)'=ROOT  D
 . S SL=$QL(X),NM=""                         ; SL is last subscript
 . I $D(@X@("\s"))!$D(@X@("\n")) Q           ; node already evaluated
 . F I=SB:1:SL S S=$QS(X,I) S NM=NM_$S(+S:"["_S_"]",(I>3)&'S:"."_S,1:S)
 . S VAL=@X
 . S ADDQ=$$JSONSTR(VAL)                     ; check if num, str, bool
 . I $D(@X@("\s")) S ADDQ=1                  ; "\s" forces string
 . I $D(@X@("\n")) S ADDQ=0                  ; "\n" forces numeric
 . S @LIST@(NM)=$S(ADDQ:""""_VAL_"""",1:VAL) ; NM=VAL, NM collates as string
 Q
JSONSTR(X) ; return 1 if should be treated as a string
 Q:X="true" 0   ; boolean
 Q:X="false" 0  ; boolean
 Q:X="null" 0   ; null object
 Q:+X=X 0       ; numeric
 Q 1
 ;
NVCRC(LIST) ; return CRC32 for LIST(name)=value
 ; LIST: reference to global containing @LIST@(name)=value pairs
 N X,STR,CRC
 S CRC=0
 S X="" F  S X=$O(@LIST@(X)) Q:'$L(X)  D
 . S STR=X_"="_@LIST@(X)
 . S CRC=$$CRC32^XLFCRC(STR,CRC)
 Q CRC
 ;
TSCRC() ; return CRC32 of all checksums for active instruments
 N IEN,X,CRC
 S (IEN,CRC)=0 F  S IEN=$O(^YTT(601.712,IEN)) Q:'IEN  D
 . S CRC=$$CRC32^XLFCRC($P(^YTT(601.712,IEN,0),U,3),CRC)
 Q CRC
 ;
SHA1ALL() ; return SHA-1 of all checksums for active instruments
 N IEN,X
 S X=""
 S IEN=0 F  S IEN=$O(^YTT(601.712,IEN)) Q:'IEN  S X=X_$P(^(IEN,0),U,3)
 W !,"Length: ",$L(X),"  X=",!,X
 W !,$$SHAHASH^XUSHSH(160,X,"H")
 Q
SAVE712(IEN,CRC) ; save updated specification to 601.712
 ; expects ^TMP("YTQ-EDIT,$J,n,0) to be entry spec text
 N REC
 S REC(.02)=$$NOW^XLFDT
 S REC(.03)=CRC
 S REC(1)=$NA(^TMP("YTQ-EDIT",$J))
 D FMUPD^YTXCHGU(601.712,.REC,IEN)
 Q
 ;
VALIDATE ; compare CRC with actual and check JSON structure
 N SPEC,TEST
 S SPEC=0 F  S SPEC=$O(^YTT(601.712,SPEC)) Q:'SPEC  D
 . S TEST=+$P(^YTT(601.712,SPEC,0),U)
 . I '$D(^YTT(601.71,TEST,0)) D  QUIT
 . . W !,"Missing entry in 601.71: ",TEST,"   ",^YTT(601.712,SPEC,1,1,0)
 . W !,$P(^YTT(601.71,+^YTT(601.712,SPEC,0),0),U)
 . N ERRS,CRCCALC,CRCSAVED
 . S CRCSAVED=$P(^YTT(601.712,SPEC,0),U,3)
 . D CHKSPEC($NA(^YTT(601.712,SPEC,1)),.ERRS,.CRCCALC)
 . I '$G(ERRS),(CRCSAVED=CRCCALC) W ?30,"ok" QUIT
 . I CRCSAVED'=CRCCALC W ?30,"Saved Checksum: ",CRCSAVED,"   Actual: ",CRCCALC
 . I $G(ERRS) W "  JSON errors"
 Q
 ;
FILE(TEST,PATH) ; write JSON from 601.712 to host file in PATH
 N IEN,OK,NAME
 I TEST'=+TEST S TEST=$O(^YTT(601.71,"B",TEST,0)) Q:'TEST
 S IEN=$O(^YTT(601.712,"B",TEST,0)) Q:'IEN
 S NAME=$TR($P(^YTT(601.71,TEST,0),U)," ","_")_"-espec.json"
 ;
 K ^TMP($J)
 M ^TMP($J)=^YTT(601.712,IEN,1) ; so can use $$GTF^%ZISH
 K ^TMP($J,0)                   ; remove count & date node
 S OK=$$GTF^%ZISH($NA(^TMP($J,1,0)),2,PATH,NAME)
 I 'OK W !,"Error writing file: "_NAME
 K ^TMP($J)
 Q
LOAD(TEST,PATH) ; read host file in PATH and load into 601.712
 N TESTNM,SPEC,FILE,OK,CRCOLD,CRCNEW,ERRS
 I TEST'=+TEST S TEST=$O(^YTT(601.71,"B",TEST,0)) QUIT:'TEST
 S TESTNM=$P(^YTT(601.71,TEST,0),U)
 S SPEC=$O(^YTT(601.712,"B",TEST,0)) QUIT:'SPEC
 S FILE=$TR(TESTNM," ","_")_"-espec.json"
 S CRCOLD=$P(^YTT(601.712,SPEC,0),U,3)
 ;
 K ^TMP("YTQ-EDIT",$J)
 S OK=$$FTG^%ZISH(PATH,FILE,$NA(^TMP("YTQ-EDIT",$J,1,0)),3)
 I 'OK W !,"Error reading file: "_FILE QUIT
 I '$$HASMODS(SPEC) W !,"Nothing changed" QUIT
 ;
 D CHKSPEC($NA(^TMP("YTQ-EDIT",$J)),.ERRS,.CRCNEW) QUIT:$G(ERRS)
 D SAVE712(SPEC,CRCNEW)
 W !,TESTNM," entry spec saved.  Old Checksum: ",CRCOLD,"  New Checksum: ",CRCNEW
 K ^TMP("YTQ-EDIT",$J)
 Q
 
--- Routine Detail   --- with STRUCTURED ROUTINE LISTING ---[H[J[2J[HYTWJSONE   7247     printed  Sep 23, 2025@19:58:07                                                                                                                                                                                                    Page 2
YTWJSONE  ;SLC/KCM - Simple Editor for JSON Instrument Spec ; 7/20/2018
 +1       ;;5.01;MENTAL HEALTH;**141,172**;Dec 30, 1994;Build 10
 +2       ;
 +3       ; Usage: D EN^YTWJSONE        edit entry spec JSON & update checksum
 +4       ;        D VALIDATE^YTWJSONE  checks recorded checksum against JSON actual
 +5       ;
EN        ; edit instrument Entry Specification
 +1        KILL ^TMP("YTQ-EDIT",$JOB)
 +2        NEW SPEC,TESTNM,DONE,ERRS,CRCOLD,CRCNEW
 +3        SET SPEC=$$LOOKUP()
           if 'SPEC
               QUIT 
 +4        SET TESTNM=$PIECE(^YTT(601.71,+^YTT(601.712,SPEC,0),0),U)
 +5        SET CRCOLD=$PIECE(^YTT(601.712,SPEC,0),U,3)
 +6        MERGE ^TMP("YTQ-EDIT",$JOB)=^YTT(601.712,SPEC,1)
 +7        SET DONE=0
           FOR 
               Begin DoDot:1
 +8                KILL ERRS
                   SET DONE=1
 +9                DO EDIT(TESTNM)
 +10               IF '$$HASMODS(SPEC)
                       WRITE !,"Nothing changed"
                       QUIT 
 +11               DO CHKSPEC($NAME(^TMP("YTQ-EDIT",$JOB)),.ERRS,.CRCNEW)
 +12               IF $GET(ERRS)
                       SET DONE=$SELECT($$EOP:0,1:1)
                       QUIT 
 +13               DO SAVE712(SPEC,CRCNEW)
 +14               WRITE !,"Changes saved.  Old Checksum: ",CRCOLD,"  New Checksum: ",CRCNEW
               End DoDot:1
               if DONE
                   QUIT 
 +15       KILL ^TMP("YTQ-EDIT",$JOB)
 +16       QUIT 
LOOKUP()  ; return 601.712 IEN for selected instrument
 +1        NEW DIC,X,Y
 +2        SET DIC="^YTT(601.712,"
           SET DIC(0)="AEMQ"
           DO ^DIC
 +3        QUIT +Y
 +4       ;
EDIT(TESTNM) ; edit copy of document in ^TMP("YTQ-EDIT",$J,
 +1        NEW DIC,DDWAUTO,DDWFLAGS,DWLW,DWPK,DIWETXT,DIWESUB
 +2        SET DDWAUTO=1
           SET DDWFLAGS="M"
 +3        SET DIC="^TMP(""YTQ-EDIT"","_$JOB_","
 +4        SET DWLW=132
           SET DWPK=1
           SET DIWETXT=TESTNM
           SET DIWESUB="Entry Specification"
 +5        DO EN^DIWE
 +6        QUIT 
HASMODS(SPEC) ; return 1 if edited version differs from 601.712
 +1        NEW I,CHANGES,ORIG,EDIT
 +2        SET CHANGES=0
 +3        SET I=0
           FOR 
               SET I=$ORDER(^YTT(601.712,SPEC,1,I))
               if 'I
                   QUIT 
               Begin DoDot:1
 +4                SET ORIG=$GET(^YTT(601.712,SPEC,1,I,0))
 +5                SET EDIT=$GET(^TMP("YTQ-EDIT",$JOB,I,0))
 +6                IF ORIG'=EDIT
                       SET CHANGES=1
               End DoDot:1
               if CHANGES
                   QUIT 
 +7        QUIT CHANGES
 +8       ;
OKSAVE()  ; return 1 if OK to save changes
 +1        NEW DIR,X,Y,DTOUT,DUOUT,DIRUT,DIROUT
 +2        SET DIR("A")="Save changes"
           SET DIR(0)="Y"
           SET DIR("B")="YES"
           DO ^DIR
 +3        QUIT +Y
 +4       ;
EOP()     ; return 1 if continue, 0 if exit
 +1        NEW DIR,X,Y,DTOUT,DUOUT,DIRUT,DIROUT
 +2        SET DIR(0)="E"
           DO ^DIR
 +3        QUIT +Y
 +4       ;
CHKSPEC(SRC,ERRS,CRC) ; parses JSON and calculates checksum
 +1       ; .ERRS: returns 1 if JSON parses without errors
 +2       ;  .CRC: returns checksum
 +3        KILL ^TMP("YTQ-JSON",$JOB),^TMP("YTQ-TREE",$JOB),^TMP("YTQ-LIST",$JOB)
 +4        NEW JSON,TREE,LIST
 +5        SET JSON=$NAME(^TMP("YTQ-JSON",$JOB))
 +6        SET TREE=$NAME(^TMP("YTQ-TREE",$JOB))
 +7        SET LIST=$NAME(^TMP("YTQ-LIST",$JOB))
 +8        DO FIXLF(SRC,JSON)
 +9        DO CHKJSON(JSON,TREE,.ERRS)
           IF $GET(ERRS)
               SET CRC=0
               QUIT 
 +10       DO NAMEVAL(TREE,LIST)
 +11       SET CRC=$$NVCRC(LIST)
 +12       KILL ^TMP("YTQ-JSON",$JOB),^TMP("YTQ-TREE",$JOB),^TMP("YTQ-LIST",$JOB)
 +13       QUIT 
FIXLF(SRC,DEST) ; Load spec from SRC, cleaning up line feeds
 +1       ; SPEC: reference to global with original JSON
 +2       ; DEST: reference to global root for destination JSON
 +3        NEW I,J,X,Y
 +4        SET (I,J)=0
           FOR 
               SET I=$ORDER(@SRC@(I))
               if 'I
                   QUIT 
               SET X=^(I,0)
               Begin DoDot:1
 +5                SET J=J+1
                   SET @DEST@(J)=X
 +6       ; check for odd number of quotes
                   IF (($LENGTH(X)-$LENGTH($TRANSLATE(X,"""","")))#2)
                       Begin DoDot:2
 +7                        FOR 
                               SET I=I+1
                               if '$DATA(@SRC@(I,0))
                                   QUIT 
                               Begin DoDot:3
 +8                                SET Y=@SRC@(I,0)
 +9                                SET @DEST@(J)=@DEST@(J)_Y
                               End DoDot:3
                               if Y[""""
                                   QUIT 
                       End DoDot:2
               End DoDot:1
 +10       QUIT 
CHKJSON(JSON,TREE,ERRS) ; decode JSON and display any errors
 +1       ;    JSON: reference to JSON global
 +2       ;    TREE: reference to TREE global
 +3       ; .ERRORS: return errors found or 0
 +4        NEW ERRORS
 +5        DO DECODE^XLFJSON(JSON,TREE,"ERRORS")
 +6        IF $GET(ERRORS(0))
               Begin DoDot:1
 +7                SET ERRS=ERRORS(0)
 +8                WRITE !,"ERRORS found while parsing JSON document"
 +9                NEW I
                   SET I=0
                   FOR 
                       SET I=$ORDER(ERRORS(I))
                       if 'I
                           QUIT 
                       WRITE !,?2,ERRORS(I)
 +10               WRITE !!,"Returning to editor..."
               End DoDot:1
 +11       QUIT 
 +12      ;
NAMEVAL(TREE,LIST) ; Convert TREE to external object name/value list in LIST
 +1       ; TREE: reference to global containing documents tree structure
 +2       ; LIST: reference to global where @LIST@(name)=value will be saved
 +3       ; NOTE -- resulting names will collate as string, so the order may be odd
 +4       ;         for example, obj[1], obj[10], obj[11], obj[2], etc.
 +5        NEW ROOT,LROOT,SB,SL,S,X,I,NM,VAL,ADDQ
 +6       ; drop last ")" for compare
           SET ROOT=$EXTRACT(TREE,1,$LENGTH(TREE)-1)
           SET LROOT=$LENGTH(ROOT)
 +7       ; begin at subscript after root 
           SET X=TREE
           SET SB=$QLENGTH(TREE)+1
 +8        FOR 
               SET X=$QUERY(@X)
               if $EXTRACT(X,1,LROOT)'=ROOT
                   QUIT 
               Begin DoDot:1
 +9       ; SL is last subscript
                   SET SL=$QLENGTH(X)
                   SET NM=""
 +10      ; node already evaluated
                   IF $DATA(@X@("\s"))!$DATA(@X@("\n"))
                       QUIT 
 +11               FOR I=SB:1:SL
                       SET S=$QSUBSCRIPT(X,I)
                       SET NM=NM_$SELECT(+S:"["_S_"]",(I>3)&'S:"."_S,1:S)
 +12               SET VAL=@X
 +13      ; check if num, str, bool
                   SET ADDQ=$$JSONSTR(VAL)
 +14      ; "\s" forces string
                   IF $DATA(@X@("\s"))
                       SET ADDQ=1
 +15      ; "\n" forces numeric
                   IF $DATA(@X@("\n"))
                       SET ADDQ=0
 +16      ; NM=VAL, NM collates as string
                   SET @LIST@(NM)=$SELECT(ADDQ:""""_VAL_"""",1:VAL)
               End DoDot:1
 +17       QUIT 
JSONSTR(X) ; return 1 if should be treated as a string
 +1       ; boolean
           if X="true"
               QUIT 0
 +2       ; boolean
           if X="false"
               QUIT 0
 +3       ; null object
           if X="null"
               QUIT 0
 +4       ; numeric
           if +X=X
               QUIT 0
 +5        QUIT 1
 +6       ;
NVCRC(LIST) ; return CRC32 for LIST(name)=value
 +1       ; LIST: reference to global containing @LIST@(name)=value pairs
 +2        NEW X,STR,CRC
 +3        SET CRC=0
 +4        SET X=""
           FOR 
               SET X=$ORDER(@LIST@(X))
               if '$LENGTH(X)
                   QUIT 
               Begin DoDot:1
 +5                SET STR=X_"="_@LIST@(X)
 +6                SET CRC=$$CRC32^XLFCRC(STR,CRC)
               End DoDot:1
 +7        QUIT CRC
 +8       ;
TSCRC()   ; return CRC32 of all checksums for active instruments
 +1        NEW IEN,X,CRC
 +2        SET (IEN,CRC)=0
           FOR 
               SET IEN=$ORDER(^YTT(601.712,IEN))
               if 'IEN
                   QUIT 
               Begin DoDot:1
 +3                SET CRC=$$CRC32^XLFCRC($PIECE(^YTT(601.712,IEN,0),U,3),CRC)
               End DoDot:1
 +4        QUIT CRC
 +5       ;
SHA1ALL() ; return SHA-1 of all checksums for active instruments
 +1        NEW IEN,X
 +2        SET X=""
 +3        SET IEN=0
           FOR 
               SET IEN=$ORDER(^YTT(601.712,IEN))
               if 'IEN
                   QUIT 
               SET X=X_$PIECE(^(IEN,0),U,3)
 +4        WRITE !,"Length: ",$LENGTH(X),"  X=",!,X
 +5        WRITE !,$$SHAHASH^XUSHSH(160,X,"H")
 +6        QUIT 
SAVE712(IEN,CRC) ; save updated specification to 601.712
 +1       ; expects ^TMP("YTQ-EDIT,$J,n,0) to be entry spec text
 +2        NEW REC
 +3        SET REC(.02)=$$NOW^XLFDT
 +4        SET REC(.03)=CRC
 +5        SET REC(1)=$NAME(^TMP("YTQ-EDIT",$JOB))
 +6        DO FMUPD^YTXCHGU(601.712,.REC,IEN)
 +7        QUIT 
 +8       ;
VALIDATE  ; compare CRC with actual and check JSON structure
 +1        NEW SPEC,TEST
 +2        SET SPEC=0
           FOR 
               SET SPEC=$ORDER(^YTT(601.712,SPEC))
               if 'SPEC
                   QUIT 
               Begin DoDot:1
 +3                SET TEST=+$PIECE(^YTT(601.712,SPEC,0),U)
 +4                IF '$DATA(^YTT(601.71,TEST,0))
                       Begin DoDot:2
 +5                        WRITE !,"Missing entry in 601.71: ",TEST,"   ",^YTT(601.712,SPEC,1,1,0)
                       End DoDot:2
                       QUIT 
 +6                WRITE !,$PIECE(^YTT(601.71,+^YTT(601.712,SPEC,0),0),U)
 +7                NEW ERRS,CRCCALC,CRCSAVED
 +8                SET CRCSAVED=$PIECE(^YTT(601.712,SPEC,0),U,3)
 +9                DO CHKSPEC($NAME(^YTT(601.712,SPEC,1)),.ERRS,.CRCCALC)
 +10               IF '$GET(ERRS)
                       IF (CRCSAVED=CRCCALC)
                           WRITE ?30,"ok"
                           QUIT 
 +11               IF CRCSAVED'=CRCCALC
                       WRITE ?30,"Saved Checksum: ",CRCSAVED,"   Actual: ",CRCCALC
 +12               IF $GET(ERRS)
                       WRITE "  JSON errors"
               End DoDot:1
 +13       QUIT 
 +14      ;
FILE(TEST,PATH) ; write JSON from 601.712 to host file in PATH
 +1        NEW IEN,OK,NAME
 +2        IF TEST'=+TEST
               SET TEST=$ORDER(^YTT(601.71,"B",TEST,0))
               if 'TEST
                   QUIT 
 +3        SET IEN=$ORDER(^YTT(601.712,"B",TEST,0))
           if 'IEN
               QUIT 
 +4        SET NAME=$TRANSLATE($PIECE(^YTT(601.71,TEST,0),U)," ","_")_"-espec.json"
 +5       ;
 +6        KILL ^TMP($JOB)
 +7       ; so can use $$GTF^%ZISH
           MERGE ^TMP($JOB)=^YTT(601.712,IEN,1)
 +8       ; remove count & date node
           KILL ^TMP($JOB,0)
 +9        SET OK=$$GTF^%ZISH($NAME(^TMP($JOB,1,0)),2,PATH,NAME)
 +10       IF 'OK
               WRITE !,"Error writing file: "_NAME
 +11       KILL ^TMP($JOB)
 +12       QUIT 
LOAD(TEST,PATH) ; read host file in PATH and load into 601.712
 +1        NEW TESTNM,SPEC,FILE,OK,CRCOLD,CRCNEW,ERRS
 +2        IF TEST'=+TEST
               SET TEST=$ORDER(^YTT(601.71,"B",TEST,0))
               if 'TEST
                   QUIT 
 +3        SET TESTNM=$PIECE(^YTT(601.71,TEST,0),U)
 +4        SET SPEC=$ORDER(^YTT(601.712,"B",TEST,0))
           if 'SPEC
               QUIT 
 +5        SET FILE=$TRANSLATE(TESTNM," ","_")_"-espec.json"
 +6        SET CRCOLD=$PIECE(^YTT(601.712,SPEC,0),U,3)
 +7       ;
 +8        KILL ^TMP("YTQ-EDIT",$JOB)
 +9        SET OK=$$FTG^%ZISH(PATH,FILE,$NAME(^TMP("YTQ-EDIT",$JOB,1,0)),3)
 +10       IF 'OK
               WRITE !,"Error reading file: "_FILE
               QUIT 
 +11       IF '$$HASMODS(SPEC)
               WRITE !,"Nothing changed"
               QUIT 
 +12      ;
 +13       DO CHKSPEC($NAME(^TMP("YTQ-EDIT",$JOB)),.ERRS,.CRCNEW)
           if $GET(ERRS)
               QUIT 
 +14       DO SAVE712(SPEC,CRCNEW)
 +15       WRITE !,TESTNM," entry spec saved.  Old Checksum: ",CRCOLD,"  New Checksum: ",CRCNEW
 +16       KILL ^TMP("YTQ-EDIT",$JOB)
 +17       QUIT