unit Api.Database;

interface

uses
  System.SysUtils, System.Classes, Data.DB, MemDS, DBAccess, Uni, UniProvider,
  PostgreSQLUniProvider, System.Variants, System.Generics.Collections, System.IniFiles,
  Common.Logging, Vcl.Forms, System.Character, Common.Ini, DAAlerter,
  UniAlerter;

type
  TApiDatabaseModule = class(TDataModule)
    uqMapUnits: TUniQuery;
    uqUnitList: TUniQuery;
    uqComplaintUnits: TUniQuery;
    uqCFSMemos: TUniQuery;
    uqComplaintList: TUniQuery;
    uqComplaintDetails: TUniQuery;
    ucENTCAD: TUniConnection;
    uqMapComplaints: TUniQuery;
    uqBadgeCounts: TUniQuery;
    uqMapComplaintUnitsList: TUniQuery;
    uqComplaintHistory: TUniQuery;
    uqComplaintContacts: TUniQuery;
    uqComplaintWarnings: TUniQuery;
    uqUnitDetails: TUniQuery;
    uqUnitLogs: TUniQuery;
    PostgreSQLUniProvider1: TPostgreSQLUniProvider;
    uqComplaintListcomplaintid: TFloatField;
    uqComplaintListcfsid: TFloatField;
    uqComplaintListcomplaint: TStringField;
    uqComplaintListagency: TStringField;
    uqComplaintListdispatch_code_desc: TStringField;
    uqComplaintListsource: TStringField;
    uqComplaintListsource_desc: TStringField;
    uqComplaintListpriority: TStringField;
    uqComplaintListaddressid: TFloatField;
    uqComplaintListaddress: TStringField;
    uqComplaintListapartment: TStringField;
    uqComplaintListcity: TStringField;
    uqComplaintListbusiness: TStringField;
    uqComplaintListdispatchdistrict: TStringField;
    uqComplaintListdispatchsector: TStringField;
    uqComplaintListaddressdistrict: TStringField;
    uqComplaintListaddresssector: TStringField;
    uqComplaintListxcoord: TFloatField;
    uqComplaintListycoord: TFloatField;
    uqComplaintListwarnings: TFloatField;
    uqComplaintListcontacts: TFloatField;
    uqComplaintListhistory: TFloatField;
    uqComplaintListdatereported: TDateTimeField;
    uqComplaintListdatereceived: TDateTimeField;
    uqComplaintListdatedispatched: TDateTimeField;
    uqComplaintListdateresponded: TDateTimeField;
    uqComplaintListdatearrived: TDateTimeField;
    uqComplaintListdatecleared: TDateTimeField;
    uqComplaintListpriority_color: TFloatField;
    uqComplaintListdistrict_desc: TStringField;
    uqComplaintListsector_desc: TStringField;
    uqComplaintListcomplaintNumber: TStringField;
    uqComplaintDetailscomplaintid: TFloatField;
    uqComplaintDetailscfsid: TFloatField;
    uqComplaintDetailscomplaint: TStringField;
    uqComplaintDetailspriority: TStringField;
    uqComplaintDetailsdispatchcode: TStringField;
    uqComplaintDetailsdispatch_code_desc: TStringField;
    uqComplaintDetailsdispatchdistrict: TStringField;
    uqComplaintDetailsaddress: TStringField;
    uqComplaintDetailsbusiness: TStringField;
    uqComplaintDetailsdatereported: TDateTimeField;
    uqComplaintDetailsdatereceived: TDateTimeField;
    uqComplaintDetailsdatedispatched: TDateTimeField;
    uqComplaintDetailsdateresponded: TDateTimeField;
    uqComplaintDetailsdatearrived: TDateTimeField;
    uqComplaintDetailsdatecleared: TDateTimeField;
    uqComplaintDetailshistory: TFloatField;
    uqComplaintDetailscontacts: TFloatField;
    uqComplaintDetailswarnings: TFloatField;
    uqComplaintDetailsaddressid: TFloatField;
    uqComplaintDetailsagency: TStringField;
    uqComplaintDetailsstrnumber: TFloatField;
    uqComplaintDetailsstrhnumber: TStringField;
    uqComplaintDetailsstrprefix: TStringField;
    uqComplaintDetailsstrname: TStringField;
    uqComplaintDetailsstrsuffix: TStringField;
    uqComplaintDetailscity: TStringField;
    uqComplaintDetailsstatus: TIntegerField;
    uqCFSMemosmemo_id: TFloatField;
    uqCFSMemoscfsid: TFloatField;
    uqCFSMemosmemo_type: TFloatField;
    uqCFSMemostimestamp: TDateTimeField;
    uqCFSMemosbadge_number: TStringField;
    uqCFSMemosremarks: TStringField;
    uqUnitDetailsunitid: TFloatField;
    uqUnitDetailsunitname: TStringField;
    uqUnitDetailscarnumber_desc: TStringField;
    uqUnitDetailsdistrict_desc: TStringField;
    uqUnitDetailslocation: TStringField;
    uqUnitDetailsunit_status_desc: TStringField;
    uqUnitDetailsofficer1_lname: TStringField;
    uqUnitDetailsofficer1_fname: TStringField;
    uqUnitDetailsofficer1_empnum: TStringField;
    uqUnitDetailsofficer2_lname: TStringField;
    uqUnitDetailsofficer2_fname: TStringField;
    uqUnitDetailsofficer2_empnum: TStringField;
    uqUnitDetailsupdate_time: TDateTimeField;
    uqUnitLogslog_time: TDateTimeField;
    uqUnitLogscomplaint_num: TStringField;
    uqUnitLogslog_text: TStringField;
    uqComplaintHistorycomplaintid: TFloatField;
    uqComplaintHistorycomplaint: TStringField;
    uqComplaintHistoryapartment: TStringField;
    uqComplaintHistorydatereported: TDateTimeField;
    uqComplaintHistorydpriority: TStringField;
    uqComplaintHistorydcalltype: TStringField;
    uqComplaintContactsname: TStringField;
    uqComplaintContactsphone: TStringField;
    uqComplaintContactsdcontacttype: TStringField;
    uqComplaintContactsremarks: TStringField;
    uqComplaintWarningscode_desc: TStringField;
    uqComplaintWarningsstrnumber: TFloatField;
    uqComplaintWarningsstrhnumber: TStringField;
    uqComplaintWarningsstrprefix: TStringField;
    uqComplaintWarningsstrname: TStringField;
    uqComplaintWarningsstrsuffix: TStringField;
    uqComplaintWarningsapartment: TStringField;
    uqComplaintWarningscity: TStringField;
    uqComplaintWarningsnotes: TStringField;
    uqMapComplaintUnitsListcomplaintid: TFloatField;
    uqMapComplaintUnitsListunitid: TFloatField;
    uqMapComplaintUnitsListunitname: TStringField;
    uqMapComplaintUnitsListdatedispatched: TDateTimeField;
    uqMapComplaintUnitsListdateresponded: TDateTimeField;
    uqMapComplaintUnitsListdatearrived: TDateTimeField;
    uqMapComplaintUnitsListdatecleared: TDateTimeField;
    uqMapComplaintUnitsListlocation: TStringField;
    uqMapComplaintscomplaintid: TFloatField;
    uqMapComplaintsdispatchdistrict: TStringField;
    uqMapComplaintspriority: TStringField;
    uqMapComplaintsbusiness: TStringField;
    uqMapComplaintsdispatchcodecategory: TStringField;
    uqMapComplaintslng: TFloatField;
    uqMapComplaintslat: TFloatField;
    uqMapComplaintsdispatch_code_desc: TStringField;
    uqMapComplaintsaddress: TStringField;
    uqComplaintUnitscomplaintid: TFloatField;
    uqComplaintUnitsunitid: TFloatField;
    uqComplaintUnitsunitname: TStringField;
    uqComplaintUnitsdatedispatched: TDateTimeField;
    uqComplaintUnitsdateresponded: TDateTimeField;
    uqComplaintUnitsdatearrived: TDateTimeField;
    uqComplaintUnitsdatecleared: TDateTimeField;
    uqComplaintUnitslocation: TStringField;
    uqUnitListagency: TStringField;
    uqUnitListagency_name: TStringField;
    uqUnitListunitid: TFloatField;
    uqUnitListunitname: TStringField;
    uqUnitListcarnumber_desc: TStringField;
    uqUnitListdistrict_desc: TStringField;
    uqUnitListsector_desc: TStringField;
    uqUnitListofficer1_empnum: TStringField;
    uqUnitListofficer1_last_name: TStringField;
    uqUnitListofficer1_first_name: TStringField;
    uqUnitListofficer1_mi: TStringField;
    uqUnitListofficer2_empnum: TStringField;
    uqUnitListofficer2_last_name: TStringField;
    uqUnitListofficer2_first_name: TStringField;
    uqUnitListofficer2_mi: TStringField;
    uqUnitListlocation: TStringField;
    uqUnitListcomplaint: TStringField;
    uqUnitListunitstatus: TFloatField;
    uqUnitListunit_status_desc: TStringField;
    uqUnitListcall_type: TStringField;
    uqUnitListentryid: TFloatField;
    uqUnitListgps_latitude: TFloatField;
    uqUnitListgps_longitude: TFloatField;
    uqBadgeCountscomplaints: TLargeintField;
    uqBadgeCountsunits: TLargeintField;
    uqMapUnitsentryid: TFloatField;
    uqMapUnitsunitid: TFloatField;
    uqMapUnitsunitname: TMemoField;
    uqMapUnitsagency: TStringField;
    uqMapUnitsagency_name: TStringField;
    uqMapUnitsgps_latitude: TFloatField;
    uqMapUnitsgps_longitude: TFloatField;
    uqMapUnitscall_type: TStringField;
    uqMapUnitspriority: TStringField;
    uqMapUnitsunit_status_desc: TStringField;
    uqMapUnitsupdate_time: TDateTimeField;
    uqMapUnitsdis_unitid: TFloatField;
    uqMapUnitsofficer1_lname: TStringField;
    uqMapUnitsofficer1_fname: TStringField;
    uqMapUnitsofficer1_empnum: TStringField;
    uqMapUnitsofficer2_lname: TStringField;
    uqMapUnitsofficer2_fname: TStringField;
    uqMapUnitsofficer2_empnum: TStringField;
    uqMapComplaintsagency: TStringField;
    uqMapComplaintsagency_name: TStringField;
    uqMapComplaintsdatedispatched: TDateTimeField;
    uqMapComplaintsdatearrived: TDateTimeField;
    uqMapComplaintspriorityBadge: TStringField;
    uqMapComplaintscomplaintStatusKey: TStringField;
    uqMapComplaintspngName: TStringField;
    uqMapUnitsagencytype: TStringField;
    uqUnitListagencytype: TStringField;
    uqMapComplaintscomplaint: TStringField;
    UniAlerter1: TUniAlerter;
    procedure uqComplaintListCalcFields(DataSet: TDataSet);
    procedure uqMapComplaintsCalcFields(DataSet: TDataSet);
    procedure DataModuleCreate(Sender: TObject);
    procedure UniAlerter1Event(Sender: TDAAlerter; const EventName,
      Message: string);
  private
    { Private declarations }
  public
    CADUpdate: Boolean;
    function HandleUniqueFilenames(const category: string): string;
    function BadgeCounts(const BaseQuery: TUniQuery): Integer;
  end;

var
  ApiDatabaseModule: TApiDatabaseModule;

implementation

{%CLASSGROUP 'Vcl.Controls.TControl'}

{$R *.dfm}


procedure TApiDatabaseModule.DataModuleCreate(Sender: TObject);
begin
  CADUpdate := False;

  ucENTCAD.ProviderName := 'PostgreSQL';
  ucENTCAD.Server := IniEntries.DatabaseServer;
  ucENTCAD.Port := IniEntries.DatabasePort;
  ucENTCAD.Database := IniEntries.DatabaseName;
  ucENTCAD.Username := IniEntries.DatabaseUsername;
  ucENTCAD.Password := IniEntries.DatabasePassword;
  ucENTCAD.LoginPrompt := False;

  if not ucENTCAD.Connected then
    ucENTCAD.Connect;

  ucENTCAD.ExecSQL('set search_path to lems, avl, entcad, public');
  Logger.Log(2, 'PostgreSQL API search_path set to lems, avl, entcad, public');

  Logger.Log(1, 'Starting PostgreSQL disupdate listener');
  UniAlerter1.Start;
  Logger.Log(1, 'PostgreSQL disupdate listener started');
end;

procedure TApiDatabaseModule.uqComplaintListCalcFields(DataSet: TDataSet);
var
  raw: string;
begin
  raw := uqComplaintListCOMPLAINT.AsString.Trim;

  if raw.Length >= 3 then
    uqComplaintListcomplaintNumber.AsString := Copy(raw, 1, 2) + '-' + Copy(raw, 3, MaxInt)
  else if raw.Length = 2 then
    uqComplaintListcomplaintNumber.AsString := raw + '-'
  else
    uqComplaintListcomplaintNumber.AsString := raw;
end;


procedure TApiDatabaseModule.uqMapComplaintsCalcFields(DataSet: TDataSet);
var
  rawCategory: string;
  rawPriority: string;
  statusKey: string;
  priorityBadge: string;
begin
  rawCategory := DataSet.FieldByName('DISPATCHCODECATEGORY').AsString;
  rawPriority := Trim(DataSet.FieldByName('PRIORITY').AsString);

  if not DataSet.FieldByName('DATEARRIVED').IsNull then
    statusKey := 'onscene'
  else if not DataSet.FieldByName('DATEDISPATCHED').IsNull then
    statusKey := 'attached'
  else
    statusKey := 'notattached';

  if rawPriority = '' then
    priorityBadge := '?'
  else
    priorityBadge := UpperCase(Copy(rawPriority, 1, 1));

  DataSet.FieldByName('complaintStatusKey').AsString := statusKey;
  DataSet.FieldByName('priorityBadge').AsString := priorityBadge;

  if Trim(rawCategory) = '' then
    DataSet.FieldByName('pngName').AsString := Format('default_%s.png', [statusKey])
  else
    DataSet.FieldByName('pngName').AsString :=
      Format('%s_%s.png', [HandleUniqueFilenames(rawCategory), statusKey]);
end;

function TApiDatabaseModule.HandleUniqueFilenames(const category: string): string;
var
  i: Integer;
  ch: Char;
  lowered, resultBuilder: string;
begin
  lowered := Trim(LowerCase(category));
  resultBuilder := '';
  for i := 1 to Length(lowered) do
  begin
    ch := lowered[i];
    if ch.IsLetterOrDigit then
      resultBuilder := resultBuilder + ch
    else
      resultBuilder := resultBuilder + '_';
  end;
  Result := resultBuilder;
end;


procedure TApiDatabaseModule.UniAlerter1Event(Sender: TDAAlerter; const EventName, Message: string);
begin
  if SameText(EventName, 'disupdate') then
    CADUpdate := True;
end;

function TApiDatabaseModule.BadgeCounts(const BaseQuery: TUniQuery): Integer;
var
  Q: TUniQuery;
  WrappedSQL: string;
begin
  Q := TUniQuery.Create(nil);
  try
    Q.Connection := BaseQuery.Connection;

    // Wrap the original query text in a COUNT(*) shell
    WrappedSQL := 'select count(*) as CNT from (' + BaseQuery.SQL.Text + ') t';
    Q.SQL.Text := WrappedSQL;

    // Carry over params (if the base query uses any)
    Q.Params.AssignValues(BaseQuery.Params);

    Q.Open;
    Result := Q.Fields[0].AsInteger;
  finally
    Q.Free;
  end;
end;

end.
