Skip to content

Commit be96fe4

Browse files
committed
Count deleted datasets and properties
1 parent 3a27bad commit be96fe4

File tree

2 files changed

+105
-5
lines changed

2 files changed

+105
-5
lines changed

lib/model/query/analytics.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,21 @@ FROM audits WHERE action = 'entity.bulk.delete'`);
742742
const getProjectsWithDescriptions = () => ({ all }) => all(sql`
743743
select id as "projectId", length(trim(description)) as description_length from projects where coalesce(trim(description),'')!=''`);
744744

745+
// Group deleted dataset and properties by project (by tracing actee ID parent)
746+
// Datasets can be deleted and purged so we can't look at their project IDs.
747+
// Projects can be archived but not deleted so this query should be fine.
748+
const countDeletedDatasetAndPropertiesByProject = () => ({ all }) =>
749+
all(sql`SELECT
750+
projects.id as "projectId",
751+
SUM(CASE WHEN audits.action = 'dataset.delete' THEN 1 ELSE 0 END) AS dataset_count,
752+
SUM(CASE WHEN audits.action = 'dataset.update.property.delete' THEN 1 ELSE 0 END) AS property_count
753+
FROM audits
754+
JOIN actees ON audits."acteeId" = actees."id"
755+
JOIN projects ON actees."parent" = projects."acteeId"
756+
WHERE audits.action IN ('dataset.delete', 'dataset.update.property.delete')
757+
AND actees.species = 'dataset'
758+
GROUP BY projects.id`);
759+
745760
const projectMetrics = () => (({ Analytics }) => runSequentially([
746761
Analytics.countUsersPerRole,
747762
Analytics.countAppUsers,
@@ -760,14 +775,15 @@ const projectMetrics = () => (({ Analytics }) => runSequentially([
760775
Analytics.countSubmissionsComments,
761776
Analytics.countSubmissionsByUserType,
762777
Analytics.getProjectsWithDescriptions,
778+
Analytics.countDeletedDatasetAndPropertiesByProject,
763779
Analytics.getDatasets,
764780
Analytics.getDatasetEvents,
765781
Analytics.getDatasetProperties,
766782
Analytics.countEntitiesWithGeometry
767783
]).then(([ userRoles, appUsers, deviceIds, pubLinks,
768784
forms, formGeoRepeats, formsEncrypt, formStates, webforms, reusedIds, entityForms,
769785
subs, subStates, subEdited, subComments, subUsers,
770-
projWithDesc, datasets, datasetEvents, datasetProperties, entitiesWithGeometry ]) => {
786+
projWithDesc, deletedDatasetCounts, datasets, datasetEvents, datasetProperties, entitiesWithGeometry ]) => {
771787
const projects = {};
772788

773789
// users
@@ -935,6 +951,12 @@ const projectMetrics = () => (({ Analytics }) => runSequentially([
935951
project.other.description_length = row.description_length;
936952
}
937953

954+
for (const row of deletedDatasetCounts) {
955+
const project = _getProject(projects, row.projectId);
956+
project.other.num_datasets_deleted = row.dataset_count;
957+
project.other.num_dataset_properties_deleted = row.property_count;
958+
}
959+
938960
// Order projects by ID
939961
const projArray = Object.entries(projects).sort((a, b) => a[0] - b[0]).map((k) => k[1]);
940962
return projArray;
@@ -1115,5 +1137,6 @@ module.exports = {
11151137
countEntitiesWithGeometry,
11161138
countMultiEntityErrors,
11171139
countEntityBulkDeletes,
1118-
checkLoginCustomization
1140+
checkLoginCustomization,
1141+
countDeletedDatasetAndPropertiesByProject
11191142
};

test/integration/other/analytics-queries.js

Lines changed: 80 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2498,6 +2498,65 @@ describe('analytics task queries', function () {
24982498
const res = await container.Analytics.getProjectsWithDescriptions();
24992499
res.should.eql([{ projectId: 1, description_length: 9 }, { projectId: projWithDesc, description_length: 13 }]);
25002500
}));
2501+
2502+
it('should count number of datasets and properties deleted in each project', testService(async (service, { Analytics, Datasets }) => {
2503+
const asAlice = await service.login('alice');
2504+
2505+
// Create a dataset
2506+
await asAlice.post('/v1/projects/1/datasets')
2507+
.send({ name: 'trees' })
2508+
.expect(200);
2509+
2510+
await asAlice.post('/v1/projects/1/datasets/trees/properties')
2511+
.send({ name: 'height' })
2512+
.expect(200);
2513+
2514+
// Create another dataset
2515+
await asAlice.post('/v1/projects/1/datasets')
2516+
.send({ name: 'people' })
2517+
.expect(200);
2518+
2519+
// Delete the property
2520+
await asAlice.delete('/v1/projects/1/datasets/trees/properties/height')
2521+
.expect(200);
2522+
2523+
// Delete the datasets
2524+
await asAlice.delete('/v1/projects/1/datasets/trees')
2525+
.expect(200);
2526+
2527+
await asAlice.delete('/v1/projects/1/datasets/people')
2528+
.expect(200);
2529+
2530+
// Create another project
2531+
const newProjectId = await asAlice.post('/v1/projects')
2532+
.set('Content-Type', 'application/json')
2533+
.send({ name: 'Test Project' })
2534+
.expect(200)
2535+
.then(({ body }) => body.id);
2536+
2537+
// Create a dataset in new project to be deleted
2538+
await asAlice.post(`/v1/projects/${newProjectId}/datasets`)
2539+
.send({ name: 'people' })
2540+
.expect(200);
2541+
2542+
// Delete the dataset
2543+
await asAlice.delete(`/v1/projects/${newProjectId}/datasets/people`)
2544+
.expect(200);
2545+
2546+
// Force purge dataset in new project
2547+
await Datasets.purge(true, newProjectId);
2548+
2549+
const res = await Analytics.countDeletedDatasetAndPropertiesByProject();
2550+
for (const row in res) {
2551+
if (row.projectId === 1) {
2552+
row.dataset_count.should.equal(2);
2553+
row.property_count.should.equal(1);
2554+
} else if (row.projectId === newProjectId) {
2555+
row.dataset_count.should.equal(1);
2556+
row.property_count.should.equal(0);
2557+
}
2558+
}
2559+
}));
25012560
});
25022561

25032562
describe('combined analytics', () => {
@@ -2833,9 +2892,27 @@ describe('analytics task queries', function () {
28332892
}));
28342893

28352894
it('should fill in all project.other queries', testService(async (service, container) => {
2836-
await service.login('alice', (asAlice) =>
2837-
asAlice.patch('/v1/projects/1')
2838-
.send({ description: 'test desc' }));
2895+
const asAlice = await service.login('alice');
2896+
2897+
await asAlice.patch('/v1/projects/1')
2898+
.send({ description: 'test desc' });
2899+
2900+
// delete datasets and properties
2901+
await asAlice.post('/v1/projects/1/datasets')
2902+
.send({ name: 'trees' })
2903+
.expect(200);
2904+
2905+
await asAlice.post('/v1/projects/1/datasets/trees/properties')
2906+
.send({ name: 'height' })
2907+
.expect(200);
2908+
2909+
// Delete the property
2910+
await asAlice.delete('/v1/projects/1/datasets/trees/properties/height')
2911+
.expect(200);
2912+
2913+
// Delete the dataset
2914+
await asAlice.delete('/v1/projects/1/datasets/trees')
2915+
.expect(200);
28392916

28402917
const res = await container.Analytics.previewMetrics();
28412918

0 commit comments

Comments
 (0)