Skip to content

Commit 92b3f20

Browse files
committed
fix: add the range to sync
1 parent a08912e commit 92b3f20

2 files changed

Lines changed: 121 additions & 26 deletions

File tree

src/main/environment/common_example.properties

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ cron-scheduler-everwelldatasync=0 0/5 * * * ? *
9595
start-nhmdashboard-scheduler=true
9696
cron-scheduler-nhmdashboard=0 1 0 * * ? *
9797
nhm-detailedcallreport-backfill-days=7
98+
# one-off recovery of older / partly imported days (yyyy-MM-dd, both inclusive,
99+
# max 60 days per run). Leave empty during normal operation - while these are set
100+
# the job pulls this range instead of only the missing days.
101+
nhm-detailedcallreport-backfill-start-date=
102+
nhm-detailedcallreport-backfill-end-date=
98103
##----------------------------------------------------#grievance data sync-----------------------------------------------------------
99104

100105
start-grievancedatasync-scheduler=false

src/main/java/com/iemr/common/service/nhm_dashboard/NHM_DashboardServiceImpl.java

Lines changed: 116 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,14 @@ public class NHM_DashboardServiceImpl implements NHM_DashboardService {
7575
@Value("${nhm-detailedcallreport-backfill-days:7}")
7676
private int detailedCallReportBackfillDays;
7777

78+
@Value("${nhm-detailedcallreport-backfill-start-date:}")
79+
private String backfillStartDate;
80+
81+
@Value("${nhm-detailedcallreport-backfill-end-date:}")
82+
private String backfillEndDate;
83+
84+
private static final int MAX_EXPLICIT_BACKFILL_DAYS = 60;
85+
7886
public String pushAbandonCalls(AbandonCallSummary abandonCallSummary) throws Exception {
7987

8088
logger.info("NHM_abandon call push API request : " + abandonCallSummary.toString());
@@ -129,7 +137,6 @@ public String getDetailedCallReport() throws Exception {
129137
return new Gson().toJson(resultSet);
130138
}
131139

132-
// JOB calling C-Zentrix 2 APIs => AgentSummaryReport & DetailedCallReport
133140
public String pull_NHM_Data_CTI() throws IEMRException {
134141
String response = "";
135142
String result1 = "";
@@ -145,14 +152,12 @@ public String pull_NHM_Data_CTI() throws IEMRException {
145152
}
146153

147154
StringBuilder detailedCallReportResult = new StringBuilder();
148-
// each pending day is pulled separately, so that one failing day does not stop
149-
// the remaining days
150155
for (LocalDate callDate : getPendingDetailedCallReportDates()) {
151156
try {
152157
List<DetailedCallReport> detailedCallReportList = callDetailedCallReportCTI_API(callDate);
153158
if (detailedCallReportList.size() > 0) {
154159
detailedCallReportResult.append(callDate).append(" : ")
155-
.append(saveDetailedCallReport(detailedCallReportList)).append("; ");
160+
.append(saveNewDetailedCallReport(detailedCallReportList, callDate)).append("; ");
156161
}
157162
} catch (Exception e) {
158163
logger.error("DetailedCallReport pull failed for " + callDate + " - " + e.getLocalizedMessage());
@@ -163,42 +168,115 @@ public String pull_NHM_Data_CTI() throws IEMRException {
163168
return response.concat(result1).concat(" ").concat(result2);
164169
}
165170

166-
/**
167-
* Days (oldest first) for which detailed call report data still has to be
168-
* pulled from CTI - yesterday plus any earlier day within the backfill window
169-
* that has no data at all. Without this, a day missed because CTI was down or
170-
* throttled ("Please wait for 1 hour") was never requested again and stayed
171-
* permanently missing from the report.
172-
*/
171+
173172
List<LocalDate> getPendingDetailedCallReportDates() {
174-
LocalDate lastDate = LocalDate.now().minusDays(1);
173+
LocalDate yesterday = LocalDate.now().minusDays(1);
174+
175+
LocalDate explicitStart = parseBackfillDate(backfillStartDate, "start");
176+
LocalDate explicitEnd = parseBackfillDate(backfillEndDate, "end");
177+
if (explicitStart != null) {
178+
LocalDate lastDate = explicitEnd != null ? explicitEnd : yesterday;
179+
// today is still in progress, never pull it
180+
if (lastDate.isAfter(yesterday))
181+
lastDate = yesterday;
182+
if (lastDate.isBefore(explicitStart)) {
183+
logger.error("Configured detailed call report backfill range is empty - start " + explicitStart
184+
+ " is after end " + lastDate + ", falling back to the missing day check");
185+
} else {
186+
List<LocalDate> explicitDates = new ArrayList<>();
187+
for (LocalDate date = explicitStart; !date.isAfter(lastDate); date = date.plusDays(1)) {
188+
if (explicitDates.size() >= MAX_EXPLICIT_BACKFILL_DAYS) {
189+
logger.warn("Configured detailed call report backfill range exceeds "
190+
+ MAX_EXPLICIT_BACKFILL_DAYS + " days - stopping at " + date.minusDays(1)
191+
+ ", move the start date forward and run again to continue");
192+
break;
193+
}
194+
explicitDates.add(date);
195+
}
196+
logger.info("DetailedCallReport configured backfill range " + explicitStart + " to " + lastDate
197+
+ " - pulling " + explicitDates.size() + " day(s)");
198+
return explicitDates;
199+
}
200+
}
201+
175202
int lookBackDays = detailedCallReportBackfillDays > 0 ? detailedCallReportBackfillDays : 1;
176-
LocalDate firstDate = lastDate.minusDays(lookBackDays - 1L);
203+
LocalDate firstDate = yesterday.minusDays(lookBackDays - 1L);
177204

178205
Set<LocalDate> existingDates = new HashSet<>();
179206
try {
180207
List<java.sql.Date> dates = detailedCallReportRepo.findExistingCallDates(
181208
Timestamp.valueOf(firstDate.atStartOfDay()),
182-
Timestamp.valueOf(lastDate.atTime(LocalTime.MAX).withNano(0)));
209+
Timestamp.valueOf(yesterday.atTime(LocalTime.MAX).withNano(0)));
183210
for (java.sql.Date date : dates) {
184211
if (date != null)
185212
existingDates.add(date.toLocalDate());
186213
}
187214
} catch (Exception e) {
188215
// on any problem in gap detection, fall back to the previous behaviour
189216
logger.error("Error while detecting missing detailed call report dates - " + e.getLocalizedMessage());
190-
return Arrays.asList(lastDate);
217+
return Arrays.asList(yesterday);
191218
}
192219

193220
List<LocalDate> pendingDates = new ArrayList<>();
194-
for (LocalDate date = firstDate; !date.isAfter(lastDate); date = date.plusDays(1)) {
221+
for (LocalDate date = firstDate; !date.isAfter(yesterday); date = date.plusDays(1)) {
195222
if (!existingDates.contains(date))
196223
pendingDates.add(date);
197224
}
198-
logger.info("DetailedCallReport pending dates between " + firstDate + " and " + lastDate + " : " + pendingDates);
225+
logger.info("DetailedCallReport pending dates between " + firstDate + " and " + yesterday + " : " + pendingDates);
199226
return pendingDates;
200227
}
201228

229+
private LocalDate parseBackfillDate(String value, String label) {
230+
if (value == null || value.trim().isEmpty())
231+
return null;
232+
try {
233+
return LocalDate.parse(value.trim());
234+
} catch (Exception e) {
235+
logger.error("Ignoring detailed call report backfill " + label + " date '" + value
236+
+ "' - expected format yyyy-MM-dd");
237+
return null;
238+
}
239+
}
240+
241+
String saveNewDetailedCallReport(List<DetailedCallReport> detailedCallReportList, LocalDate callDate)
242+
throws IEMRException {
243+
parseDetailedCallReportTimestamps(detailedCallReportList);
244+
245+
Set<String> existingKeys = new HashSet<>();
246+
for (DetailedCallReport existing : detailedCallReportRepo.findByCallStartTimeBetween(
247+
Timestamp.valueOf(callDate.atStartOfDay()),
248+
Timestamp.valueOf(callDate.atTime(LocalTime.MAX).withNano(0)))) {
249+
existingKeys.add(getDetailedCallReportKey(existing));
250+
}
251+
252+
List<DetailedCallReport> newRecords = new ArrayList<>();
253+
for (DetailedCallReport detailedCallReport : detailedCallReportList) {
254+
if (existingKeys.add(getDetailedCallReportKey(detailedCallReport)))
255+
newRecords.add(detailedCallReport);
256+
}
257+
258+
int duplicates = detailedCallReportList.size() - newRecords.size();
259+
if (newRecords.isEmpty()) {
260+
logger.info("DetailedCallReport " + callDate + " - all " + detailedCallReportList.size()
261+
+ " record(s) already present, nothing to save");
262+
return "0 records saved, " + duplicates + " already present";
263+
}
264+
265+
List<DetailedCallReport> resultSet = (List<DetailedCallReport>) detailedCallReportRepo.saveAll(newRecords);
266+
logger.info("DetailedCallReport " + callDate + " - pulled " + detailedCallReportList.size() + ", saved "
267+
+ resultSet.size() + ", already present " + duplicates);
268+
return resultSet.size() + " records saved, " + duplicates + " already present";
269+
}
270+
271+
/**
272+
* Natural key of a call record. A session can hold more than one leg (transfer,
273+
* redial), so the phone number and start time are part of the key as well.
274+
*/
275+
private String getDetailedCallReportKey(DetailedCallReport detailedCallReport) {
276+
return String.valueOf(detailedCallReport.getSession_ID()) + '|' + detailedCallReport.getPHONE() + '|'
277+
+ detailedCallReport.getCallStartTime() + '|' + detailedCallReport.getAgent_ID();
278+
}
279+
202280
public String saveAgentSummaryReport(List<AgentSummaryReport> agentSummaryReportList) throws IEMRException {
203281

204282
List<AgentSummaryReport> resultSet = (List<AgentSummaryReport>) agentSummaryReportRepo
@@ -210,7 +288,26 @@ public String saveAgentSummaryReport(List<AgentSummaryReport> agentSummaryReport
210288
public String saveDetailedCallReport(List<DetailedCallReport> detailedCallReportList) throws IEMRException {
211289

212290
if (detailedCallReportList != null && detailedCallReportList.size() > 0) {
213-
for (DetailedCallReport detailedCallReport : detailedCallReportList) {
291+
parseDetailedCallReportTimestamps(detailedCallReportList);
292+
293+
List<DetailedCallReport> resultSet = (List<DetailedCallReport>) detailedCallReportRepo
294+
.saveAll(detailedCallReportList);
295+
296+
return resultSet.size() + " detailedCallReport records saved successfully";
297+
} else
298+
throw new IEMRException("please pass valid DetailedCallReport data in list");
299+
}
300+
301+
/**
302+
* CTI sends the times as strings; they are moved into the timestamp columns
303+
* here. Has to run before the records are compared against what is already
304+
* stored, because the comparison uses the parsed start time.
305+
*/
306+
private void parseDetailedCallReportTimestamps(List<DetailedCallReport> detailedCallReportList) {
307+
if (detailedCallReportList == null)
308+
return;
309+
310+
for (DetailedCallReport detailedCallReport : detailedCallReportList) {
214311
try {
215312
if (detailedCallReport.getCall_Start_Time() != null
216313
&& !detailedCallReport.getCall_Start_Time().equalsIgnoreCase("0000-00-00 00:00:00"))
@@ -244,14 +341,7 @@ public String saveDetailedCallReport(List<DetailedCallReport> detailedCallReport
244341
} catch (Exception e) {
245342
logger.error("Call_Start_Time" + e.getLocalizedMessage());
246343
}
247-
}
248-
249-
List<DetailedCallReport> resultSet = (List<DetailedCallReport>) detailedCallReportRepo
250-
.saveAll(detailedCallReportList);
251-
252-
return resultSet.size() + " detailedCallReport records saved successfully";
253-
} else
254-
throw new IEMRException("please pass valid DetailedCallReport data in list");
344+
}
255345
}
256346

257347
public List<AgentSummaryReport> callAgentSummaryReportCTI_API() throws IEMRException {

0 commit comments

Comments
 (0)