-
Notifications
You must be signed in to change notification settings - Fork 21
/
saved_search_migration.js
135 lines (121 loc) · 5.95 KB
/
saved_search_migration.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
const knex = require('./connection');
const db = require('.');
function log(msg) {
console.log(msg);
}
async function main() {
const isDryRun = process.argv.includes('--dry-run');
const dryRunStr = isDryRun ? 'DRY RUN :: ' : '';
module.exports.log(`${dryRunStr}Begin migrating legacy agency criteria to saved searches`);
await knex.transaction(async (trns) => {
const allAgencies = await trns('agencies').orderBy('id');
const allEligibilityCodesRes = await trns(`eligibility_codes`).orderBy('code');
const allEligibilityCodesStr = allEligibilityCodesRes.map((ec) => ec.code).toString();
const allEligibilityCodesByCode = {};
allEligibilityCodesRes.forEach((ec) => { allEligibilityCodesByCode[ec.code] = ec; });
/*
Example criteria:
{
"includeKeywords":"foo, bar",
"excludeKeywords":"baz, ball",
"opportunityNumber":null,
"opportunityStatuses":["posted"],
"fundingTypes":null,
"agency":null,
"bill":null,
"costSharing":null,
"opportunityCategories":[],
"postedWithin":[],
"eligibility":[
{"code":"01","enabled":false,"label":"County governments","agency_id":0},
{"code":"05","enabled":false,"label":"Independent school districts","agency_id":0},
{"code":"06","enabled":true,"label":"Public and State controlled institutions of higher education","agency_id":0},
{"code":"02","enabled":false,"label":"City or township governments","agency_id":0}
]
}
*/
const defaultCriteria = {
opportunityStatuses: ['posted'],
fundingTypes: null,
agency: null,
bill: null,
costSharing: null,
opportunityCategories: [],
postedWithin: [],
};
const agenciesToMigrate = {};
// Populate agenciesToMigrate with agencies that have legacy criteria
// agencies with non-default eligibility codes selections
// or agencies with include/exclude keywords
for (const agency of allAgencies) {
const legacyCriteria = await db.getAgencyCriteriaForAgency(agency.id); // eslint-disable-line no-await-in-loop
if (legacyCriteria.eligibilityCodes.toString() !== allEligibilityCodesStr || legacyCriteria.includeKeywords.length > 0 || legacyCriteria.excludeKeywords.length > 0) {
const newCriteria = {
...defaultCriteria,
includeKeywords: legacyCriteria.includeKeywords.join(', '),
excludeKeywords: legacyCriteria.excludeKeywords.join(', '),
eligibility: legacyCriteria.eligibilityCodes.map((ec) => allEligibilityCodesByCode[ec]),
};
const filters = db.formatSearchCriteriaToQueryFilters(JSON.stringify(newCriteria));
const validationErrors = db.validateSearchFilters(filters);
if (validationErrors.length > 0) {
module.exports.log(`${dryRunStr}Validation errors for agency ${agency.id}: ${validationErrors}. Not migrating.`);
} else {
agenciesToMigrate[agency.id] = {
criteria: newCriteria,
user_ids: [],
};
module.exports.log(`${dryRunStr}Migrating agency criteria for agency ${agency.id}`);
}
} else {
module.exports.log(`${dryRunStr}No agency criteria to migrate for agency ${agency.id}`);
}
}
// If agency belongs in the migration list, then populate its user_ids
const usersByAgencyId = await trns('users').select(knex.raw(`users.agency_id, array_agg(users.id) as user_ids`)).groupBy('users.agency_id').orderBy('users.agency_id');
for (const { agency_id, user_ids } of usersByAgencyId) {
if (agenciesToMigrate[agency_id] !== undefined) {
agenciesToMigrate[agency_id].user_ids = user_ids;
module.exports.log(`${dryRunStr}Migrating agency criteria for users ${user_ids} belonging to agency ${agency_id}`);
} else {
module.exports.log(`${dryRunStr}No agency criteria to migrate for users ${user_ids} belonging to agency ${agency_id}`);
}
}
// Build insert query for saved searches
const toInsert = [];
for (const [agency_id, details] of Object.entries(agenciesToMigrate)) {
if (details.user_ids.length === 0) {
module.exports.log(`${dryRunStr}No users to migrate for agency ${agency_id}`);
continue; // eslint-disable-line no-continue
}
for (const user_id of details.user_ids) {
toInsert.push(
{
created_by: user_id,
criteria: details.criteria,
name: `Legacy - Saved Search`,
},
);
}
}
// Perform insert if not dry run
if (!isDryRun) {
const res = await trns('grants_saved_searches')
.insert(toInsert)
.onConflict(knex.raw('ON CONSTRAINT grants_saved_searches_name_created_by_idx'))
.ignore()
.returning('id');
module.exports.log(`${dryRunStr}Inserted ${res.length} saved searches`);
} else {
module.exports.log(`${dryRunStr}Would have inserted approximately ${toInsert.length} saved searches. Note: there may be duplicates.`);
}
module.exports.log(`${dryRunStr}Done migrating legacy agency criteria to saved searches`);
});
}
module.exports = {
migrate_keywords_to_saved_search: main,
log,
};
if (require.main === module) {
main().then(() => process.exit(0));
}