Commit d011eb7b by Mac Stephens

Connected to entcad database, uniqueries created with all fields, initial…

Connected to entcad database, uniqueries created with all fields, initial complaint list endpoint created, cleaned up more envoy calls issues
parent 6fa13c63
object ApiDatabaseModule: TApiDatabaseModule
OnCreate = DataModuleCreate
Height = 480
Height = 491
Width = 640
object ucEnvoy: TUniConnection
ProviderName = 'PostgreSQL'
......@@ -21,27 +21,16 @@ object ApiDatabaseModule: TApiDatabaseModule
Left = 363
Top = 138
end
object ucBooking: TUniConnection
ProviderName = 'Oracle'
Database = 'EMBOOKING'
Username = 'emBooking'
Server = 'EMBOOK-CPS'
LoginPrompt = False
Left = 77
Top = 231
EncryptedPassword = '9AFF92FF9DFF90FF90FF94FFCFFFCEFF'
end
object OracleUniProvider1: TOracleUniProvider
Left = 226
Top = 236
end
object uqBooking: TUniQuery
Connection = ucBooking
Left = 374
Top = 222
end
object uqUnitsCurrent: TUniQuery
Connection = ucBooking
Connection = ucENTCAD
SQL.Strings = (
'SELECT'
' uc.ENTRYID,'
......@@ -51,11 +40,32 @@ object ApiDatabaseModule: TApiDatabaseModule
' uc.GPS_LATITUDE,'
' uc.GPS_LONGITUDE'
'FROM UNITS_CURRENT@AVL_LINK uc')
ReadOnly = True
Left = 462
Top = 324
object uqUnitsCurrentENTRYID: TFloatField
FieldName = 'ENTRYID'
end
object uqUnitsCurrentUNITID: TFloatField
FieldName = 'UNITID'
end
object uqUnitsCurrentUNITNAME: TStringField
FieldName = 'UNITNAME'
ReadOnly = True
end
object uqUnitsCurrentUNIT_DISTRICT: TStringField
FieldName = 'UNIT_DISTRICT'
Size = 8
end
object uqUnitsCurrentGPS_LATITUDE: TFloatField
FieldName = 'GPS_LATITUDE'
end
object uqUnitsCurrentGPS_LONGITUDE: TFloatField
FieldName = 'GPS_LONGITUDE'
end
end
object uqDISUnitsActive: TUniQuery
Connection = ucBooking
Connection = ucENTCAD
SQL.Strings = (
'SELECT'
' dua.UNITID,'
......@@ -103,11 +113,104 @@ object ApiDatabaseModule: TApiDatabaseModule
'LEFT JOIN UNITS_CURRENT@AVL_LINK uc ON dua.UNITID = uc.UNI' +
'TID')
ReadOnly = True
Left = 462
Top = 374
object uqDISUnitsActiveUNITID: TFloatField
FieldName = 'UNITID'
end
object uqDISUnitsActiveUNITNAME: TStringField
FieldName = 'UNITNAME'
Size = 10
end
object uqDISUnitsActiveCARNUMBER_DESC: TStringField
FieldName = 'CARNUMBER_DESC'
ReadOnly = True
Size = 120
end
object uqDISUnitsActiveDISTRICT_DESC: TStringField
FieldName = 'DISTRICT_DESC'
ReadOnly = True
Size = 120
end
object uqDISUnitsActiveSECTOR_DESC: TStringField
FieldName = 'SECTOR_DESC'
ReadOnly = True
Size = 120
end
object uqDISUnitsActiveOFFICER1_EMPNUM: TStringField
FieldName = 'OFFICER1_EMPNUM'
ReadOnly = True
Size = 10
end
object uqDISUnitsActiveOFFICER1_LAST_NAME: TStringField
FieldName = 'OFFICER1_LAST_NAME'
ReadOnly = True
Size = 45
end
object uqDISUnitsActiveOFFICER1_FIRST_NAME: TStringField
FieldName = 'OFFICER1_FIRST_NAME'
ReadOnly = True
Size = 30
end
object uqDISUnitsActiveOFFICER1_MI: TStringField
FieldName = 'OFFICER1_MI'
ReadOnly = True
Size = 1
end
object uqDISUnitsActiveOFFICER2_EMPNUM: TStringField
FieldName = 'OFFICER2_EMPNUM'
ReadOnly = True
Size = 10
end
object uqDISUnitsActiveOFFICER2_LAST_NAME: TStringField
FieldName = 'OFFICER2_LAST_NAME'
ReadOnly = True
Size = 45
end
object uqDISUnitsActiveOFFICER2_FIRST_NAME: TStringField
FieldName = 'OFFICER2_FIRST_NAME'
ReadOnly = True
Size = 30
end
object uqDISUnitsActiveOFFICER2_MI: TStringField
FieldName = 'OFFICER2_MI'
ReadOnly = True
Size = 1
end
object uqDISUnitsActiveLOCATION: TStringField
FieldName = 'LOCATION'
ReadOnly = True
Size = 30
end
object uqDISUnitsActiveCOMPLAINT: TStringField
FieldName = 'COMPLAINT'
ReadOnly = True
Size = 10
end
object uqDISUnitsActiveUNITSTATUS: TFloatField
FieldName = 'UNITSTATUS'
ReadOnly = True
end
object uqDISUnitsActiveUNIT_STATUS_DESC: TStringField
FieldName = 'UNIT_STATUS_DESC'
ReadOnly = True
end
object uqDISUnitsActiveENTRYID: TFloatField
FieldName = 'ENTRYID'
ReadOnly = True
end
object uqDISUnitsActiveGPS_LATITUDE: TFloatField
FieldName = 'GPS_LATITUDE'
ReadOnly = True
end
object uqDISUnitsActiveGPS_LONGITUDE: TFloatField
FieldName = 'GPS_LONGITUDE'
ReadOnly = True
end
end
object uqCFSActive: TUniQuery
Connection = ucBooking
Connection = ucENTCAD
SQL.Strings = (
'SELECT'
' ca.COMPLAINTID,'
......@@ -121,17 +224,44 @@ object ApiDatabaseModule: TApiDatabaseModule
'FROM CFS_ACTIVE ca'
'WHERE ca.COMPLAINTID = :COMPLAINTID'
'ORDER BY ca.DATEDISPATCHED')
ReadOnly = True
Left = 278
Top = 318
ParamData = <
item
DataType = ftUnknown
Name = 'COMPLAINTID'
Value = nil
Value = Null
end>
object uqCFSActiveCOMPLAINTID: TFloatField
FieldName = 'COMPLAINTID'
end
object uqCFSActiveUNITID: TFloatField
FieldName = 'UNITID'
end
object uqCFSActiveUNITNAME: TStringField
FieldName = 'UNITNAME'
Size = 10
end
object uqCFSActiveDATEDISPATCHED: TDateTimeField
FieldName = 'DATEDISPATCHED'
end
object uqCFSActiveDATERESPONDED: TDateTimeField
FieldName = 'DATERESPONDED'
end
object uqCFSActiveDATEARRIVED: TDateTimeField
FieldName = 'DATEARRIVED'
end
object uqCFSActiveDATECLEARED: TDateTimeField
FieldName = 'DATECLEARED'
end
object uqCFSActiveLOCATION: TStringField
FieldName = 'LOCATION'
Size = 30
end
end
object uqCFSMemos: TUniQuery
Connection = ucBooking
Connection = ucENTCAD
SQL.Strings = (
'SELECT'
' cm.MEMO_ID,'
......@@ -143,19 +273,40 @@ object ApiDatabaseModule: TApiDatabaseModule
'FROM CFS_MEMOS cm'
'WHERE cm.CFSID = :CFSID'
'ORDER BY cm.TIMESTAMP ASC')
ReadOnly = True
Left = 282
Top = 376
ParamData = <
item
DataType = ftUnknown
Name = 'CFSID'
Value = nil
Value = Null
end>
object uqCFSMemosMEMO_ID: TFloatField
FieldName = 'MEMO_ID'
Required = True
end
object uqCFSMemosCFSID: TFloatField
FieldName = 'CFSID'
end
object uqCFSMemosMEMO_TYPE: TFloatField
FieldName = 'MEMO_TYPE'
end
object uqCFSMemosTIMESTAMP: TDateTimeField
FieldName = 'TIMESTAMP'
end
object uqCFSMemosBADGE_NUMBER: TStringField
FieldName = 'BADGE_NUMBER'
Size = 6
end
object uqCFSMemosREMARKS: TStringField
FieldName = 'REMARKS'
Size = 2048
end
end
object uqComplaintList: TUniQuery
Connection = ucBooking
Connection = ucENTCAD
SQL.Strings = (
'-- uqComplaintActive_List'
'SELECT'
' ca.COMPLAINTID,'
' ca.CFSID,'
......@@ -190,12 +341,120 @@ object ApiDatabaseModule: TApiDatabaseModule
'LEFT JOIN CD_DISPATCHCODES cdc ON ca.DISPATCHCODE = cdc.CODE'
'LEFT JOIN CD_CALLSOURCES ccs ON ca.SOURCE = ccs.CODE'
'WHERE ca.COMPLAINT IS NOT NULL'
'ORDER BY ct.DATEREPORTED DESC, ca.PRIORITY DESC')
'ORDER BY ct.DATEREPORTED DESC, ca.PRIORITY DESC'
'FETCH FIRST 10 ROWS ONLY')
ReadOnly = True
Left = 76
Top = 320
Top = 322
object uqComplaintListCOMPLAINTID: TFloatField
FieldName = 'COMPLAINTID'
Required = True
end
object uqComplaintListCFSID: TFloatField
FieldName = 'CFSID'
end
object uqComplaintListCOMPLAINT: TStringField
FieldName = 'COMPLAINT'
Size = 10
end
object uqComplaintListAGENCY: TStringField
FieldName = 'AGENCY'
Size = 6
end
object uqComplaintListDISPATCH_CODE_DESC: TStringField
FieldName = 'DISPATCH_CODE_DESC'
ReadOnly = True
Size = 60
end
object uqComplaintListSOURCE: TStringField
FieldName = 'SOURCE'
Size = 6
end
object uqComplaintListSOURCE_DESC: TStringField
FieldName = 'SOURCE_DESC'
ReadOnly = True
Size = 120
end
object uqComplaintListPRIORITY: TStringField
FieldName = 'PRIORITY'
Size = 6
end
object uqComplaintListADDRESSID: TFloatField
FieldName = 'ADDRESSID'
end
object uqComplaintListADDRESS: TStringField
FieldName = 'ADDRESS'
Size = 64
end
object uqComplaintListAPARTMENT: TStringField
FieldName = 'APARTMENT'
Size = 6
end
object uqComplaintListCITY: TStringField
FieldName = 'CITY'
end
object uqComplaintListBUSINESS: TStringField
FieldName = 'BUSINESS'
Size = 35
end
object uqComplaintListDISPATCHDISTRICT: TStringField
FieldName = 'DISPATCHDISTRICT'
Size = 6
end
object uqComplaintListDISPATCHSECTOR: TStringField
FieldName = 'DISPATCHSECTOR'
Size = 6
end
object uqComplaintListADDRESSDISTRICT: TStringField
FieldName = 'ADDRESSDISTRICT'
Size = 5
end
object uqComplaintListADDRESSSECTOR: TStringField
FieldName = 'ADDRESSSECTOR'
Size = 5
end
object uqComplaintListXCOORD: TFloatField
FieldName = 'XCOORD'
end
object uqComplaintListYCOORD: TFloatField
FieldName = 'YCOORD'
end
object uqComplaintListWARNINGS: TFloatField
FieldName = 'WARNINGS'
end
object uqComplaintListCONTACTS: TFloatField
FieldName = 'CONTACTS'
end
object uqComplaintListHISTORY: TFloatField
FieldName = 'HISTORY'
end
object uqComplaintListDATEREPORTED: TDateTimeField
FieldName = 'DATEREPORTED'
ReadOnly = True
end
object uqComplaintListDATERECEIVED: TDateTimeField
FieldName = 'DATERECEIVED'
ReadOnly = True
end
object uqComplaintListDATEDISPATCHED: TDateTimeField
FieldName = 'DATEDISPATCHED'
ReadOnly = True
end
object uqComplaintListDATERESPONDED: TDateTimeField
FieldName = 'DATERESPONDED'
ReadOnly = True
end
object uqComplaintListDATEARRIVED: TDateTimeField
FieldName = 'DATEARRIVED'
ReadOnly = True
end
object uqComplaintListDATECLEARED: TDateTimeField
FieldName = 'DATECLEARED'
ReadOnly = True
end
end
object uqComplaintDetails: TUniQuery
Connection = ucBooking
Connection = ucENTCAD
SQL.Strings = (
'-- uqComplaintActive_Detail'
'SELECT'
......@@ -233,13 +492,134 @@ object ApiDatabaseModule: TApiDatabaseModule
'LEFT JOIN CD_DISPATCHCODES cdc ON ca.DISPATCHCODE = cdc.CODE'
'LEFT JOIN CD_CALLSOURCES ccs ON ca.SOURCE = ccs.CODE'
'WHERE ca.COMPLAINTID = :COMPLAINTID')
ReadOnly = True
Left = 74
Top = 376
ParamData = <
item
DataType = ftUnknown
Name = 'COMPLAINTID'
Value = nil
Value = Null
end>
object uqComplaintDetailsCOMPLAINTID: TFloatField
FieldName = 'COMPLAINTID'
Required = True
end
object uqComplaintDetailsCFSID: TFloatField
FieldName = 'CFSID'
end
object uqComplaintDetailsCOMPLAINT: TStringField
FieldName = 'COMPLAINT'
Size = 10
end
object uqComplaintDetailsAGENCY: TStringField
FieldName = 'AGENCY'
Size = 6
end
object uqComplaintDetailsDISPATCHCODE: TStringField
FieldName = 'DISPATCHCODE'
Size = 6
end
object uqComplaintDetailsDISPATCH_CODE_DESC: TStringField
FieldName = 'DISPATCH_CODE_DESC'
ReadOnly = True
Size = 60
end
object uqComplaintDetailsSOURCE: TStringField
FieldName = 'SOURCE'
Size = 6
end
object uqComplaintDetailsSOURCE_DESC: TStringField
FieldName = 'SOURCE_DESC'
ReadOnly = True
Size = 120
end
object uqComplaintDetailsPRIORITY: TStringField
FieldName = 'PRIORITY'
Size = 6
end
object uqComplaintDetailsADDRESSID: TFloatField
FieldName = 'ADDRESSID'
end
object uqComplaintDetailsADDRESS: TStringField
FieldName = 'ADDRESS'
Size = 64
end
object uqComplaintDetailsAPARTMENT: TStringField
FieldName = 'APARTMENT'
Size = 6
end
object uqComplaintDetailsCITY: TStringField
FieldName = 'CITY'
end
object uqComplaintDetailsBUSINESS: TStringField
FieldName = 'BUSINESS'
Size = 35
end
object uqComplaintDetailsDISPATCHDISTRICT: TStringField
FieldName = 'DISPATCHDISTRICT'
Size = 6
end
object uqComplaintDetailsDISPATCHSECTOR: TStringField
FieldName = 'DISPATCHSECTOR'
Size = 6
end
object uqComplaintDetailsADDRESSDISTRICT: TStringField
FieldName = 'ADDRESSDISTRICT'
Size = 5
end
object uqComplaintDetailsADDRESSSECTOR: TStringField
FieldName = 'ADDRESSSECTOR'
Size = 5
end
object uqComplaintDetailsXCOORD: TFloatField
FieldName = 'XCOORD'
end
object uqComplaintDetailsYCOORD: TFloatField
FieldName = 'YCOORD'
end
object uqComplaintDetailsWARNINGS: TFloatField
FieldName = 'WARNINGS'
end
object uqComplaintDetailsCONTACTS: TFloatField
FieldName = 'CONTACTS'
end
object uqComplaintDetailsHISTORY: TFloatField
FieldName = 'HISTORY'
end
object uqComplaintDetailsDATEREPORTED: TDateTimeField
FieldName = 'DATEREPORTED'
ReadOnly = True
end
object uqComplaintDetailsDATERECEIVED: TDateTimeField
FieldName = 'DATERECEIVED'
ReadOnly = True
end
object uqComplaintDetailsDATEDISPATCHED: TDateTimeField
FieldName = 'DATEDISPATCHED'
ReadOnly = True
end
object uqComplaintDetailsDATERESPONDED: TDateTimeField
FieldName = 'DATERESPONDED'
ReadOnly = True
end
object uqComplaintDetailsDATEARRIVED: TDateTimeField
FieldName = 'DATEARRIVED'
ReadOnly = True
end
object uqComplaintDetailsDATECLEARED: TDateTimeField
FieldName = 'DATECLEARED'
ReadOnly = True
end
end
object ucENTCAD: TUniConnection
ProviderName = 'Oracle'
Port = 1521
Username = 'ENTCAD'
Server = 'BUFENTCAD'
LoginPrompt = False
Left = 76
Top = 244
EncryptedPassword = 'BAFFB1FFABFFBCFFBEFFBBFF'
end
end
// Where the database is kept. Only used by Lookup.ServiceImpl to retrieve info
// from the data base and send it to the client.
// Author: ???
unit Api.Database;
interface
......@@ -15,7 +12,6 @@ type
ucEnvoy: TUniConnection;
PostgreSQLUniProvider1: TPostgreSQLUniProvider;
UniQuery1: TUniQuery;
ucBooking: TUniConnection;
OracleUniProvider1: TOracleUniProvider;
uqBooking: TUniQuery;
uqUnitsCurrent: TUniQuery;
......@@ -24,6 +20,104 @@ type
uqCFSMemos: TUniQuery;
uqComplaintList: TUniQuery;
uqComplaintDetails: TUniQuery;
ucENTCAD: TUniConnection;
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;
uqComplaintDetailsCOMPLAINTID: TFloatField;
uqComplaintDetailsCFSID: TFloatField;
uqComplaintDetailsCOMPLAINT: TStringField;
uqComplaintDetailsAGENCY: TStringField;
uqComplaintDetailsDISPATCHCODE: TStringField;
uqComplaintDetailsDISPATCH_CODE_DESC: TStringField;
uqComplaintDetailsSOURCE: TStringField;
uqComplaintDetailsSOURCE_DESC: TStringField;
uqComplaintDetailsPRIORITY: TStringField;
uqComplaintDetailsADDRESSID: TFloatField;
uqComplaintDetailsADDRESS: TStringField;
uqComplaintDetailsAPARTMENT: TStringField;
uqComplaintDetailsCITY: TStringField;
uqComplaintDetailsBUSINESS: TStringField;
uqComplaintDetailsDISPATCHDISTRICT: TStringField;
uqComplaintDetailsDISPATCHSECTOR: TStringField;
uqComplaintDetailsADDRESSDISTRICT: TStringField;
uqComplaintDetailsADDRESSSECTOR: TStringField;
uqComplaintDetailsXCOORD: TFloatField;
uqComplaintDetailsYCOORD: TFloatField;
uqComplaintDetailsWARNINGS: TFloatField;
uqComplaintDetailsCONTACTS: TFloatField;
uqComplaintDetailsHISTORY: TFloatField;
uqComplaintDetailsDATEREPORTED: TDateTimeField;
uqComplaintDetailsDATERECEIVED: TDateTimeField;
uqComplaintDetailsDATEDISPATCHED: TDateTimeField;
uqComplaintDetailsDATERESPONDED: TDateTimeField;
uqComplaintDetailsDATEARRIVED: TDateTimeField;
uqComplaintDetailsDATECLEARED: TDateTimeField;
uqCFSActiveCOMPLAINTID: TFloatField;
uqCFSActiveUNITID: TFloatField;
uqCFSActiveUNITNAME: TStringField;
uqCFSActiveDATEDISPATCHED: TDateTimeField;
uqCFSActiveDATERESPONDED: TDateTimeField;
uqCFSActiveDATEARRIVED: TDateTimeField;
uqCFSActiveDATECLEARED: TDateTimeField;
uqCFSActiveLOCATION: TStringField;
uqCFSMemosMEMO_ID: TFloatField;
uqCFSMemosCFSID: TFloatField;
uqCFSMemosMEMO_TYPE: TFloatField;
uqCFSMemosTIMESTAMP: TDateTimeField;
uqCFSMemosBADGE_NUMBER: TStringField;
uqCFSMemosREMARKS: TStringField;
uqUnitsCurrentENTRYID: TFloatField;
uqUnitsCurrentUNITID: TFloatField;
uqUnitsCurrentUNITNAME: TStringField;
uqUnitsCurrentUNIT_DISTRICT: TStringField;
uqUnitsCurrentGPS_LATITUDE: TFloatField;
uqUnitsCurrentGPS_LONGITUDE: TFloatField;
uqDISUnitsActiveUNITID: TFloatField;
uqDISUnitsActiveUNITNAME: TStringField;
uqDISUnitsActiveCARNUMBER_DESC: TStringField;
uqDISUnitsActiveDISTRICT_DESC: TStringField;
uqDISUnitsActiveSECTOR_DESC: TStringField;
uqDISUnitsActiveOFFICER1_EMPNUM: TStringField;
uqDISUnitsActiveOFFICER1_LAST_NAME: TStringField;
uqDISUnitsActiveOFFICER1_FIRST_NAME: TStringField;
uqDISUnitsActiveOFFICER1_MI: TStringField;
uqDISUnitsActiveOFFICER2_EMPNUM: TStringField;
uqDISUnitsActiveOFFICER2_LAST_NAME: TStringField;
uqDISUnitsActiveOFFICER2_FIRST_NAME: TStringField;
uqDISUnitsActiveOFFICER2_MI: TStringField;
uqDISUnitsActiveLOCATION: TStringField;
uqDISUnitsActiveCOMPLAINT: TStringField;
uqDISUnitsActiveUNITSTATUS: TFloatField;
uqDISUnitsActiveUNIT_STATUS_DESC: TStringField;
uqDISUnitsActiveENTRYID: TFloatField;
uqDISUnitsActiveGPS_LATITUDE: TFloatField;
uqDISUnitsActiveGPS_LONGITUDE: TFloatField;
procedure DataModuleCreate(Sender: TObject);
private
{ Private declarations }
......
unit Api.Service;
interface
uses
XData.Service.Common,
Aurelius.Mapping.Attributes,
System.JSON,
System.Generics.Collections,
System.Classes;
const
API_MODEL = 'Api';
type
[ServiceContract, Model(API_MODEL)]
IApiService = interface(IInvokable)
['{4FCB7FAF-44E5-49D6-9C0F-EE44BFB33313}']
[HttpGet] function GetComplaintList: TJSONObject;
end;
implementation
end.
unit Api.ServiceImpl;
interface
uses
XData.Server.Module, XData.Service.Common, Api.Database, Data.DB,
System.SysUtils, System.Generics.Collections, XData.Sys.Exceptions,
System.Hash, System.Classes, Common.Logging, System.JSON, Api.Service;
type
[ServiceImplementation]
TApiService = class(TInterfacedObject, IApiService)
strict private
ApiDB: TApiDatabaseModule;
private
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
public
function GetComplaintList: TJSONObject;
end;
implementation
uses
uLibrary;
procedure TApiService.AfterConstruction;
begin
inherited;
ApiDB := TApiDatabaseModule.Create(nil);
Logger.Log(3, 'ApiDatabaseModule created');
end;
procedure TApiService.BeforeDestruction;
begin
ApiDB.Free;
inherited;
Logger.Log(3, 'ApiDatabaseModule destroyed');
end;
function TApiService.GetComplaintList: TJSONObject;
var
data: TJSONArray;
begin
Logger.Log(3, '---TApiService.GetComplaintList initiated');
Result := TJSONObject.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result);
data := TJSONArray.Create;
try
with ApiDB.uqComplaintList do
begin
Open;
First;
while not Eof do
begin
var status: string;
if not FieldByName('DATECLEARED').IsNull then
status := 'Cleared'
else if not FieldByName('DATEARRIVED').IsNull then
status := 'AtScene'
else if not FieldByName('DATEDISPATCHED').IsNull then
status := 'Dispatched'
else
status := 'Pending';
var item := TJSONObject.Create;
item.AddPair('ComplaintId', ApiDB.uqComplaintListCOMPLAINTID.AsString);
item.AddPair('Agency', ApiDB.uqComplaintListAGENCY.AsString);
item.AddPair('Priority', TJSONNumber.Create(ApiDB.uqComplaintListPRIORITY.AsString));
item.AddPair('DispatchCodeDesc', ApiDB.uqComplaintListDISPATCH_CODE_DESC.AsString);
item.AddPair('Address', ApiDB.uqComplaintListADDRESS.AsString);
item.AddPair('CFSId', ApiDB.uqComplaintListCFSID.AsString);
item.AddPair('Status', status);
item.AddPair('DispatchDistrict', ApiDB.uqComplaintListDISPATCHDISTRICT.AsString);
item.AddPair('DateReported', ApiDB.uqComplaintListDATEREPORTED.AsString);
data.AddElement(item);
Next;
end;
end;
Result.AddPair('count', TJSONNumber.Create(data.Count));
Result.AddPair('returned', TJSONNumber.Create(data.Count));
Result.AddPair('data', data);
except
data.Free;
Logger.Log(3, '---TApiService.GetComplaintList End (error)');
raise EXDataHttpException.Create(500, 'Failed to load complaints list');
end;
Logger.Log(3, '---TApiService.GetComplaintList End');
end;
initialization
RegisterServiceType(TApiService);
end.
// Lookup Service interface which retrieves information from the database
// which is then sent to the client.
// Authors:
// Cameron Hayes
// Mac ...
// Elias Sarraf
unit Lookup.Service;
interface
uses
XData.Service.Common,
Aurelius.Mapping.Attributes,
System.JSON,
System.Generics.Collections,
System.Classes;
const
API_MODEL = 'Api';
type
TCallItem = class
// Class of the info we want from the database from a specific call.
// callSid: SID of the call, 34 digit string.
// fromNumber: Who the phone call was from. (xxx) xxx-xxxx
// toNumber: Who the phone call was to. (xxx) xxx-xxxx
// dateCreated: Date the phone call was created. mm/dd/yyyy hh:nn:ss am/pm
// mediaURL: Link to the recording audio
// duration: Length of the entire call and recording.
// transcription: Transcription of the recording. Not always present due to
// the call being answerered or caller did not leave a message.
public
callSid: string;
fromNumber: string;
toNumber: string;
dateCreated: string;
mediaUrl: string;
duration: string;
transcription: string;
end;
// List of call items
// count: Total amount of records that fit the SQL query
// data: List of retrieved calls
TCallList = class
public
count: integer;
data: TList<TCallItem>;
end;
TUserItem = class
public
userID: string;
username: string;
full_name: string;
phone_number: string;
email_address: string;
admin: boolean;
active: boolean;
password: string;
end;
TUserList = class
public
count: integer;
data: TList<TUserItem>;
end;
type
[ServiceContract, Model(API_MODEL)]
ILookupService = interface(IInvokable)
['{F24E1468-5279-401F-A877-CD48B44F4416}']
[HttpGet] function GetCalls(searchOptions: string): TCallList;
[HttpGet] function Search(phoneNum: string): TCallList;
[HttpGet] function GetUsers(searchOptions: string): TUserList;
function AddUser(userInfo: string): string;
function DelUser(username: string): string;
function EditUser(const editOptions: string): string;
end;
implementation
initialization
RegisterServiceType(TypeInfo(ILookupService));
end.
// Implementation of the Lookup Service interface used to send call information
// to the client.
// Authors:
// Cameron Hayes
// Mac ...
// Elias Sarraf
unit Lookup.ServiceImpl;
interface
uses
XData.Server.Module, XData.Service.Common, Api.Database, Data.DB,
Lookup.Service, System.Hash, System.Classes, Common.Logging;
type
[ServiceImplementation]
TLookupService = class(TInterfacedObject, ILookupService)
strict private
callsDB: TApiDatabaseModule;
private
function GetUsers(searchOptions: string): TUserList;
function GetCalls(searchOptions: string): TCallList;
function EditUser(const editOptions: string): string;
function Search(phoneNum: string): TCallList;
function AddUser(userInfo: string): string;
function DelUser(username: string): string;
procedure AfterConstruction; override;
procedure BeforeDestruction; override;
end;
implementation
uses
System.SysUtils,
System.Generics.Collections,
XData.Sys.Exceptions, uLibrary;
procedure TLookupService.AfterConstruction;
begin
inherited;
callsDB := TApiDatabaseModule.Create(nil);
Logger.Log(3, 'callsDB created');
end;
procedure TLookupService.BeforeDestruction;
begin
callsDB.Free;
inherited;
Logger.Log(3, 'callsDB destroyed');
end;
function TLookupService.Search(phoneNum: string): TCallList;
var
SQL: string;
call: TCallItem;
begin
Logger.Log(3, 'Search called for phone number: ' + phoneNum);
SQL := 'select * ' +
'from calls inner join recordings on calls.sid = recordings.call_sid ' +
'where from_formatted = ' + QuotedStr(phoneNum) +
' order by calls.date_created desc';
Logger.Log(3, 'Search main query: ' + SQL);
doQuery(callsDB.UniQuery1, SQL);
Result := TCallList.Create;
Result.data := TList<TCallItem>.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result.data);
while not callsDB.UniQuery1.Eof do
begin
call := TCallItem.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(call);
Result.data.Add(call);
call.callSid := callsDB.UniQuery1.FieldByName('sid').AsString;
call.fromNumber := callsDB.UniQuery1.FieldByName('from_formatted').AsString;
call.ToNumber := callsDB.UniQuery1.FieldByName('to_formatted').AsString;
call.dateCreated := callsDB.UniQuery1.FieldByName('date_created').AsString;
call.mediaUrl := callsDB.UniQuery1.FieldByName('media_url').AsString;
call.duration := callsDB.UniQuery1.FieldByName('duration').AsString;
call.transcription := callsDB.UniQuery1.FieldByName('transcription').AsString;
callsDB.UniQuery1.Next;
end;
callsDB.UniQuery1.Close;
SQL := 'select count(*) as total_count from calls inner join ' +
'recordings on calls.sid = recordings.call_sid where ' +
'from_formatted = ' + QuotedStr(phoneNum);
Logger.Log(3, 'Search count query: ' + SQL);
doQuery(callsDB.UniQuery1, SQL);
Result.count := callsDB.UniQuery1.FieldByName('total_count').AsInteger;
callsDB.UniQuery1.Close;
Logger.Log(3, 'Search completed. Total results: ' + IntToStr(Result.count));
end;
function TLookupService.GetCalls(searchOptions: string): TCallList;
var
params: TStringList;
SQL: string;
DBString: string;
call: TCallItem;
offset: string;
limit: string;
PhoneNum: string;
PageNum: integer;
PageSize: integer;
StartDate: string;
EndDate: string;
OrderBy: string;
whereSQL: string;
orderBySQL: string;
Caller: string;
begin
Logger.Log(3, 'GetCalls called with searchOptions: ' + searchOptions);
params := TStringList.Create;
params.StrictDelimiter := true;
params.Delimiter := '&';
params.DelimitedText := searchOptions;
PhoneNum := params.Values['phonenumber'];
PageNum := StrToInt(params.Values['pagenumber']);
PageSize := StrToInt(params.Values['pagesize']);
StartDate := params.Values['startdate'];
EndDate := params.Values['enddate'];
OrderBy := params.Values['orderby'];
Caller := params.Values['caller'];
offset := IntToStr((PageNum - 1) * PageSize);
limit := IntToStr(PageSize);
whereSQL := 'where ';
if PhoneNum <> '' then
whereSQL := whereSQL + 'to_formatted = ' + QuotedStr(PhoneNum);
if StartDate <> '' then
if whereSQL = 'where ' then
whereSQL := whereSQL + 'calls.date_created > ' + QuotedStr(StartDate)
else
whereSQL := whereSQL + ' AND calls.date_created > ' + QuotedStr(StartDate);
if EndDate <> '' then
if whereSQL = 'where ' then
whereSQL := whereSQL + 'calls.date_created < ' + QuotedStr(EndDate)
else
whereSQL := whereSQL + ' AND calls.date_created < ' + QuotedStr(EndDate);
if Caller <> '' then
if whereSQL = 'where ' then
whereSQL := whereSQL + 'from_formatted = ' + QuotedStr(Caller)
else
whereSQL := whereSQL + ' AND from_formatted = ' + QuotedStr(Caller);
if whereSQL = 'where ' then
whereSQL := '';
if (OrderBy = '') or (OrderBy = 'Date') then
orderBySQL := 'order by calls.date_created desc'
else
orderBySQL := 'order by calls.from_formatted desc';
SQL := 'select * ' +
'from calls inner join recordings on calls.sid = recordings.call_sid ' +
whereSQL + ' ' + orderBySQL + ' limit ' + limit + ' offset ' + offset;
Logger.Log(3, 'GetCalls query: ' + SQL);
doQuery(callsDB.UniQuery1, SQL);
Result := TCallList.Create;
Result.data := TList<TCallItem>.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result.data);
while not callsDB.UniQuery1.Eof do
begin
call := TCallItem.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(call);
Result.data.Add(call);
call.callSid := callsDB.UniQuery1.FieldByName('sid').AsString;
call.fromNumber := callsDB.UniQuery1.FieldByName('from_formatted').AsString;
call.ToNumber := callsDB.UniQuery1.FieldByName('to_formatted').AsString;
call.dateCreated := callsDB.UniQuery1.FieldByName('date_created').AsString;
call.mediaUrl := callsDB.UniQuery1.FieldByName('media_url').AsString;
call.duration := callsDB.UniQuery1.FieldByName('duration').AsString;
call.transcription := callsDB.UniQuery1.FieldByName('transcription').AsString;
callsDB.UniQuery1.Next;
end;
callsDB.UniQuery1.Close;
SQL := 'select count(*) as total_count from calls inner join recordings on calls.sid = recordings.call_sid ' + whereSQL;
Logger.Log(3, 'GetCalls count query: ' + SQL);
doQuery(callsDB.UniQuery1, SQL);
Result.count := callsDB.UniQuery1.FieldByName('total_count').AsInteger;
callsDB.UniQuery1.Close;
Logger.Log(3, 'GetCalls completed successfully. Returned count: ' + IntToStr(Result.count));
end;
function TLookupService.GetUsers(searchOptions: string): TUserList;
var
SQL: string;
user: TUserItem;
begin
if searchOptions = '' then
SQL := 'select * from users order by full_name ASC'
else
SQL := 'select * from users where username=' + QuotedStr(searchOptions);
Logger.Log(3, 'GetUsers query: ' + SQL);
doQuery(callsDB.UniQuery1, SQL);
Result := TUserList.Create;
Result.data := TList<TUserItem>.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(Result.data);
while not callsDB.UniQuery1.Eof do
begin
user := TUserItem.Create;
TXDataOperationContext.Current.Handler.ManagedObjects.Add(user);
Result.data.Add(user);
user.userID := callsDB.UniQuery1.FieldByName('user_id').AsString;
user.username := callsDB.UniQuery1.FieldByName('username').AsString;
user.full_name := callsDB.UniQuery1.FieldByName('full_name').AsString;
user.phone_number := callsDB.UniQuery1.FieldByName('phone_number').AsString;
user.email_address := callsDB.UniQuery1.FieldByName('email').AsString;
user.admin := callsDB.UniQuery1.FieldByName('admin').AsBoolean;
user.password := callsDB.UniQuery1.FieldByName('password').AsString;
user.active := callsDB.UniQuery1.FieldByName('active').AsBoolean;
callsDB.UniQuery1.Next;
end;
callsDB.UniQuery1.Close;
SQL := 'select count(*) as total_count from users';
Logger.Log(3, 'GetUsers count query: ' + SQL);
doQuery(callsDB.UniQuery1, SQL);
Result.count := callsDB.UniQuery1.FieldByName('total_count').AsInteger;
callsDB.UniQuery1.Close;
Logger.Log(3, 'GetUsers returned user list, count = ' + IntToStr(Result.count));
end;
function TLookupService.EditUser(const editOptions: string): string;
var
params: TStringList;
user, full_name, email, phone, Admin, newUser, hashString, hashPW, password, active: string;
SQL: string;
begin
Logger.Log(3, 'EditUser called with options: ' + editOptions);
params := TStringList.Create;
params.Delimiter := '&';
params.StrictDelimiter := true;
params.DelimitedText := editOptions;
user := params.Values['username'];
full_name := params.Values['fullname'];
phone := params.Values['phonenumber'];
email := params.Values['email'];
Admin := params.Values['admin'];
newUser := params.Values['newuser'];
password := params.Values['password'];
active := params.Values['active'];
SQL := 'select * from users where username = ' + QuotedStr(user);
Logger.Log(3, 'EditUser query: ' + SQL);
doQuery(callsDB.UniQuery1, SQL);
if callsDB.UniQuery1.IsEmpty then
begin
Result := 'No such user found';
Logger.Log(2, 'EditUser failed: user not found (' + user + ')');
end
else
begin
callsDB.UniQuery1.Edit;
if not newUser.IsEmpty then
callsDB.UniQuery1.FieldByName('username').AsString := newUser;
if not full_name.IsEmpty then
callsDB.UniQuery1.FieldByName('full_name').AsString := full_name;
if not phone.IsEmpty then
callsDB.UniQuery1.FieldByName('phone_number').AsString := phone;
if not email.IsEmpty then
callsDB.UniQuery1.FieldByName('email').AsString := email;
if not Admin.IsEmpty then
callsDB.UniQuery1.FieldByName('admin').AsBoolean := StrToBool(Admin);
if not Active.IsEmpty then
callsDB.UniQuery1.FieldByName('active').AsBoolean := StrToBool(Active);
if (password <> 'hidden') and (not password.IsEmpty) then
begin
hashString := callsDB.UniQuery1.FieldByName('date_created').AsString + password;
hashPW := THashSHA2.GetHashString(hashString, THashSHA2.TSHA2Version.SHA512).ToUpper;
callsDB.UniQuery1.FieldByName('password').AsString := hashPW;
end;
callsDB.UniQuery1.Post;
Result := 'Success:Edit Successful';
Logger.Log(3, 'EditUser success for: ' + user);
end;
callsDB.UniQuery1.Close;
end;
function TLookupService.AddUser(userInfo: string): string;
var
dateCreated: TDateTime;
hashString, hashPW, SQL: string;
params: TStringList;
begin
Logger.Log(3, 'AddUser called with info: ' + userInfo);
params := TStringList.Create;
try
params.StrictDelimiter := true;
params.Delimiter := '&';
params.DelimitedText := userInfo;
dateCreated := Now;
hashString := DateTimeToStr(dateCreated) + params.Values['password'];
hashPW := THashSHA2.GetHashString(hashString, THashSHA2.TSHA2Version.SHA512).ToUpper;
SQL := 'select * from users where username = ' + QuotedStr(params.Values['username'].ToLower);
Logger.Log(3, 'AddUser query: ' + SQL);
callsDB.UniQuery1.Close;
callsDB.UniQuery1.SQL.Text := SQL;
callsDB.UniQuery1.Open;
if callsDB.UniQuery1.IsEmpty then
begin
callsDB.UniQuery1.Insert;
callsDB.UniQuery1.FieldByName('username').AsString := params.Values['username'].ToLower;
callsDB.UniQuery1.FieldByName('password').AsString := hashPW;
callsDB.UniQuery1.FieldByName('date_created').AsString := DateTimeToStr(dateCreated);
callsDB.UniQuery1.FieldByName('full_name').AsString := params.Values['fullname'];
callsDB.UniQuery1.FieldByName('phone_number').AsString := params.Values['phonenumber'];
callsDB.UniQuery1.FieldByName('email').AsString := params.Values['email'];
callsDB.UniQuery1.FieldByName('admin').AsBoolean := StrToBool(params.Values['admin']);
callsDB.UniQuery1.FieldByName('active').AsBoolean := True;
callsDB.UniQuery1.Post;
Result := 'Success:User successfully added';
Logger.Log(3, 'AddUser success: ' + params.Values['username']);
end
else
begin
Result := 'Failure:Username already taken';
Logger.Log(2, 'AddUser failed: Username already taken (' + params.Values['username'] + ')');
end;
finally
params.Free;
end;
end;
function TLookupService.DelUser(username: string): string;
var
SQL: string;
begin
Logger.Log(3, 'DelUser called for: ' + username);
SQL := 'select * from users where username = ' + QuotedStr(username.ToLower);
callsDB.UniQuery1.Close;
callsDB.UniQuery1.SQL.Text := SQL;
callsDB.UniQuery1.Open;
if callsDB.UniQuery1.IsEmpty then
begin
Result := 'Failure:User does not exist';
Logger.Log(2, 'DelUser failed: user not found (' + username + ')');
end
else
begin
SQL := 'DELETE FROM users where username = ' + QuotedStr(username.ToLower);
callsDB.UniQuery1.SQL.Text := SQL;
callsDB.UniQuery1.ExecSQL;
Result := 'Success:User deleted';
Logger.Log(3, 'DelUser success: ' + username);
end;
end;
initialization
RegisterServiceType(TLookupService);
end.
......@@ -7,7 +7,7 @@ uses
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, Vcl.ExtCtrls, System.Generics.Collections, System.IniFiles,
Auth.Service, Auth.Server.Module, Api.Server.Module, App.Server.Module,
ExeInfo, Lookup.Service;
ExeInfo, Api.Service;
type
TFMain = class(TForm)
......
......@@ -18,9 +18,9 @@ uses
Auth.Database in 'Source\Auth.Database.pas' {AuthDatabase: TDataModule},
uLibrary in 'Source\uLibrary.pas',
Auth.Service in 'Source\Auth.Service.pas',
Lookup.Service in 'Source\Lookup.Service.pas',
Api.Service in 'Source\Api.Service.pas',
Auth.ServiceImpl in 'Source\Auth.ServiceImpl.pas',
Lookup.ServiceImpl in 'Source\Lookup.ServiceImpl.pas',
Api.ServiceImpl in 'Source\Api.ServiceImpl.pas',
Twilio.Data.Module in 'Source\Twilio.Data.Module.pas' {TwilioDataModule: TDataModule},
App.Server.Module in 'Source\App.Server.Module.pas' {AppServerModule: TDataModule},
Common.Ini in 'Source\Common.Ini.pas';
......
......@@ -160,9 +160,9 @@
</DCCReference>
<DCCReference Include="Source\uLibrary.pas"/>
<DCCReference Include="Source\Auth.Service.pas"/>
<DCCReference Include="Source\Lookup.Service.pas"/>
<DCCReference Include="Source\Api.Service.pas"/>
<DCCReference Include="Source\Auth.ServiceImpl.pas"/>
<DCCReference Include="Source\Lookup.ServiceImpl.pas"/>
<DCCReference Include="Source\Api.ServiceImpl.pas"/>
<DCCReference Include="Source\Twilio.Data.Module.pas">
<Form>TwilioDataModule</Form>
<DesignClass>TDataModule</DesignClass>
......
[Settings]
LogFileNum=409
LogFileNum=414
webClientVersion=0.1.0
TwilioUpdateTime=1
......
object FViewComplaints: TFViewComplaints
Width = 676
Width = 359
Height = 480
CSSLibrary = cssBootstrap
ElementFont = efCSS
......@@ -11,157 +11,91 @@ object FViewComplaints: TFViewComplaints
ParentFont = False
Visible = True
OnCreate = WebFormCreate
object lblEntries: TWebLabel
Left = 0
Top = 336
Width = 77
Height = 13
Caption = 'Showing 0 of ...'
ElementID = 'lblentries'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
Visible = False
WidthPercent = 100.000000000000000000
end
object wcbPageSize: TWebComboBox
Left = 0
Top = 0
Width = 145
Height = 21
ElementClassName = 'custom-select'
ElementID = 'wcbpagesize'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
Text = '10'
Visible = False
WidthPercent = 100.000000000000000000
ItemIndex = -1
Items.Strings = (
'10'
'25'
'50')
end
object wcbLocation: TWebLookupComboBox
Left = 154
Top = 0
Width = 145
Height = 22
ElementClassName = 'custom-select'
ElementID = 'wcblocation'
ElementFont = efCSS
HeightPercent = 100.000000000000000000
Visible = False
WidthPercent = 100.000000000000000000
ItemIndex = -1
LookupValues = <
item
DisplayText = 'All'
end
item
Value = '(716) 681-8820'
DisplayText = 'Galleria'
end
item
Value = '(716) 297-4654'
DisplayText = 'NF Outlet'
end
item
Value = '(585) 445-8911'
DisplayText = 'Rochester'
end
item
Value = '(315) 565-4138'
DisplayText = 'Syracuse'
end>
end
object dtpStartDate: TWebEdit
Left = 342
Top = 0
Width = 121
Height = 22
ChildOrder = 10
ElementClassName = 'form-control'
ElementID = 'dtpstartdate'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
Visible = False
WidthPercent = 100.000000000000000000
end
object dtpEndDate: TWebEdit
Left = 478
Top = 0
Width = 121
Height = 22
ChildOrder = 10
ElementClassName = 'form-control'
ElementID = 'dtpenddate'
object WebButton1: TWebButton
Left = 180
Top = 110
Width = 43
Height = 25
Caption = 'Group'
ChildOrder = 1
ElementClassName = 'btn btn-light'
ElementID = 'complaints_btngroup'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
Visible = False
WidthPercent = 100.000000000000000000
end
object wcbSortBy: TWebComboBox
Left = 442
Top = 52
Width = 145
Height = 21
ElementClassName = 'custom-select'
ElementID = 'wcbsortby'
object WebButton2: TWebButton
Left = 242
Top = 110
Width = 37
Height = 25
Caption = 'Filter'
ChildOrder = 1
ElementClassName = 'btn btn-light'
ElementID = 'complaints_btnfilter'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
Text = 'Date'
Visible = False
WidthPercent = 100.000000000000000000
ItemIndex = -1
Items.Strings = (
'Date'
'Phone Number')
end
object btnApply: TWebButton
Left = 478
Top = 128
Width = 96
object Complaints: TWebButton
Left = 114
Top = 110
Width = 53
Height = 25
Caption = 'Apply'
ChildOrder = 7
Caption = 'Refresh'
ChildOrder = 1
ElementClassName = 'btn btn-light'
ElementID = 'btnapply'
ElementID = 'complaints_btnrefresh'
ElementFont = efCSS
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
Visible = False
WidthPercent = 100.000000000000000000
end
object edtSearch: TWebEdit
Left = 50
Top = 382
Width = 121
Height = 22
HelpType = htKeyword
ChildOrder = 8
ElementClassName = 'form-control'
ElementID = 'edtsearch'
ElementFont = efCSS
object dblComplaintsList: TWebDBListControl
Left = 36
Top = 148
Width = 263
Height = 237
ElementID = 'complaints_dbl_complaint_list'
HeightStyle = ssAuto
HeightPercent = 100.000000000000000000
HideSelection = False
TextHint = 'Format: (XXX) XXX-XXXX'
Visible = False
WidthPercent = 100.000000000000000000
ChildOrder = 3
DefaultItemClassName = 'list-group-item'
DefaultItemLinkClassName = 'list-group-link'
ElementFont = efCSS
ElementListClassName = 'list-group'
Items = <
item
ItemClassName = 'list-group-item'
Items = <>
LinkClassName = 'list-group-link'
Text = 'Item 0'
end
item
ItemClassName = 'list-group-item'
Items = <>
LinkClassName = 'list-group-link'
Text = 'Item 1'
end
item
ItemClassName = 'list-group-item'
Items = <>
LinkClassName = 'list-group-link'
Text = 'Item 2'
end>
Style = lsListGroup
end
object XDataWebClient1: TXDataWebClient
object xdwcComplaints: TXDataWebClient
Connection = DMConnection.ApiConnection
Left = 426
Top = 240
Left = 92
Top = 416
end
object XDataWebDataSet1: TXDataWebDataSet
object xdwdsComplaints: TXDataWebDataSet
Connection = DMConnection.ApiConnection
Left = 440
Top = 300
Left = 214
Top = 414
end
end
......@@ -5,20 +5,20 @@
<div class="container-fluid">
<div class="row w-100 g-2 align-items-stretch">
<div class="col">
<span id="complaints.title" class="navbar-brand mb-0 h5 text-white">Complaints</span>
<span id="complaints_title" class="navbar-brand mb-0 h5 text-white">Complaints</span>
</div>
<div class="col">
<button id="complaints.btnrefresh" type="button" class="btn btn-primary w-100 h-100">
<button id="complaints_btnrefresh" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-sync-alt me-1"></i><span class="d-none d-sm-inline">Refresh</span>
</button>
</div>
<div class="col">
<button id="complaints.btngroup" type="button" class="btn btn-primary w-100 h-100">
<button id="complaints_btngroup" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-layer-group me-1"></i><span class="d-none d-sm-inline">Group</span>
</button>
</div>
<div class="col">
<button id="complaints.btnfilter" type="button" class="btn btn-primary w-100 h-100">
<button id="complaints_btnfilter" type="button" class="btn btn-primary w-100 h-100">
<i class="fa fa-sliders-h me-1"></i><span class="d-none d-sm-inline">Filter</span>
</button>
</div>
......@@ -31,52 +31,33 @@
<div class="container-fluid">
<div class="input-group">
<span class="input-group-text bg-white"><i class="fa fa-search"></i></span>
<input id="complaints.search" class="form-control" placeholder="Search...">
<input id="complaints_search" class="form-control" placeholder="Search...">
</div>
</div>
</div>
</div> <!-- /sticky-top wrapper -->
<!-- Existing content (unchanged) -->
<div class="row">
<div class="col-12">
<div class="container mt-4">
<div class="row justify-content-center">
<div class="col-12 col-md-10 col-lg-8">
<h1 class="page-header pt-3 pb-2 mb-3 border-bottom fs-4 fw-bold" id="view.calls.title">Complaints</h1>
<!-- Data Table -->
<div class="table-responsive mt-4">
<table class="table table-sm table-striped table-bordered align-middle" id="tblPhoneGrid">
<thead class="table-dark">
<tr>
<th scope="col">Phone Number</th>
<th scope="col">Caller</th>
<th scope="col">Time</th>
<th scope="col">Duration</th>
<th scope="col">Transcript</th>
<th scope="col">Listen</th>
</tr>
</thead>
<tbody>
<!-- Rows added dynamically in Delphi -->
</tbody>
</table>
</div>
<!-- Complaints list container -->
<div class="container-fluid mt-2">
<div class="row justify-content-center">
<div class="col-12 col-md-10 col-lg-8">
<!-- This is where the DBListControl will inject cards -->
<div id="complaints_dbl_complaint_list" class="d-flex flex-column gap-2">
<!-- Cards will render here -->
</div>
<!-- Entry Count Label -->
<label id="lblentries" class="mt-2 d-block"></label>
<!-- Entry Count Label -->
<label id="lblentries" class="mt-2 d-block"></label>
<!-- Pagination -->
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center" id="pagination">
<!-- Pagination items added in Delphi -->
</ul>
</nav>
</div>
</div>
<!-- Pagination -->
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center" id="pagination">
<!-- Pagination items rendered in Delphi -->
</ul>
</nav>
</div>
</div>
</div>
......@@ -5,32 +5,20 @@ interface
uses
System.SysUtils, System.Classes, Web, WEBLib.Graphics, WEBLib.Forms, WEBLib.Dialogs,
Vcl.Controls, Vcl.StdCtrls, WEBLib.StdCtrls, WEBLib.Controls, WEBLib.Grids, WebLib.Lists,
XData.Web.Client, WEBLib.ExtCtrls, DB, XData.Web.JsonDataset,
XData.Web.Dataset, XData.Web.Connection, Vcl.Forms, DateUtils, WebAudio;
XData.Web.Client, WEBLib.ExtCtrls, DB, XData.Web.JsonDataset, WebLib.DB,
XData.Web.Dataset, XData.Web.Connection, Vcl.Forms, DateUtils, WEBLib.DBCtrls;
type
TFViewComplaints = class(TWebForm)
XDataWebClient1: TXDataWebClient;
XDataWebDataSet1: TXDataWebDataSet;
lblEntries: TWebLabel;
wcbPageSize: TWebComboBox;
wcbLocation: TWebLookupComboBox;
dtpStartDate: TWebEdit;
dtpEndDate: TWebEdit;
wcbSortBy: TWebComboBox;
btnApply: TWebButton;
edtSearch: TWebEdit;
xdwcComplaints: TXDataWebClient;
xdwdsComplaints: TXDataWebDataSet;
WebButton1: TWebButton;
WebButton2: TWebButton;
Complaints: TWebButton;
dblComplaintsList: TWebDBListControl;
procedure WebFormCreate(Sender: TObject);
procedure btnApplyClick(Sender: TObject);
procedure btnSearchClick(Sender: TObject);
private
FChildForm: TWebForm;
procedure AddRowToTable(PhoneNumber, Caller, Time, Duration, Transcript, MediaUrl: string);
procedure ClearTable();
procedure GeneratePagination(TotalPages: Integer);
function GenerateSearchOptions(): string;
[async] procedure Search(searchOptions: string);
[async] procedure GetCalls(searchOptions: string);
var
PageNumber: integer;
PageSize: integer;
......@@ -55,422 +43,12 @@ uses
{$R *.dfm}
procedure TFViewComplaints.WebFormCreate(Sender: TObject);
// Initializes important values:
// PageNumber: What page number the user is on IE 1: 1-10, 2: 11-20 etc
// TotalPages: Total number of pages returned from the search.
// PageSize: Number of entries per page.
begin
DMConnection.ApiConnection.Connected := True;
PageNumber := 1;
TotalPages := 1; // Initial total pages
wcbPageSize.Text := '10';
wcbLocation.DisplayText := 'All';
wcbSortBy.Text := 'Date';
// hardcoded default toNumber/location and load grid
wcbLocation.Value := 'Galleria';
PageSize := StrToInt(wcbPageSize.Text);
StartDate := '';
EndDate := '';
OrderBy := wcbSortBy.Text;
Caller := '';
GetCalls(GenerateSearchOptions);
end;
procedure TFViewComplaints.AddRowToTable(PhoneNumber, Caller, Time, Duration, Transcript, MediaUrl: string);
// Adds rows to the table
// PhoneNumber: phone number of the location
// Caller: phone number of the caller
// Duration: duration of the call
// Transcript: transcription of the recording
// MediaUrl: Link to the recording
var
NewRow, Cell, P, Button, Audio: TJSHTMLElement;
begin
NewRow := TJSHTMLElement(document.createElement('tr'));
// Phone Number Cell
Cell := TJSHTMLElement(document.createElement('td'));
Cell.setAttribute('data-label', 'Phone Number');
Cell.innerText := PhoneNumber;
NewRow.appendChild(Cell);
// Caller Cell
Cell := TJSHTMLElement(document.createElement('td'));
Cell.setAttribute('data-label', 'Caller');
Cell.innerText := Caller;
NewRow.appendChild(Cell);
// Time Cell
Cell := TJSHTMLElement(document.createElement('td'));
Cell.setAttribute('data-label', 'Time');
Cell.innerText := DateTimeToStr(IncHour(StrToDateTime(Time), -4));;
NewRow.appendChild(Cell);
// Duration Cell
Cell := TJSHTMLElement(document.createElement('td'));
Cell.setAttribute('data-label', 'Duration');
Cell.innerText := Duration;
NewRow.appendChild(Cell);
// Transcript Cell
Cell := TJSHTMLElement(document.createElement('td'));
Cell.setAttribute('data-label', 'Transcript');
P := TJSHTMLElement(document.createElement('p'));
P.className := 'transcript';
P.innerText := Transcript;
Cell.appendChild(P);
NewRow.appendChild(Cell);
// Listen Button Cell
Cell := TJSHTMLElement(document.createElement('td'));
Cell.setAttribute('data-label', 'Listen');
Button := TJSHTMLElement(document.createElement('button'));
Button.className := 'btn btn-primary';
Button.innerHTML := '<i class="fa fa-play"></i>';
// Open voicemail audio in a new tab on click
Button.addEventListener('click', procedure(Event: TJSMouseEvent)
begin
{$IFNDEF WIN32}
asm
var audioElem = document.getElementById('playerAudio');
audioElem.src = MediaUrl;
audioElem.load();
audioElem.play();
var modal = document.getElementById('playerModal');
if (modal && modal.parentNode !== document.body) {
document.body.appendChild(modal);
}
// Create the Bootstrap modal object
var bsModal = new bootstrap.Modal(modal, { keyboard: false });
// Listen for when the modal *finishes* hiding:
modal.addEventListener('hidden.bs.modal', function (evt) {
// Pause and/or reset the audio
audioElem.pause();
audioElem.currentTime = 0;
}, { once: true });
// Show the modal
bsModal.show();
end;
{$ENDIF}
end);
// Add the button to the cell, and the cell to the row
Cell.appendChild(Button);
NewRow.appendChild(Cell);
// Appends new rows to the table body
TJSHTMLElement(document.getElementById('tblPhoneGrid').getElementsByTagName('tbody')[0]).appendChild(NewRow);
end;
procedure TFViewComplaints.GeneratePagination(TotalPages: Integer);
// Generates pagination for the table.
// TotalPages: Total amount of pages generated by the search
var
PaginationElement, PageItem, PageLink: TJSHTMLElement;
I, Start, Finish: Integer;
begin
PaginationElement := TJSHTMLElement(document.getElementById('pagination'));
PaginationElement.innerHTML := ''; // Clear existing pagination
// Previous Button
PageItem := TJSHTMLElement(document.createElement('li'));
PageItem.className := 'page-item';
if PageNumber = 1 then
PageItem.classList.add('disabled');
PageLink := TJSHTMLElement(document.createElement('a'));
PageLink.className := 'page-link';
PageLink.innerText := 'Previous';
PageLink.setAttribute('href', 'javascript:void(0)');
PageLink.addEventListener('click', procedure(Event: TJSMouseEvent)
begin
if PageNumber > 1 then
begin
Dec(PageNumber);
GetCalls(GenerateSearchOptions());
end;
end);
PageItem.appendChild(PageLink);
PaginationElement.appendChild(PageItem);
// Page Numbers
if PageNumber <= 4 then
// If page number is low enough no early elipsis needed
Begin
Start := 2;
Finish := 5;
End
else if (PageNumber >= (TotalPages - 3)) then
// If page number is high enough no late elipsis needed
begin
Start := TotalPages - 3;
Finish := TotalPages - 1;
end
else
begin
Start := PageNumber - 1;
Finish := PageNumber + 1;
end;
PageItem := TJSHTMLElement(document.createElement('li'));
PageItem.className := 'page-item';
if 1 = PageNumber then
PageItem.classList.add('selected-number'); // Add the selected-number class
PageLink := TJSHTMLElement(document.createElement('a'));
PageLink.className := 'page-link';
PageLink.innerText := '1';
PageLink.setAttribute('href', 'javascript:void(0)');
PageLink.addEventListener('click', procedure(Event: TJSMouseEvent)
var
PageNum: Integer;
begin
PageNum := StrToInt((Event.currentTarget as TJSHTMLElement).innerText);
PageNumber := PageNum;
GetCalls(GenerateSearchOptions());
end);
PageItem.appendChild(PageLink);
PaginationElement.appendChild(PageItem);
// Adds Elipse to pagination if page number is too big
if PageNumber > 4 then
begin
PageItem := TJSHTMLElement(document.createElement('li'));
PageItem.className := 'page-item';
PageItem.classList.add('disabled');
PageLink := TJSHTMLElement(document.createElement('a'));
PageLink.className := 'page-link';
PageLink.innerText := '...';
PageLink.setAttribute('href', 'javascript:void(0)');
PageItem.appendChild(PageLink);
PaginationElement.appendChild(PageItem);
end;
// Adds Page, page - 1, and page + 1 to pagination
for I := Start to Finish do
begin
if ( I > 1) and (I < TotalPages) then
begin
PageItem := TJSHTMLElement(document.createElement('li'));
PageItem.className := 'page-item';
if I = PageNumber then
PageItem.classList.add('selected-number'); // Add the selected-number class
PageLink := TJSHTMLElement(document.createElement('a'));
PageLink.className := 'page-link';
PageLink.innerText := IntToStr(I);
PageLink.setAttribute('href', 'javascript:void(0)');
PageLink.addEventListener('click', procedure(Event: TJSMouseEvent)
var
PageNum: Integer;
begin
PageNum := StrToInt((Event.currentTarget as TJSHTMLElement).innerText);
PageNumber := PageNum;
GetCalls(GenerateSearchOptions());
end);
PageItem.appendChild(PageLink);
PaginationElement.appendChild(PageItem);
end;
end;
// adds ellipse if number is too small
if PageNumber < TotalPages - 4 then
begin
PageItem := TJSHTMLElement(document.createElement('li'));
PageItem.className := 'page-item';
PageItem.classList.add('disabled');
PageLink := TJSHTMLElement(document.createElement('a'));
PageLink.className := 'page-link';
PageLink.innerText := '...';
PageLink.setAttribute('href', 'javascript:void(0)');
PageItem.appendChild(PageLink);
PaginationElement.appendChild(PageItem);
end;
if TotalPages <> 1 then
begin
PageItem := TJSHTMLElement(document.createElement('li'));
PageItem.className := 'page-item';
if TotalPages = PageNumber then
PageItem.classList.add('selected-number');
PageLink := TJSHTMLElement(document.createElement('a'));
PageLink.className := 'page-link';
PageLink.innerText := IntToStr(TotalPages);
PageLink.setAttribute('href', 'javascript:void(0)');
PageLink.addEventListener('click', procedure(Event: TJSMouseEvent)
var
PageNum: Integer;
begin
PageNum := StrToInt((Event.currentTarget as TJSHTMLElement).innerText);
PageNumber := PageNum;
GetCalls(generateSearchOptions());
end);
end;
PageItem.appendChild(PageLink);
PaginationElement.appendChild(PageItem);
// Next Button
PageItem := TJSHTMLElement(document.createElement('li'));
PageItem.className := 'page-item';
if PageNumber = TotalPages then
PageItem.classList.add('disabled');
PageLink := TJSHTMLElement(document.createElement('a'));
PageLink.className := 'page-link';
PageLink.innerText := 'Next';
PageLink.setAttribute('href', 'javascript:void(0)');
PageLink.addEventListener('click', procedure(Event: TJSMouseEvent)
begin
if PageNumber < TotalPages then
begin
Inc(PageNumber);
GetCalls(GenerateSearchOptions());
end;
end);
PageItem.appendChild(PageLink);
PaginationElement.appendChild(PageItem);
end;
procedure TFViewComplaints.GetCalls(searchOptions: string);
// Retrieves list of calls from the server that meet the criteria searchOptions
// searchOptions: information about how to generate the SQL statement based on
// user input.
var
xdcResponse: TXDataClientResponse;
callList : TJSObject;
i: integer;
data: TJSArray;
call: TJSObject;
callListLength: integer;
begin
if PageNumber > 0 then
begin
Utils.ShowSpinner('spinner');
xdcResponse := await(XDataWebClient1.RawInvokeAsync('ILookupService.GetCalls',
[searchOptions]));
callList := TJSObject(xdcResponse.Result);
data := TJSArray(callList['data']);
callListLength := integer(callList['count']);
ClearTable();
Utils.HideSpinner('spinner');
for i := 0 to data.Length - 1 do
begin
call := TJSObject(data[i]);
AddRowToTable(string(call['toNumber']), string(call['fromNumber']), string(call['dateCreated']),
string(call['duration']), string(call['transcription']), string(call['mediaUrl']));
end;
TotalPages := (callListLength + PageSize - 1) div PageSize;
if (PageNumber * PageSize) < callListLength then
begin
lblEntries.Caption := 'Showing entries ' + IntToStr((PageNumber - 1) * PageSize + 1) +
' - ' + IntToStr(PageNumber * PageSize) +
' of ' + IntToStr(callListLength);
end
else
begin
lblEntries.Caption := 'Showing entries ' + IntToStr((PageNumber - 1) * PageSize + 1) +
' - ' + IntToStr(callListLength) +
' of ' + IntToStr(callListLength);
end;
GeneratePagination(TotalPages);
end;
end;
procedure TFViewComplaints.btnApplyClick(Sender: TObject);
// Button that effectively functions as a GetCalls() button
var
searchOptions: string;
begin
PageNumber := 1;
PageSize := StrToInt(wcbPageSize.Text);
StartDate := dtpStartDate.Text;
EndDate := dtpEndDate.Text;
OrderBy := wcbSortBy.Text;
Caller := edtSearch.Text;
searchOptions := '&phonenumber=' + wcbLocation.Value +
'&pagenumber=' + IntToStr(PageNumber) +
'&pagesize=' + IntToStr(PageSize) +
'&startdate=' + StartDate +
'&enddate=' + EndDate +
'&orderby=' + OrderBy +
'&caller=' + Caller;
GetCalls(searchOptions);
end;
procedure TFViewComplaints.Search(searchOptions: string);
// Search method that searches the database for a specific phone number
var
xdcResponse: TXDataClientResponse;
callList : TJSObject;
i: integer;
data: TJSArray;
call: TJSObject;
callListLength: integer;
begin
if PageNumber > 0 then
begin
xdcResponse := await(XDataWebClient1.RawInvokeAsync('ILookupService.Search',
[searchOptions]));
callList := TJSObject(xdcResponse.Result);
data := TJSArray(callList['data']);
callListLength := integer(callList['count']);
ClearTable();
for i := 0 to data.Length - 1 do
begin
call := TJSObject(data[i]);
AddRowToTable(string(call['toNumber']), string(call['fromNumber']), string(call['dateCreated']),
string(call['duration']), string(call['transcription']), string(call['mediaUrl']));
end;
TotalPages := (callListLength + PageSize - 1) div PageSize;
lblEntries.Caption := 'Showing entries for phone number: ' + searchOptions;
end;
end;
procedure TFViewComplaints.btnSearchClick(Sender: TObject);
// calls Search method
begin
end;
procedure TFViewComplaints.ClearTable();
// clears the table
var
tbody: TJSHTMLElement;
begin
tbody := TJSHTMLElement(document.getElementById('tblPhoneGrid').getElementsByTagName('tbody')[0]);
tbody.innerHTML := '';
end;
function TFViewComplaints.GenerateSearchOptions(): string;
// Generates searchOptions for get calls.
var
searchOptions: string;
begin
PageSize := StrToInt(wcbPageSize.Text);
StartDate := dtpStartDate.Text;
EndDate := dtpEndDate.Text;
OrderBy := wcbSortBy.Text;
searchOptions := '&phonenumber=' + wcbLocation.Value +
'&pagenumber=' + IntToStr(PageNumber) +
'&pagesize=' + IntToStr(PageSize) +
'&startdate=' + StartDate +
'&enddate=' + EndDate +
'&orderby=' + OrderBy +
'&caller=' + Caller;
Result := searchOptions;
end;
end.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment