• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OpenSRP / opensrp-client-immunization / #898

pending completion
#898

push

github-actions

web-flow
Merge pull request #195 from opensrp/check-vaccine-duplicates

Add Duplicate Vaccines & Recurring Service Record Checks

5076 of 6673 relevant lines covered (76.07%)

0.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.07
opensrp-immunization/src/main/java/org/smartregister/immunization/repository/RecurringServiceRecordRepository.java
1
package org.smartregister.immunization.repository;
2

3
import android.content.ContentValues;
4
import android.database.Cursor;
5

6
import net.sqlcipher.database.SQLiteDatabase;
7

8
import org.apache.commons.lang3.StringUtils;
9
import org.smartregister.immunization.domain.ServiceRecord;
10
import org.smartregister.repository.BaseRepository;
11
import org.smartregister.repository.EventClientRepository;
12

13
import java.text.ParseException;
14
import java.util.ArrayList;
15
import java.util.Calendar;
16
import java.util.Date;
17
import java.util.List;
18

19
import timber.log.Timber;
20

21
public class RecurringServiceRecordRepository extends BaseRepository {
1✔
22
    public static final String TABLE_NAME = "recurring_service_records";
23
    public static final String ID_COLUMN = "_id";
24
    public static final String BASE_ENTITY_ID = "base_entity_id";
25
    public static final String VALUE = "value";
26
    public static final String EVENT_ID = "event_id";
27
    public static final String FORMSUBMISSION_ID = "formSubmissionId";
28
    public static final String PROGRAM_CLIENT_ID = "program_client_id";
29
    public static final String RECURRING_SERVICE_ID = "recurring_service_id";
30
    public static final String DATE = "date";
31
    public static final String ANMID = "anmid";
32
    public static final String LOCATION_ID = "location_id";
33
    public static final String SYNC_STATUS = "sync_status";
34
    public static final String UPDATED_AT_COLUMN = "updated_at";
35
    public static final String CREATED_AT = "created_at";
36
    public static final String TEAM_ID = "team_id";
37
    public static final String TEAM = "team";
38
    public static final String CHILD_LOCATION_ID = "child_location_id";
39
    public static final String[] TABLE_COLUMNS = {ID_COLUMN, BASE_ENTITY_ID, PROGRAM_CLIENT_ID, RECURRING_SERVICE_ID, VALUE, DATE, ANMID, LOCATION_ID, CHILD_LOCATION_ID, TEAM, TEAM_ID, SYNC_STATUS, EVENT_ID, FORMSUBMISSION_ID, UPDATED_AT_COLUMN, CREATED_AT};
1✔
40
    public static final String RECURRING_SERVICE_ID_INDEX = "CREATE INDEX " + TABLE_NAME + "_" + RECURRING_SERVICE_ID + "_index ON " + TABLE_NAME + "(" + RECURRING_SERVICE_ID + ");";
41
    public static final String EVENT_ID_INDEX = "CREATE INDEX " + TABLE_NAME + "_" + EVENT_ID + "_index ON " + TABLE_NAME + "(" + EVENT_ID + " COLLATE NOCASE);";
42
    public static final String FORMSUBMISSION_INDEX = "CREATE INDEX " + TABLE_NAME + "_" + FORMSUBMISSION_ID + "_index ON " + TABLE_NAME + "(" + FORMSUBMISSION_ID + " COLLATE NOCASE);";
43
    public static final String ALTER_ADD_CREATED_AT_COLUMN = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + CREATED_AT + " DATETIME NULL ";
44
    public static final String UPDATE_TABLE_ADD_TEAM_COL = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + TEAM + " VARCHAR;";
45
    public static final String UPDATE_TABLE_ADD_TEAM_ID_COL = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + TEAM_ID + " VARCHAR;";
46
    public static final String UPDATE_TABLE_ADD_CHILD_LOCATION_ID_COL = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + CHILD_LOCATION_ID + " VARCHAR;";
47
    private static final String TAG = RecurringServiceRecordRepository.class.getCanonicalName();
1✔
48
    private static final String CREATE_TABLE_SQL = "CREATE TABLE recurring_service_records (_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,base_entity_id VARCHAR NOT NULL,program_client_id VARCHAR NULL,recurring_service_id INTERGER NOT NULL,value VARCHAR, date DATETIME NOT NULL,anmid VARCHAR NULL,location_id VARCHAR NULL,sync_status VARCHAR, event_id VARCHAR, formSubmissionId VARCHAR, updated_at INTEGER NULL, UNIQUE(base_entity_id, recurring_service_id) ON CONFLICT IGNORE)";
49
    private static final String BASE_ENTITY_ID_INDEX = "CREATE INDEX " + TABLE_NAME + "_" + BASE_ENTITY_ID + "_index ON " + TABLE_NAME + "(" + BASE_ENTITY_ID + " COLLATE NOCASE);";
50
    private static final String UPDATED_AT_INDEX = "CREATE INDEX " + TABLE_NAME + "_" + UPDATED_AT_COLUMN + "_index ON " + TABLE_NAME + "(" + UPDATED_AT_COLUMN + ");";
51
    public static final String UPDATE_TABLE_ADD_OUT_OF_AREA_COL = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + VaccineRepository.OUT_OF_AREA + " INTEGER;";
52
    public static final String UPDATE_TABLE_ADD_IS_VOIDED_COL = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + VaccineRepository.IS_VOIDED + " INTEGER;";
53

54
    public static void createTable(SQLiteDatabase database) {
55
        database.execSQL(CREATE_TABLE_SQL);
1✔
56
        database.execSQL(BASE_ENTITY_ID_INDEX);
1✔
57
        database.execSQL(RECURRING_SERVICE_ID_INDEX);
1✔
58
        database.execSQL(EVENT_ID_INDEX);
1✔
59
        database.execSQL(FORMSUBMISSION_INDEX);
1✔
60
        database.execSQL(UPDATED_AT_INDEX);
1✔
61
        database.execSQL(UPDATE_TABLE_ADD_OUT_OF_AREA_COL);
1✔
62
        database.execSQL(UPDATE_TABLE_ADD_IS_VOIDED_COL);
1✔
63
    }
1✔
64

65
    public static void migrateCreatedAt(SQLiteDatabase database) {
66
        try {
67
            String sql = "UPDATE " + TABLE_NAME +
1✔
68
                    " SET " + CREATED_AT + " = " +
69
                    " ( SELECT " + EventClientRepository.event_column.dateCreated.name() +
1✔
70
                    "   FROM " + EventClientRepository.Table.event.name() +
1✔
71
                    "   WHERE " + EventClientRepository.event_column.eventId.name() + " = " + TABLE_NAME + "." + EVENT_ID +
1✔
72
                    "   OR " + EventClientRepository.event_column.formSubmissionId
73
                    .name() + " = " + TABLE_NAME + "." + FORMSUBMISSION_ID +
1✔
74
                    " ) " +
75
                    " WHERE " + CREATED_AT + " is null ";
76
            database.execSQL(sql);
1✔
77
        } catch (Exception e) {
×
78
            Timber.e(e);
×
79
        }
1✔
80
    }
1✔
81

82
    public void add(ServiceRecord serviceRecord) {
83
        if (serviceRecord == null) {
1✔
84
            return;
1✔
85
        }
86

87
        try {
88
            if (StringUtils.isBlank(serviceRecord.getSyncStatus())) {
1✔
89
                serviceRecord.setSyncStatus(TYPE_Unsynced);
1✔
90
            }
91
            if (StringUtils.isBlank(serviceRecord.getFormSubmissionId())) {
1✔
92
                serviceRecord.setFormSubmissionId(generateRandomUUIDString());
1✔
93
            }
94

95
            if (serviceRecord.getUpdatedAt() == null) {
1✔
96
                serviceRecord.setUpdatedAt(Calendar.getInstance().getTimeInMillis());
1✔
97
            }
98

99
            SQLiteDatabase database = getWritableDatabase();
1✔
100
            if (serviceRecord.getId() == null) {
1✔
101
                ServiceRecord sameServiceRecord = findUnique(database, serviceRecord);
1✔
102
                if (sameServiceRecord != null) {
1✔
103
                    serviceRecord.setUpdatedAt(sameServiceRecord.getUpdatedAt());
×
104
                    serviceRecord.setId(sameServiceRecord.getId());
×
105
                    update(database, serviceRecord);
×
106
                } else {
107
                    if (serviceRecord.getCreatedAt() == null) {
1✔
108
                        serviceRecord.setCreatedAt(new Date());
1✔
109
                    }
110
                    serviceRecord.setId(database.insert(TABLE_NAME, null, createValuesFor(serviceRecord)));
1✔
111
                }
112
            } else {
1✔
113
                //mark the recurring service as unsynced for processing as an updated event
114
                serviceRecord.setSyncStatus(TYPE_Unsynced);
1✔
115
                update(database, serviceRecord);
1✔
116
            }
117
        } catch (Exception e) {
×
118
            Timber.e(e);
×
119
        }
1✔
120
    }
1✔
121

122
    public ServiceRecord findUnique(SQLiteDatabase database_, ServiceRecord serviceRecord) {
123
        SQLiteDatabase database = database_;
1✔
124
        if (serviceRecord == null || (StringUtils.isBlank(serviceRecord.getFormSubmissionId()) && StringUtils
1✔
125
                .isBlank(serviceRecord.getEventId()))) {
×
126
            return null;
1✔
127
        }
128

129
        try {
130
            if (database == null) {
1✔
131
                database = getReadableDatabase();
1✔
132
            }
133

134
            String selection = null;
1✔
135
            String[] selectionArgs = null;
1✔
136
            if (StringUtils.isNotBlank(serviceRecord.getFormSubmissionId()) && StringUtils
1✔
137
                    .isNotBlank(serviceRecord.getEventId())) {
1✔
138
                selection = FORMSUBMISSION_ID + " = ? " + COLLATE_NOCASE + " OR " + EVENT_ID + " = ? " + COLLATE_NOCASE;
1✔
139
                selectionArgs = new String[] {serviceRecord.getFormSubmissionId(), serviceRecord.getEventId()};
1✔
140
            } else if (StringUtils.isNotBlank(serviceRecord.getEventId())) {
1✔
141
                selection = EVENT_ID + " = ? " + COLLATE_NOCASE;
×
142
                selectionArgs = new String[] {serviceRecord.getEventId()};
×
143
            } else if (StringUtils.isNotBlank(serviceRecord.getFormSubmissionId())) {
1✔
144
                selection = FORMSUBMISSION_ID + " = ? " + COLLATE_NOCASE;
1✔
145
                selectionArgs = new String[] {serviceRecord.getFormSubmissionId()};
1✔
146
            }
147

148
            Cursor cursor = database
1✔
149
                    .query(TABLE_NAME, TABLE_COLUMNS, selection, selectionArgs, null, null, ID_COLUMN + " DESC ", null);
1✔
150
            List<ServiceRecord> serviceRecordList = readAllServiceRecords(cursor);
1✔
151
            if (!serviceRecordList.isEmpty()) {
1✔
152
                return serviceRecordList.get(0);
1✔
153
            }
154
        } catch (Exception e) {
1✔
155
            Timber.e(e);
1✔
156
        }
1✔
157

158
        return null;
1✔
159
    }
160

161
    public void update(SQLiteDatabase database_, ServiceRecord serviceRecord) {
162
        SQLiteDatabase database = database_;
1✔
163
        if (serviceRecord == null || serviceRecord.getId() == null) {
1✔
164
            return;
1✔
165
        }
166

167
        if (database == null) {
1✔
168
            database = getWritableDatabase();
1✔
169
        }
170

171
        try {
172
            String idSelection = ID_COLUMN + " = ?";
1✔
173
            database.update(TABLE_NAME, createValuesFor(serviceRecord), idSelection,
1✔
174
                    new String[] {serviceRecord.getId().toString()});
1✔
175
        } catch (Exception e) {
1✔
176
            Timber.e(e);
1✔
177
        }
1✔
178
    }
1✔
179

180
    private ContentValues createValuesFor(ServiceRecord serviceRecord) {
181
        ContentValues values = new ContentValues();
1✔
182
        values.put(ID_COLUMN, serviceRecord.getId());
1✔
183
        values.put(BASE_ENTITY_ID, serviceRecord.getBaseEntityId());
1✔
184
        values.put(PROGRAM_CLIENT_ID, serviceRecord.getProgramClientId());
1✔
185
        values.put(RECURRING_SERVICE_ID, serviceRecord.getRecurringServiceId());
1✔
186
        values.put(VALUE, serviceRecord.getValue());
1✔
187
        values.put(DATE, serviceRecord.getDate() != null ? serviceRecord.getDate().getTime() : null);
1✔
188
        values.put(ANMID, serviceRecord.getAnmId());
1✔
189
        values.put(LOCATION_ID, serviceRecord.getLocationId());
1✔
190
        values.put(TEAM, serviceRecord.getTeam());
1✔
191
        values.put(TEAM_ID, serviceRecord.getTeamId());
1✔
192
        values.put(CHILD_LOCATION_ID, serviceRecord.getChildLocationId());
1✔
193
        values.put(SYNC_STATUS, serviceRecord.getSyncStatus());
1✔
194
        values.put(EVENT_ID, serviceRecord.getEventId());
1✔
195
        values.put(FORMSUBMISSION_ID,
1✔
196
                serviceRecord.getFormSubmissionId());
1✔
197
        values.put(UPDATED_AT_COLUMN, serviceRecord.getUpdatedAt());
1✔
198
        values.put(CREATED_AT, serviceRecord.getCreatedAt() != null ? EventClientRepository.dateFormat
1✔
199
                .format(serviceRecord.getCreatedAt()) : null);
1✔
200
        return values;
1✔
201
    }
202

203
    private List<ServiceRecord> readAllServiceRecords(Cursor cursor) {
204
        List<ServiceRecord> serviceRecords = new ArrayList<>();
1✔
205

206
        try {
207

208
            if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) {
1✔
209
                while (!cursor.isAfterLast()) {
1✔
210

211
                    Date createdAt = null;
1✔
212
                    String dateCreatedString = cursor.getString(cursor.getColumnIndex(CREATED_AT));
1✔
213
                    if (StringUtils.isNotBlank(dateCreatedString)) {
1✔
214
                        try {
215
                            createdAt = EventClientRepository.dateFormat.parse(dateCreatedString);
1✔
216
                        } catch (ParseException e) {
1✔
217
                            Timber.e(e);
1✔
218
                        }
1✔
219
                    }
220
                    ServiceRecord serviceRecord = new ServiceRecord(cursor.getLong(cursor.getColumnIndex(ID_COLUMN)),
1✔
221
                            cursor.getString(cursor.getColumnIndex(BASE_ENTITY_ID)),
1✔
222
                            cursor.getString(cursor.getColumnIndex(PROGRAM_CLIENT_ID)),
1✔
223
                            cursor.getLong(cursor.getColumnIndex(RECURRING_SERVICE_ID)),
1✔
224
                            cursor.getString(cursor.getColumnIndex(VALUE)),
1✔
225
                            new Date(cursor.getLong(cursor.getColumnIndex(DATE))),
1✔
226
                            cursor.getString(cursor.getColumnIndex(ANMID)),
1✔
227
                            cursor.getString(cursor.getColumnIndex(LOCATION_ID)),
1✔
228
                            cursor.getString(cursor.getColumnIndex(SYNC_STATUS)),
1✔
229
                            cursor.getString(cursor.getColumnIndex(EVENT_ID)),
1✔
230
                            cursor.getString(cursor.getColumnIndex(FORMSUBMISSION_ID)),
1✔
231
                            cursor.getLong(cursor.getColumnIndex(UPDATED_AT_COLUMN)),
1✔
232
                            createdAt);
233

234

235
                    if (cursor.getColumnIndex(RecurringServiceTypeRepository.TYPE) > -1) {
1✔
236
                        String type = cursor.getString(cursor.getColumnIndex(RecurringServiceTypeRepository.TYPE));
1✔
237
                        if (type != null) {
1✔
238
                            type = removeHyphen(type);
1✔
239
                            serviceRecord.setType(type);
1✔
240
                        }
241
                    }
242

243
                    if (cursor.getColumnIndex(RecurringServiceTypeRepository.NAME) > -1) {
1✔
244
                        String name = cursor.getString(cursor.getColumnIndex(RecurringServiceTypeRepository.NAME));
1✔
245
                        if (name != null) {
1✔
246
                            name = removeHyphen(name);
1✔
247
                            serviceRecord.setName(name);
1✔
248
                        }
249
                    }
250

251
                    serviceRecord.setTeam(cursor.getString(cursor.getColumnIndex(TEAM)));
1✔
252
                    serviceRecord.setTeamId(cursor.getString(cursor.getColumnIndex(TEAM_ID)));
1✔
253
                    serviceRecord.setChildLocationId(cursor.getString(cursor.getColumnIndex(CHILD_LOCATION_ID)));
1✔
254
                    serviceRecords.add(serviceRecord);
1✔
255

256
                    cursor.moveToNext();
1✔
257
                }
1✔
258
            }
259
        } catch (Exception e) {
1✔
260
                Timber.e(e);
1✔
261
        } finally {
262
            if (cursor != null) {
1✔
263
                cursor.close();
1✔
264
            }
265
        }
266
        return serviceRecords;
1✔
267
    }
268

269
    public static String removeHyphen(String s) {
270
        if (StringUtils.isNotBlank(s)) {
1✔
271
            return s.replace("_", " ");
1✔
272
        }
273
        return s;
×
274
    }
275

276
    public List<ServiceRecord> findUnSyncedBeforeTime(int minutes) {
277
        List<ServiceRecord> serviceRecords = new ArrayList<>();
1✔
278
        Cursor cursor = null;
1✔
279
        try {
280
            Calendar calendar = Calendar.getInstance();
1✔
281
            calendar.add(Calendar.MINUTE, -minutes);
1✔
282

283
            Long time = calendar.getTimeInMillis();
1✔
284

285
            cursor = getReadableDatabase()
1✔
286
                    .query(TABLE_NAME, TABLE_COLUMNS, UPDATED_AT_COLUMN + " < ? AND " + SYNC_STATUS + " = ?",
1✔
287
                            new String[] {time.toString(), TYPE_Unsynced}, null, null, null, null);
1✔
288
            serviceRecords = readAllServiceRecords(cursor);
1✔
289
        } catch (Exception e) {
×
290
            Timber.e(e);
×
291
        } finally {
292
            if (cursor != null) {
1✔
293
                cursor.close();
1✔
294
            }
295
        }
296
        return serviceRecords;
1✔
297
    }
298

299
    public List<ServiceRecord> findUnSynced() {
300
        List<ServiceRecord> serviceRecords = new ArrayList<>();
×
301
        Cursor cursor = null;
×
302
        try {
303
            cursor = getReadableDatabase()
×
304
                    .query(TABLE_NAME, TABLE_COLUMNS, SYNC_STATUS + " = ?",
×
305
                            new String[] {TYPE_Unsynced}, null, null, null, null);
306
            serviceRecords = readAllServiceRecords(cursor);
×
307
        } catch (Exception e) {
×
308
            Timber.e(e);
×
309
        } finally {
310
            if (cursor != null) {
×
311
                cursor.close();
×
312
            }
313
        }
314
        return serviceRecords;
×
315
    }
316

317
    public List<ServiceRecord> findByEntityId(String entityId) {
318
        SQLiteDatabase database = getReadableDatabase();
1✔
319
        String sql = " SELECT " + TABLE_NAME + ".*, " + RecurringServiceTypeRepository.TABLE_NAME + ".name, " + RecurringServiceTypeRepository.TABLE_NAME + ".type FROM " + TABLE_NAME + " LEFT JOIN " + RecurringServiceTypeRepository.TABLE_NAME +
1✔
320
                " ON " + TABLE_NAME + "." + RECURRING_SERVICE_ID + " = " + RecurringServiceTypeRepository.TABLE_NAME + "." + RecurringServiceTypeRepository.ID_COLUMN +
321
                " WHERE " + TABLE_NAME + "." + BASE_ENTITY_ID + " = ? " + COLLATE_NOCASE + " ORDER BY " + TABLE_NAME + "." + UPDATED_AT_COLUMN;
322
        Cursor cursor = database.rawQuery(sql, new String[] {entityId});
1✔
323
        return readAllServiceRecords(cursor);
1✔
324
    }
325

326
    public void deleteServiceRecord(Long caseId) {
327
        try {
328
            ServiceRecord serviceRecord = find(caseId);
1✔
329
            if (serviceRecord != null) {
1✔
330
                getWritableDatabase().delete(TABLE_NAME, ID_COLUMN + "= ?", new String[] {caseId.toString()});
1✔
331
            }
332
        } catch (Exception e) {
×
333
            Timber.e(e);
×
334
        }
1✔
335
    }
1✔
336

337
    public ServiceRecord find(Long caseId) {
338
        ServiceRecord serviceRecord = null;
1✔
339
        Cursor cursor = null;
1✔
340
        try {
341
            cursor = getReadableDatabase()
1✔
342
                    .query(TABLE_NAME, TABLE_COLUMNS, ID_COLUMN + " = ?", new String[] {caseId.toString()}, null, null, null,
1✔
343
                            null);
344
            List<ServiceRecord> serviceRecords = readAllServiceRecords(cursor);
1✔
345
            if (!serviceRecords.isEmpty()) {
1✔
346
                serviceRecord = serviceRecords.get(0);
×
347
            }
348
        } catch (Exception e) {
1✔
349
            Timber.e(e);
1✔
350
        } finally {
351
            if (cursor != null) {
1✔
352
                cursor.close();
1✔
353
            }
354
        }
355
        return serviceRecord;
1✔
356
    }
357

358
    public ServiceRecord findByBaseEntityIdAndRecurringServiceId(String baseEntityId, Long recurringServiceId) {
359
        ServiceRecord serviceRecord = null;
1✔
360
        Cursor cursor = null;
1✔
361

362
        try {
363
            cursor = getReadableDatabase().query(
1✔
364
                    TABLE_NAME,
365
                    TABLE_COLUMNS,
366
                    BASE_ENTITY_ID + " = ? AND " + RECURRING_SERVICE_ID + " = ?",
367
                    new String[]{baseEntityId, String.valueOf(recurringServiceId)},
1✔
368
                    null,
369
                    null,
370
                    null,
371
                    null
372
            );
373

374
            List<ServiceRecord> serviceRecords = readAllServiceRecords(cursor);
1✔
375
            if (!serviceRecords.isEmpty()) {
1✔
376
                serviceRecord = serviceRecords.get(0);
1✔
377
            }
378
        } catch (Exception e) {
1✔
379
            Timber.e(e);
1✔
380
        } finally {
381
            if (cursor != null) {
1✔
382
                cursor.close();
1✔
383
            }
384
        }
385
        return serviceRecord;
1✔
386
    }
387

388
    public void close(Long caseId) {
389
        try {
390
            ContentValues values = new ContentValues();
1✔
391
            values.put(SYNC_STATUS, TYPE_Synced);
1✔
392
            getWritableDatabase().update(TABLE_NAME, values, ID_COLUMN + " = ?", new String[] {caseId.toString()});
1✔
393
        } catch (Exception e) {
1✔
394
            Timber.e(e);
1✔
395
        }
1✔
396
    }
1✔
397
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc