unit BatchAddU; (* WorldVistA Configuration Utility (c) 8/2008 Kevin Toppenberg Programmed by Kevin Toppenberg, Eddie Hagood Family Physicians of Greeneville, PC 1410 Tusculum Blvd, Suite 2600 Greeneville, TN 37745 kdtop@yahoo.com This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *) interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, Grids, ExtCtrls, ComCtrls, DateUtils; type TBatchAddForm = class(TForm) Panel1: TPanel; Panel2: TPanel; BatchGrid: TStringGrid; btnCreateTemplate: TBitBtn; btnOpenDataFile: TBitBtn; btnDoRegistration: TBitBtn; btnClearGrid: TBitBtn; OpenDialog: TOpenDialog; btnDone: TBitBtn; ProgressBar: TProgressBar; btnSaveGrid: TBitBtn; btnAbortRegistration: TBitBtn; SaveDialog: TSaveDialog; EstTimeLabel: TLabel; procedure btnCreateTemplateClick(Sender: TObject); procedure btnClearGridClick(Sender: TObject); procedure btnOpenDataFileClick(Sender: TObject); function LTrim(s,subStr : string): string; function RTrim(s,subStr : string): string; function Trim(s,subStr : string): string; procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure FormCreate(Sender: TObject); procedure btnDoRegistrationClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure btnAbortRegistrationClick(Sender: TObject); procedure btnSaveGridClick(Sender: TObject); procedure BatchGridSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: String); private { Private declarations } FieldNumList : TStringList; FAbortRegistration : boolean; procedure ClearGrid; procedure LoadData(fileName : string); procedure AddHeaderCol(oneEntry : string); procedure GetColFieldNums(List : TStringList); function GetOneRow(row : integer; ColFields : TStringList) : string; function RowToStr(row : integer) : string; procedure AddRowFromStr(Str : string); function RegisterOne(oneRow : string; Log : TStringList) : string; //procedure DelGridRow(BatchGrid : TStringGrid; row : integer); public { Public declarations } end; var BatchAddForm: TBatchAddForm; implementation uses CreateTemplateU, StrUtils, ORNet, ORFn, Trpcb, //needed for .ptype types FMErrorU; const NOT_ADDED='(Not Added)'; ADD_ROW='Add-->'; ALREADY_ADDED='(Already Registered)'; {$R *.dfm} procedure TBatchAddForm.ClearGrid; begin BatchGrid.ColCount := 2; BatchGrid.RowCount := 3; BatchGrid.Cells[0,0] := 'Field NAME'; BatchGrid.Cells[0,1] := 'Field NUM'; BatchGrid.Cells[0,2] := ''; BatchGrid.Cells[1,0] := ''; BatchGrid.Cells[1,1] := ''; BatchGrid.Cells[1,2] := ''; end; procedure TBatchAddForm.btnCreateTemplateClick(Sender: TObject); begin CreateTemplateForm.PrepForm('2'); CreateTemplateForm.ShowModal; end; procedure TBatchAddForm.btnClearGridClick(Sender: TObject); begin if MessageDlg('Clear All Patients?',mtConfirmation,mbOKCancel,0) = mrOK then begin ClearGrid; btnSaveGrid.Enabled := false; btnDoRegistration.Enabled := false; btnClearGrid.Enabled := false; btnCreateTemplate.Enabled := true; btnOpenDataFile.Enabled := true; end; end; procedure TBatchAddForm.btnOpenDataFileClick(Sender: TObject); begin if OpenDialog.Execute then begin LoadData(OpenDialog.FileName); btnClearGrid.Enabled := true; btnDoRegistration.Enabled := true; btnCreateTemplate.Enabled := false; btnOpenDataFile.Enabled := false; end; end; function TBatchAddForm.RTrim(s,subStr : string): string; var p,subLen : integer; begin result := s; p := Pos(subStr,s); subLen := length(subStr); if p <> length(s)-subLen+1 then exit; result := MidStr(s,1, length(s)-subLen); end; function TBatchAddForm.LTrim(s,subStr : string): string; var p,subLen : integer; begin result := s; p := Pos(subStr,s); subLen := length(subStr); if p <> 1 then exit; result := MidStr(s,subLen+1,999); end; function TBatchAddForm.Trim(s,subStr : string): string; begin result := LTrim(s,subStr); result := RTrim(result,subStr); end; procedure TBatchAddForm.LoadData(fileName : string); var Data: TStringList; Line1List: TStringList; oneLine : string; value : string; row,i,j : integer; fldName,fldNum : string; dataFound : boolean; oneEntry : string; begin Data := TStringList.Create; Line1List := TStringList.Create; Data.LoadFromFile(fileName); if Data.Count < 3 then begin MessageDlg('Template file has less than 3 lines.'+#10+#13+ 'Doesn''t appear to be valid template'+#10+#13+ 'filled with patient data. Please fill '+#10+#13+ 'with patient demographics first.', mtError,[mbOK],0); exit; end; ClearGrid; //------- set up header rows (name and number) ------------------ oneLine := Data.Strings[0]; PiecesToList(oneLine, #9, Line1List); oneLine := Data.Strings[1]; PiecesToList(oneLine, #9, FieldNumList); for i := 1 to Line1List.Count-1 do begin fldName := Trim(Line1List.Strings[i],'"'); if i < FieldNumList.Count then begin fldNum := LTrim(FieldNumList.Strings[i],'"<'); fldNum := RTrim(fldNum,'>"'); end else begin fldNum := '??'; end; oneEntry := fldNum+'^'+fldName; AddHeaderCol(oneEntry); end; //--------- now load data ---------------------- dataFound := true; for i := 2 to Data.Count-1 do begin if dataFound then begin if BatchGrid.RowCount = 3 then begin if BatchGrid.Cells[0,2] <> '' then BatchGrid.RowCount := BatchGrid.RowCount + 1; end else begin BatchGrid.RowCount := BatchGrid.RowCount + 1; end; end; row := BatchGrid.RowCount-1; oneLine := Data.Strings[i]; PiecesToList(oneLine, #9, Line1List); BatchGrid.Cells[0,row] := ADD_ROW; dataFound := false; //don't advance line if data row is empty. for j := 1 to Line1List.Count-1 do begin //don't use first column of data file value := Line1List.Strings[j]; value := Trim(value,'"'); if FieldNumList.Strings[j]='"<.09>"' then begin value := LTrim(value,'<'); value := RTrim(value,'>'); end; if value <> '' then dataFound := true; BatchGrid.Cells[j,row]:= value; end; if (dataFound = false) and (i = Data.Count-1) then begin BatchGrid.RowCount := BatchGrid.RowCount - 1; end; end; Data.Free; Line1List.Free; end; procedure TBatchAddForm.AddHeaderCol(oneEntry : string); //oneEntry format: fldNum^fldName var i : integer; fldName,fldNum : string; begin CreateTemplateForm.GetNumName(oneEntry,fldName,fldNum); i := BatchGrid.ColCount-1; if BatchGrid.Cells[i,0] <> '' then begin BatchGrid.ColCount := BatchGrid.ColCount + 1; end; i := BatchGrid.ColCount-1; BatchGrid.Cells[i,0] := fldName; BatchGrid.Cells[i,1] := fldNum; end; procedure TBatchAddForm.FormClose(Sender: TObject; var Action: TCloseAction); begin { if MessageDlg('Leave Batch Registration? (Later check to ensure data was saved)',mtConfirmation,mbOKCancel,0) = mrOK then begin Action := caHide; end else begin Action := caNone; end; } end; procedure TBatchAddForm.FormCreate(Sender: TObject); begin FieldNumList := TStringList.Create; end; procedure TBatchAddForm.btnDoRegistrationClick(Sender: TObject); var i,row : integer; FailedPatients : TStringList; ColFields : TStringList; onePostEntry : string; RPCResult : string; Log : TStringList; Success,Failure,PreExisting,Errors : integer; StartTime : TDateTime; procedure ShowEstTime(Pct : single); var Delta : single; EstTotalMin : Integer; s : string; begin Delta := MinuteSpan(Now,StartTime); if Pct=0 then begin EstTimeLabel.caption := ''; exit; end; EstTotalMin := Round(Delta / Pct); s := ''; if EstTotalMin > 1440 then begin //extract number of days s := s + IntToStr(EstTotalMin div 1440) + 'd:'; EstTotalMIn := EstTotalMin mod 1440; end; if EstTotalMin > 60 then begin //extract number of hours if s <> '' then s := s + IntToStr(EstTotalMin div 60) + 'h:' else s := IntToStr(EstTotalMin div 60) + ':'; EstTotalMIn := EstTotalMin mod 60; end; s := s + IntToStr(EstTotalMin) + ':'; EstTimeLabel.Caption := s; end; begin btnOpenDataFile.Enabled := false; btnClearGrid.Enabled := true; btnAbortRegistration.Enabled := True; btnClearGrid.Enabled := False; btnSaveGrid.Enabled := False; Log := TStringList.Create; Success := 0; Failure := 0; PreExisting := 0; Errors := 0; FMErrorForm.Memo.Lines.clear; ColFields := TStringList.Create; FailedPatients := TStringList.Create; GetColFieldNums(ColFields); ProgressBar.Max := BatchGrid.RowCount-1; FAbortRegistration := false; //abort button can change this. StartTime := Now; EstTimeLabel.Caption := ''; for row := 2 to BatchGrid.RowCount-1 do begin if FAbortRegistration = true then break; ProgressBar.Position := row; if (row mod 100) = 0 then ShowEstTime(row/(BatchGrid.RowCount-1)); onePostEntry := GetOneRow(row,ColFields); RPCResult := RegisterOne(onePostEntry,Log); //Returns: NewIEN^PrevReg^Reg'dNow^ErrorOccured BatchGrid.Cells[0,row] := Piece(RPCResult,'^',1); if Piece(RPCResult,'^',1)=NOT_ADDED then inc(Failure); if Piece(RPCResult,'^',2)='1' then inc(PreExisting); if Piece(RPCResult,'^',3)='1' then inc(Success); if Piece(RPCResult,'^',4)='1' then inc(Errors); Application.ProcessMessages; end; ProgressBar.Position := 0; ColFields.free; MessageDlg(IntToStr(Success)+' successful registrations or data refresh, '+#10+#13+ IntToStr(PreExisting)+' patients were already registered,'+#10+#13+ IntToStr(Errors)+' filing errors encountered (including minor errors),'+#10+#13+ IntToStr(Failure)+' patients NOT registered.', mtInformation,[mbOK],0); EstTimeLabel.Caption := ''; StartTime := Now; if BatchGrid.RowCount > 1000 then ShowMessage('Will now clear out patients that have been registered.'); for row := 2 to BatchGrid.RowCount-1 do begin if (BatchGrid.Cells[0,row] = NOT_ADDED) or (BatchGrid.Cells[0,row] = ADD_ROW) then begin FailedPatients.Add(RowToStr(row)); end; ProgressBar.Position := row; if (row mod 100) = 0 then ShowEstTime(row/(BatchGrid.RowCount-1)); Application.ProcessMessages; end; BatchGrid.RowCount := 2; ProgressBar.Max := FailedPatients.Count; StartTime := Now; for i := 0 to FailedPatients.Count-1 do begin AddRowFromStr(FailedPatients.Strings[i]); ProgressBar.Position := i; if (i mod 100) = 0 then ShowEstTime(i/(FailedPatients.Count-1)); Application.ProcessMessages; end; { for row := BatchGrid.RowCount-1 downto 2 do begin if (BatchGrid.Cells[0,row] <> NOT_ADDED) and (BatchGrid.Cells[0,row] <> ADD_ROW) then begin DelGridRow(BatchGrid,row); // <------- this is VERY SLOW!!@#@! end; ProgressBar.Position := row; if (row mod 100) = 0 then ShowEstTime((BatchGrid.RowCount-1-row)/(BatchGrid.RowCount-1)); Application.ProcessMessages; end; } EstTimeLabel.Caption := ''; if Log.Count>0 then begin FMErrorForm.Memo.Lines.Assign(Log); FMErrorForm.PrepMessage; FMErrorForm.ShowModal; end; Log.Free; if (BatchGrid.RowCount=3)and(BatchGrid.Cells[0,2]='') then begin btnSaveGrid.Enabled := false; btnDoRegistration.Enabled := false; btnClearGrid.Enabled := false; btnCreateTemplate.Enabled := true; btnOpenDataFile.Enabled := true; btnAbortRegistration.Enabled := true; end else begin btnClearGrid.Enabled := true; btnSaveGrid.Enabled := true; btnAbortRegistration.Enabled := false; end; FailedPatients.Free; end; function TBatchAddForm.RegisterOne(oneRow : string; Log : TStringList) : string; //Returns: NewIEN^Bool1^Bool2^Bool3 // NewIEN can be a #, or NOT_ADDED // For each Bool, 0=false, 1=true // Bool1 : Patient previously registered // Bool2 : Patient registered this time (using identifier fields) // Bool3 : Some Problem occurred during filing var tempResult,RPCResult : string; Msg : string; i : integer; begin RPCBrokerV.remoteprocedure := 'TMG CHANNEL'; RPCBrokerV.param[0].ptype := list; RPCBrokerV.Param[0].Mult['"REQUEST"'] := 'REGISTER PATIENT^'+oneRow; RPCBrokerV.Call; RPCResult := RPCBrokerV.Results[0]; //returns: error: -1^Message; success=1^Success^IEN; or Equivical=0^Message^IEN //If 0 then Message is in this format // [Bool1;Bool2;Bool3;Bool4;Bool5*MessageText] // For each Bool, 0=false, 1=true // Bool1 : Patient previously registered // Bool2 : Patient registered this time (using identifier fields) // Bool3 : Problem filing non-identifier fields // Bool4 : Problem filing data info sub-file fields // Bool5 : Problem filing HRN tempResult := piece(RPCResult,'^',1); if tempResult='1' then begin //1=Success result := piece(RPCResult,'^',3)+'^0^1^0'; end else if tempResult='0' then begin //0=Mixed results Msg := piece(RPCResult,'^',2); result := piece(RPCResult,'^',3); if result = '' then result := NOT_ADDED; result := result + '^' + Piece(Msg,';',1) + '^' + Piece(Msg,';',2); if Pos('1',Pieces(Msg,';',3,5))>0 then begin result := result + '^1'; if RPCBrokerV.Results.Count > 1 then begin Log.Add('-----------------------------------------------'); Log.Add('There was a problem with registering a patient.'); for i := 1 to RPCBrokerV.Results.Count-1 do begin Log.Add(RPCBrokerV.Results.Strings[i]); end; end; end else begin result := result + '^0'; end; end else begin //should be tempResult='-1' //-1=Error FMErrorForm.Memo.Lines.Assign(RPCBrokerV.Results); FMErrorForm.PrepMessage; //Does some clean up of the message format //FMErrorForm.ShowModal; //later just put these in log... Log.Add('-----------------------------------------------'); Log.Add('There was a problem with registering a patient.'); for i:= 0 to FMErrorForm.Memo.Lines.Count-1 do begin Log.Add(FMErrorForm.Memo.Lines.Strings[i]); end; Log.Add(' '); FMErrorForm.Memo.Lines.Clear; result := NOT_ADDED+'^0^0^1'; //Not-prev-reg^Not-reg-this-time^Problem-occured end; end; { procedure TBatchAddForm.DelGridRow(BatchGrid : TStringGrid; row : integer); var col : integer; begin if row >= BatchGrid.RowCount then exit; repeat if row = BatchGrid.RowCount-1 then begin if BatchGrid.RowCount=3 then begin for col := 0 to BatchGrid.ColCount-1 do begin BatchGrid.Cells[col,row] := ''; end; end else begin BatchGrid.RowCount := BatchGrid.RowCount-1; end; exit; end; for col := 0 to BatchGrid.ColCount-1 do begin BatchGrid.Cells[col,row] := BatchGrid.Cells[col,row+1] end; inc(row); until (1=0); end; } procedure TBatchAddForm.FormDestroy(Sender: TObject); begin FieldNumList.Free; end; procedure TBatchAddForm.GetColFieldNums(List : TStringList); var i : integer; begin List.Clear; List.Add(''); //fill 0'th column will null for i := 1 to BatchGrid.ColCount-1 do begin List.Add(BatchGrid.Cells[i,1]); end; end; function TBatchAddForm.GetOneRow(row : integer; ColFields : TStringList) : string; //Output format: FldNum1^Value1^fldNum2^Value2^FldNum3^Value3... var i : integer; begin result := ''; if row >= BatchGrid.RowCount then exit; for i := 1 to BatchGrid.ColCount-1 do begin result := result + ColFields.Strings[i]+'^'+BatchGrid.Cells[i,row]+'^'; end; end; function TBatchAddForm.RowToStr(row : integer) : string; //Output format: Cell0^Cell1^Cell2^Cell3^Cell4.... var i : integer; begin result := ''; if row >= BatchGrid.RowCount then exit; for i := 0 to BatchGrid.ColCount-1 do begin result := result + BatchGrid.Cells[i,row]+'^'; end; end; procedure TBatchAddForm.AddRowFromStr(Str : string); var row : integer; i : integer; begin BatchGrid.RowCount := BatchGrid.RowCount + 1; row := BatchGrid.RowCount-1; for i := 0 to BatchGrid.ColCount-1 do begin BatchGrid.Cells[i,row] := Piece(Str,'^',i+1); end; end; procedure TBatchAddForm.btnAbortRegistrationClick(Sender: TObject); begin FAbortRegistration := true; end; procedure TBatchAddForm.btnSaveGridClick(Sender: TObject); var DataLines : TStringList; Value : string; row,col : integer; Line : string; begin If SaveDialog.Execute then begin DataLines := TStringList.Create; for row := 0 to BatchGrid.RowCount-1 do begin Line := ''; for col := 0 to BatchGrid.ColCount-1 do begin Value := BatchGrid.Cells[col,row]; if Value = ADD_ROW then Value := ' '; if (row=1)or((BatchGrid.Cells[col,1]='.09')and(row<>0)) then begin Value := '<'+Value+'>'; //protect field numbers as text end; Line := Line + '"'+Value+'"' + #9 end; DataLines.Add(Line); end; DataLines.Add(' '); DataLines.Add('Add as many rows as needed)'); DataLines.Add(' '); DataLines.Add('When done, save file and import with'); DataLines.Add(' the WorldVista Config Utility.'); DataLines.Add('Save in CSV format, using TAB as field'); DataLines.Add(' delimiter, and " as text delimiter.'); DataLines.SaveToFile(SaveDialog.FileName); DataLines.Free; end; end; procedure TBatchAddForm.BatchGridSetEditText(Sender: TObject; ACol, ARow: Integer; const Value: String); begin btnSaveGrid.Enabled := true; end; end.