@servicetitan/acquisition-functions 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # @servicetitan/acquisition-functions
2
+
3
+ ### Synopsis
4
+
5
+ This repo contains Marketing Acquisition properties & filters converters for MongoDB Atlas Functions
6
+
7
+ ### Usage in functions
8
+
9
+ Example:
10
+
11
+ ```js
12
+ const { configureProcessor } = require('@servicetitan/acquisition-functions');
13
+
14
+ const { filterFetcher } = configureProcessor({
15
+ workers: 10,
16
+ mongoDb: mongoDbConnectionToDb,
17
+ });
18
+
19
+ exports = async () => {
20
+ return filterFetcher.start('000000-999999');
21
+ };
22
+ ```
23
+
24
+ ### Run tests
25
+
26
+ Locally:
27
+
28
+ ```sh
29
+ docker-compose up -d mongo
30
+ npm run test
31
+ ```
32
+
33
+ In Docker:
34
+
35
+ ```sh
36
+ # In root FE directory
37
+ lerna run build --scope=@servicetitan/acquisition-functions
38
+
39
+ # In package directory
40
+ docker-compose up
41
+ ```
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const globals_1 = require("@jest/globals");
4
+ const index_1 = require("../index");
5
+ const setup_1 = require("./setup");
6
+ describe('property-assessorlastsaledate-converter', () => {
7
+ let processor;
8
+ let base;
9
+ (0, globals_1.beforeAll)(async () => {
10
+ const client = await (0, setup_1.openConnection)();
11
+ base = client.db('test');
12
+ });
13
+ describe('processor', () => {
14
+ (0, globals_1.beforeEach)(async () => {
15
+ await (0, setup_1.setupProperties)(base);
16
+ processor = (0, index_1.configureProcessor)({
17
+ workers: 1,
18
+ mongoDb: base,
19
+ });
20
+ const { assessorLastSaleDateProcessor } = processor;
21
+ await assessorLastSaleDateProcessor.start('90001');
22
+ });
23
+ (0, globals_1.test)('should convert existing field to date', async () => {
24
+ const result = await new Promise(resolve => {
25
+ base.collection('properties')
26
+ .find({
27
+ assessorlastsaledate: {
28
+ $exists: true,
29
+ },
30
+ })
31
+ .toArray((err, res) => {
32
+ resolve(res);
33
+ });
34
+ });
35
+ (0, globals_1.expect)(result[0].assessorlastsaledate).toBeInstanceOf(Date);
36
+ });
37
+ (0, globals_1.test)('should not touch nulls', async () => {
38
+ const result = await new Promise(resolve => {
39
+ base.collection('properties')
40
+ .find({
41
+ assessorlastsaledate: null,
42
+ })
43
+ .toArray((err, res) => {
44
+ resolve(res);
45
+ });
46
+ });
47
+ (0, globals_1.expect)(result[0].assessorlastsaledate).toBe(null);
48
+ });
49
+ });
50
+ describe('aggregate', () => {
51
+ (0, globals_1.beforeEach)(async () => {
52
+ await (0, setup_1.setupProperties)(base);
53
+ await (0, index_1.propertiesAssessorLastSaleDateAggregate)(base);
54
+ });
55
+ (0, globals_1.test)('should convert existing field to date', async () => {
56
+ const result = await new Promise(resolve => {
57
+ base.collection('properties')
58
+ .find({
59
+ assessorlastsaledate: {
60
+ $exists: true,
61
+ },
62
+ })
63
+ .toArray((err, res) => {
64
+ resolve(res);
65
+ });
66
+ });
67
+ (0, globals_1.expect)(result[0].assessorlastsaledate).toBeInstanceOf(Date);
68
+ });
69
+ (0, globals_1.test)('should not touch nulls', async () => {
70
+ const result = await new Promise(resolve => {
71
+ base.collection('properties')
72
+ .find({
73
+ assessorlastsaledate: null,
74
+ })
75
+ .toArray((err, res) => {
76
+ resolve(res);
77
+ });
78
+ });
79
+ (0, globals_1.expect)(result[0].assessorlastsaledate).toBe(null);
80
+ });
81
+ });
82
+ afterAll(async () => {
83
+ await (0, setup_1.closeConnection)();
84
+ });
85
+ });
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const globals_1 = require("@jest/globals");
4
+ const index_1 = require("../index");
5
+ const setup_1 = require("./setup");
6
+ describe('property-assessorlastsaledate-converter', () => {
7
+ let processor;
8
+ let base;
9
+ (0, globals_1.beforeAll)(async () => {
10
+ const client = await (0, setup_1.openConnection)();
11
+ base = client.db('test');
12
+ await (0, setup_1.setupProperties)(base);
13
+ processor = (0, index_1.configureProcessor)({
14
+ workers: 10,
15
+ mongoDb: base,
16
+ });
17
+ const { filterFetcher, propertyUseGroupProcessor } = processor;
18
+ await propertyUseGroupProcessor.start('90001');
19
+ await filterFetcher.start('90001-90002');
20
+ });
21
+ (0, globals_1.test)('should have empty filter for missing zip', async () => {
22
+ const result = await base.collection('filters').findOne({
23
+ zipcode: '90002',
24
+ });
25
+ (0, globals_1.expect)(result).not.toBe(null);
26
+ (0, globals_1.expect)(result?.propertyusegroup.length).toBe(0);
27
+ (0, globals_1.expect)(result?.flooringmaterialprimary.length).toBe(0);
28
+ (0, globals_1.expect)(result?.hvaccoolingdetail.length).toBe(0);
29
+ (0, globals_1.expect)(result?.hvacheatingdetail.length).toBe(0);
30
+ (0, globals_1.expect)(result?.propertyusestandardized.length).toBe(0);
31
+ (0, globals_1.expect)(result?.utilitieswatersource.length).toBe(0);
32
+ });
33
+ (0, globals_1.test)('should form correct filter', async () => {
34
+ const result = await base.collection('filters').findOne({
35
+ zipcode: '90001',
36
+ });
37
+ (0, globals_1.expect)(result).toMatchObject({
38
+ zipcode: '90001',
39
+ propertyusegroup: [
40
+ {
41
+ name: 'COMMERCIAL',
42
+ count: 2,
43
+ },
44
+ ],
45
+ propertyusestandardized: [
46
+ {
47
+ name: 'APARTMENT HOUSE (5+ UNITS)',
48
+ groupName: 'COMMERCIAL',
49
+ count: 1,
50
+ },
51
+ {
52
+ name: 'AUTO REPAIR, GARAGE',
53
+ groupName: 'COMMERCIAL',
54
+ count: 1,
55
+ },
56
+ ],
57
+ hvaccoolingdetail: [
58
+ {
59
+ name: 'YES',
60
+ count: 1,
61
+ },
62
+ ],
63
+ hvacheatingdetail: [
64
+ {
65
+ name: 'NONE',
66
+ count: 2,
67
+ },
68
+ ],
69
+ utilitieswatersource: [],
70
+ flooringmaterialprimary: [],
71
+ });
72
+ });
73
+ (0, globals_1.test)('should have single group with captial-case', async () => {
74
+ const result = await base.collection('filters').findOne({
75
+ zipcode: '90001',
76
+ });
77
+ (0, globals_1.expect)(result).not.toBe(null);
78
+ (0, globals_1.expect)(result?.propertyusegroup.length).toBe(1);
79
+ (0, globals_1.expect)(result?.propertyusegroup[0].name).toBe('COMMERCIAL');
80
+ (0, globals_1.expect)(result?.propertyusestandardized.every(({ groupName }) => groupName === 'COMMERCIAL')).toBeTruthy();
81
+ });
82
+ afterAll(async () => {
83
+ await (0, setup_1.closeConnection)();
84
+ });
85
+ });
@@ -0,0 +1,458 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MOCK = void 0;
4
+ /* eslint-disable */
5
+ exports.MOCK = [
6
+ {
7
+ attomid: 154817956,
8
+ fulladdress: '7310 S Central Ave Los Angeles CA 90001',
9
+ addressstreet: '7310 S Central Ave',
10
+ addressunit: ' ',
11
+ primarynumber: 7310,
12
+ streetname: 'Central',
13
+ streetpredirection: 'S',
14
+ streetpostdirection: '',
15
+ streetsuffix: 'Ave',
16
+ secondarynumber: '',
17
+ secondarydesignator: '',
18
+ extrasecondarynumber: '',
19
+ extrasecondarydesignator: '',
20
+ cityname: 'Los Angeles',
21
+ stateabbreviation: 'CA',
22
+ zipcode: '90001',
23
+ latitude: 33.97346,
24
+ longitude: -118.25645,
25
+ situsstatecode: 'CA',
26
+ situscounty: 'Los Angeles',
27
+ propertyjurisdictionname: 'LOS ANGELES',
28
+ situsstatecountyfips: '06037',
29
+ combinedstatisticalarea: 'Los Angeles-Long Beach, CA',
30
+ cbsaname: 'LOS ANGELES-LONG BEACH-SANTA ANA, CA METROPOLITAN STATISTICAL AREA',
31
+ cbsacode: 31100,
32
+ msaname: 'LOS ANGELES-LONG BEACH-SANTA ANA, CA',
33
+ msacode: 31100,
34
+ metropolitandivision: 'LOS ANGELES-LONG BEACH-GLENDALE, CA METROPOLITAN DIVISION',
35
+ minorcivildivisionname: 'SOUTH GATE-EAST LOS ANGELES',
36
+ minorcivildivisioncode: 93155,
37
+ neighborhoodcode: '',
38
+ censusfipsplacecode: 24477,
39
+ censustract: 535001,
40
+ censusblockgroup: 1,
41
+ censusblock: 1001,
42
+ parcelnumberraw: '6024-003-903',
43
+ parcelnumberformatted: '',
44
+ parcelnumberyearadded: 0,
45
+ parcelnumberalternate: '',
46
+ parcelmapbook: '',
47
+ parcelmappage: '',
48
+ parcelnumberyearchange: 2008,
49
+ parcelnumberprevious: 6024003003,
50
+ parcelaccountnumber: '',
51
+ propertyaddressfull: '7310 S CENTRAL AVE',
52
+ propertyaddresshousenumber: 7310,
53
+ propertyaddressstreetdirection: 'S',
54
+ propertyaddressstreetname: 'CENTRAL',
55
+ propertyaddressstreetsuffix: 'AVE',
56
+ propertyaddressstreetpostdirection: '',
57
+ propertyaddressunitprefix: '',
58
+ propertyaddressunitvalue: '',
59
+ propertyaddresscity: 'LOS ANGELES',
60
+ propertyaddressstate: 'CA',
61
+ propertyaddresszip: 90001,
62
+ propertyaddresszip4: 2423,
63
+ propertyaddresscrrt: 'C002',
64
+ propertyaddressinfoprivacy: 0,
65
+ congressionaldistricthouse: 53,
66
+ legaldescription: 'TR=5450 LOT 803',
67
+ legalrange: '',
68
+ legaltownship: '',
69
+ legalsection: '',
70
+ legalquarter: '',
71
+ legalquarterquarter: '',
72
+ legalsubdivision: 5450,
73
+ legalphase: '',
74
+ legaltractnumber: 5450,
75
+ legalblock1: '',
76
+ legalblock2: '',
77
+ legallotnumber1: 803,
78
+ legallotnumber2: '',
79
+ legallotnumber3: '',
80
+ legalunit: '',
81
+ partyowner1namefull: 'L A UNIFIED SCHOOL DIST',
82
+ partyowner1namefirst: '',
83
+ partyowner1namemiddle: '',
84
+ partyowner1namelast: 'L A UNIFIED SCHOOL DIST',
85
+ partyowner1namesuffix: '',
86
+ trustdescription: '',
87
+ companyflag: 'The Owner is Determine to be a Company',
88
+ partyowner2namefull: '',
89
+ partyowner2namefirst: '',
90
+ partyowner2namemiddle: '',
91
+ partyowner2namelast: '',
92
+ partyowner2namesuffix: '',
93
+ ownertypedescription1: 'COMPANY',
94
+ ownershipvestingrelationcode: '',
95
+ partyowner3namefull: '',
96
+ partyowner3namefirst: '',
97
+ partyowner3namemiddle: '',
98
+ partyowner3namelast: '',
99
+ partyowner3namesuffix: '',
100
+ partyowner4namefull: '',
101
+ partyowner4namefirst: '',
102
+ partyowner4namemiddle: '',
103
+ partyowner4namelast: '',
104
+ partyowner4namesuffix: '',
105
+ ownertypedescription2: 'NP',
106
+ contactownermailingcounty: 'LOS ANGELES',
107
+ contactownermailingfips: '06037',
108
+ contactownermailaddressfull: '333 S BEAUDRY AVE',
109
+ contactownermailaddresshousenumber: 333,
110
+ contactownermailaddressstreetdirection: 'S',
111
+ contactownermailaddressstreetname: 'BEAUDRY',
112
+ contactownermailaddressstreetsuffix: 'AVE',
113
+ contactownermailaddressstreetpostdirection: '',
114
+ contactownermailaddressunitprefix: '',
115
+ contactownermailaddressunit: '',
116
+ contactownermailaddresscity: 'LOS ANGELES',
117
+ contactownermailaddressstate: 'CA',
118
+ contactownermailaddresszip: 90017,
119
+ contactownermailaddresszip4: 1466,
120
+ contactownermailaddresscrrt: 'C006',
121
+ contactownermailaddressinfoformat: 'H',
122
+ contactownermailinfoprivacy: '',
123
+ statusowneroccupiedflag: 'unknown',
124
+ deedowner1namefull: 'L A UNIFIED SCHOOL DIST',
125
+ deedowner1namefirst: '',
126
+ deedowner1namemiddle: '',
127
+ deedowner1namelast: 'L A UNIFIED SCHOOL DIST',
128
+ deedowner1namesuffix: '',
129
+ deedowner2namefull: '',
130
+ deedowner2namefirst: '',
131
+ deedowner2namemiddle: '',
132
+ deedowner2namelast: '',
133
+ deedowner2namesuffix: '',
134
+ deedowner3namefull: '',
135
+ deedowner3namefirst: '',
136
+ deedowner3namemiddle: '',
137
+ deedowner3namelast: '',
138
+ deedowner3namesuffix: '',
139
+ deedowner4namefull: '',
140
+ deedowner4namefirst: '',
141
+ deedowner4namemiddle: '',
142
+ deedowner4namelast: '',
143
+ deedowner4namesuffix: '',
144
+ taxyearassessed: 2021,
145
+ taxassessedvaluetotal: 19483,
146
+ taxassessedvalueimprovements: 0,
147
+ taxassessedvalueland: 19483,
148
+ taxassessedimprovementsperc: 0,
149
+ previousassessedvalue: 19483,
150
+ taxmarketvalueyear: 0,
151
+ taxmarketvaluetotal: 0,
152
+ taxmarketvalueimprovements: 0,
153
+ taxmarketvalueland: 0,
154
+ taxmarketimprovementsperc: 0,
155
+ taxfiscalyear: 2020,
156
+ taxratearea: '1-265',
157
+ taxbilledamount: 0,
158
+ taxdelinquentyear: 0,
159
+ lastassessortaxrollupdate: '2021-07-01T00:00:00.000Z',
160
+ assrlastupdated: '2021-09-20T00:00:00.000Z',
161
+ taxexemptionadditional: '',
162
+ yearbuilt: 1928,
163
+ yearbuilteffective: 1928,
164
+ zonedcodelocal: 'LCC3*',
165
+ propertyusemuni: '0500',
166
+ propertyusegroup: 'COMMERCIAL',
167
+ propertyusestandardized: 'APARTMENT HOUSE (5+ UNITS)',
168
+ assessorlastsaledate: '2006-08-04T00:00:00.000Z',
169
+ assessorlastsaleamount: 0,
170
+ assessorpriorsaledate: '2006-08-04T00:00:00.000Z',
171
+ assessorpriorsaleamount: 0,
172
+ lastownershiptransferdate: '2006-08-04T00:00:00.000Z',
173
+ lastownershiptransferdocumentnumber: '2006-1731837',
174
+ lastownershiptransfertransactionid: 199634356,
175
+ deedlastsaledocumentbook: '',
176
+ deedlastsaledocumentpage: '',
177
+ deedlastdocumentnumber: '2006-1731837',
178
+ deedlastsaledate: '2006-08-04T00:00:00.000Z',
179
+ deedlastsaleprice: 0,
180
+ deedlastsaletransactionid: 199634356,
181
+ areabuilding: 3200,
182
+ areabuildingdefinitioncode: 'Total Area',
183
+ areagross: 3200,
184
+ area1stfloor: 0,
185
+ area2ndfloor: 0,
186
+ areaupperfloors: 0,
187
+ arealotacres: 0.0933425,
188
+ arealotsf: 4066,
189
+ arealotdepth: 0,
190
+ arealotwidth: 0,
191
+ roomsatticarea: 0,
192
+ roomsatticflag: 0,
193
+ roomsbasementarea: 0,
194
+ roomsbasementareafinished: 0,
195
+ roomsbasementareaunfinished: 0,
196
+ parkinggarage: 'Unknown',
197
+ parkinggaragearea: 0,
198
+ parkingcarport: 0,
199
+ parkingcarportarea: 0,
200
+ hvaccoolingdetail: 'YES',
201
+ hvacheatingdetail: 'NONE',
202
+ hvacheatingfuel: 'unknown',
203
+ utilitiessewageusage: 'unknown',
204
+ utilitieswatersource: 'unknown',
205
+ utilitiesmobilehomehookupflag: 'UNKNOWN',
206
+ foundation: 'unknown',
207
+ construction: 'UNKNOWN',
208
+ interiorstructure: 'unknown',
209
+ plumbingfixturescount: 0,
210
+ constructionfireresistanceclass: '',
211
+ assafetyfiresprinklersflag: 'UNKNOWN',
212
+ flooringmaterialprimary: 'unknown',
213
+ bathcount: 0,
214
+ bathpartialcount: 0,
215
+ bedroomscount: 0,
216
+ roomscount: 0,
217
+ storiescount: 0,
218
+ unitscount: 6,
219
+ fireplace: 'unknown',
220
+ fireplacecount: 0,
221
+ structurestyle: 'unknown',
222
+ exterior1code: 'UNKNOWN',
223
+ roofmaterial: 'unknown',
224
+ roofconstruction: 'unknown',
225
+ porchcode: 'unknown',
226
+ pool: 'UNKNOWN',
227
+ poolarea: 0,
228
+ buildingscount: 0,
229
+ modifiedon: '2021-09-14T00:00:00T00:00:00.000Z',
230
+ location: '33.97346,-118.25645',
231
+ },
232
+ {
233
+ attomid: 154816509,
234
+ fulladdress: '6602 S Central Ave Los Angeles CA 90001',
235
+ addressstreet: '6602 S Central Ave',
236
+ addressunit: ' ',
237
+ primarynumber: 6602,
238
+ streetname: 'Central',
239
+ streetpredirection: 'S',
240
+ streetpostdirection: '',
241
+ streetsuffix: 'Ave',
242
+ secondarynumber: '',
243
+ secondarydesignator: '',
244
+ extrasecondarynumber: '',
245
+ extrasecondarydesignator: '',
246
+ cityname: 'Los Angeles',
247
+ stateabbreviation: 'CA',
248
+ zipcode: '90001',
249
+ latitude: 33.97984,
250
+ longitude: -118.25633,
251
+ situsstatecode: 'CA',
252
+ situscounty: 'Los Angeles',
253
+ propertyjurisdictionname: 'LOS ANGELES',
254
+ situsstatecountyfips: '06037',
255
+ combinedstatisticalarea: 'Los Angeles-Long Beach, CA',
256
+ cbsaname: 'LOS ANGELES-LONG BEACH-SANTA ANA, CA METROPOLITAN STATISTICAL AREA',
257
+ cbsacode: 31100,
258
+ msaname: 'LOS ANGELES-LONG BEACH-SANTA ANA, CA',
259
+ msacode: 31100,
260
+ metropolitandivision: 'LOS ANGELES-LONG BEACH-GLENDALE, CA METROPOLITAN DIVISION',
261
+ minorcivildivisionname: 'SOUTH GATE-EAST LOS ANGELES',
262
+ minorcivildivisioncode: 93155,
263
+ neighborhoodcode: '',
264
+ censusfipsplacecode: 24477,
265
+ censustract: 532900,
266
+ censusblockgroup: 3,
267
+ censusblock: 3000,
268
+ parcelnumberraw: '6010-005-002',
269
+ parcelnumberformatted: '',
270
+ parcelnumberyearadded: 0,
271
+ parcelnumberalternate: '',
272
+ parcelmapbook: '',
273
+ parcelmappage: '',
274
+ parcelnumberyearchange: 0,
275
+ parcelnumberprevious: '',
276
+ parcelaccountnumber: '',
277
+ propertyaddressfull: '6602 S CENTRAL AVE',
278
+ propertyaddresshousenumber: 6602,
279
+ propertyaddressstreetdirection: 'S',
280
+ propertyaddressstreetname: 'CENTRAL',
281
+ propertyaddressstreetsuffix: 'AVE',
282
+ propertyaddressstreetpostdirection: '',
283
+ propertyaddressunitprefix: '',
284
+ propertyaddressunitvalue: '',
285
+ propertyaddresscity: 'LOS ANGELES',
286
+ propertyaddressstate: 'CA',
287
+ propertyaddresszip: 90001,
288
+ propertyaddresszip4: '',
289
+ propertyaddresscrrt: '',
290
+ propertyaddressinfoprivacy: 0,
291
+ congressionaldistricthouse: 0,
292
+ legaldescription: 'TRACT NO 5450 E 50 FT OF LOT 33',
293
+ legalrange: '',
294
+ legaltownship: '',
295
+ legalsection: '',
296
+ legalquarter: '',
297
+ legalquarterquarter: '',
298
+ legalsubdivision: 5450,
299
+ legalphase: '',
300
+ legaltractnumber: 5450,
301
+ legalblock1: '',
302
+ legalblock2: '',
303
+ legallotnumber1: 33,
304
+ legallotnumber2: '',
305
+ legallotnumber3: '',
306
+ legalunit: '',
307
+ partyowner1namefull: 'LEVI JONES',
308
+ partyowner1namefirst: 'LEVI',
309
+ partyowner1namemiddle: '',
310
+ partyowner1namelast: 'JONES',
311
+ partyowner1namesuffix: '',
312
+ trustdescription: '',
313
+ companyflag: 'unknown',
314
+ partyowner2namefull: '',
315
+ partyowner2namefirst: '',
316
+ partyowner2namemiddle: '',
317
+ partyowner2namelast: '',
318
+ partyowner2namesuffix: '',
319
+ ownertypedescription1: 'INDIVIDUAL',
320
+ ownershipvestingrelationcode: '',
321
+ partyowner3namefull: 'LEVI & ANNABELLE JONES TRUST',
322
+ partyowner3namefirst: '',
323
+ partyowner3namemiddle: '',
324
+ partyowner3namelast: 'LEVI & ANNABELLE JONES TRUST',
325
+ partyowner3namesuffix: '',
326
+ partyowner4namefull: '',
327
+ partyowner4namefirst: '',
328
+ partyowner4namemiddle: '',
329
+ partyowner4namelast: '',
330
+ partyowner4namesuffix: '',
331
+ ownertypedescription2: 'COMPANY',
332
+ contactownermailingcounty: 'LOS ANGELES',
333
+ contactownermailingfips: '06037',
334
+ contactownermailaddressfull: '6534 S CENTRAL AVE',
335
+ contactownermailaddresshousenumber: 6534,
336
+ contactownermailaddressstreetdirection: 'S',
337
+ contactownermailaddressstreetname: 'CENTRAL',
338
+ contactownermailaddressstreetsuffix: 'AVE',
339
+ contactownermailaddressstreetpostdirection: '',
340
+ contactownermailaddressunitprefix: '',
341
+ contactownermailaddressunit: '',
342
+ contactownermailaddresscity: 'LOS ANGELES',
343
+ contactownermailaddressstate: 'CA',
344
+ contactownermailaddresszip: 90001,
345
+ contactownermailaddresszip4: 1642,
346
+ contactownermailaddresscrrt: 'C043',
347
+ contactownermailaddressinfoformat: 'S',
348
+ contactownermailinfoprivacy: '',
349
+ statusowneroccupiedflag: 'Not Owner Occupied or Unknown',
350
+ deedowner1namefull: 'LEVI & ANNABELLE JONES TRUST',
351
+ deedowner1namefirst: '',
352
+ deedowner1namemiddle: '',
353
+ deedowner1namelast: 'LEVI & ANNABELLE JONES TRUST',
354
+ deedowner1namesuffix: '',
355
+ deedowner2namefull: '',
356
+ deedowner2namefirst: '',
357
+ deedowner2namemiddle: '',
358
+ deedowner2namelast: '',
359
+ deedowner2namesuffix: '',
360
+ deedowner3namefull: '',
361
+ deedowner3namefirst: '',
362
+ deedowner3namemiddle: '',
363
+ deedowner3namelast: '',
364
+ deedowner3namesuffix: '',
365
+ deedowner4namefull: '',
366
+ deedowner4namefirst: '',
367
+ deedowner4namemiddle: '',
368
+ deedowner4namelast: '',
369
+ deedowner4namesuffix: '',
370
+ taxyearassessed: 2021,
371
+ taxassessedvaluetotal: 10579,
372
+ taxassessedvalueimprovements: 4718,
373
+ taxassessedvalueland: 5861,
374
+ taxassessedimprovementsperc: 44,
375
+ previousassessedvalue: 10579,
376
+ taxmarketvalueyear: 0,
377
+ taxmarketvaluetotal: 0,
378
+ taxmarketvalueimprovements: 0,
379
+ taxmarketvalueland: 0,
380
+ taxmarketimprovementsperc: 0,
381
+ taxfiscalyear: 2021,
382
+ taxratearea: '1-166',
383
+ taxbilledamount: 1216.77,
384
+ taxdelinquentyear: 0,
385
+ lastassessortaxrollupdate: '2021-07-01T00:00:00.000Z',
386
+ assrlastupdated: '2021-10-11T00:00:00.000Z',
387
+ taxexemptionadditional: '',
388
+ yearbuilt: 1950,
389
+ yearbuilteffective: 1951,
390
+ zonedcodelocal: 'LCM1*',
391
+ propertyusemuni: 2600,
392
+ propertyusegroup: 'Commercial',
393
+ propertyusestandardized: 'AUTO REPAIR, GARAGE',
394
+ assessorlastsaledate: null,
395
+ assessorlastsaleamount: 0,
396
+ assessorpriorsaledate: null,
397
+ assessorpriorsaleamount: 0,
398
+ lastownershiptransferdate: '1996-06-20T00:00:00.000Z',
399
+ lastownershiptransferdocumentnumber: '96-0972167',
400
+ lastownershiptransfertransactionid: 0,
401
+ deedlastsaledocumentbook: '',
402
+ deedlastsaledocumentpage: '',
403
+ deedlastdocumentnumber: '',
404
+ deedlastsaledate: null,
405
+ deedlastsaleprice: 0,
406
+ deedlastsaletransactionid: 0,
407
+ areabuilding: 760,
408
+ areabuildingdefinitioncode: 'Total Area',
409
+ areagross: 760,
410
+ area1stfloor: 0,
411
+ area2ndfloor: 0,
412
+ areaupperfloors: 0,
413
+ arealotacres: 0.0463499,
414
+ arealotsf: 2019,
415
+ arealotdepth: 0,
416
+ arealotwidth: 0,
417
+ roomsatticarea: 0,
418
+ roomsatticflag: 0,
419
+ roomsbasementarea: 0,
420
+ roomsbasementareafinished: 0,
421
+ roomsbasementareaunfinished: 0,
422
+ parkinggarage: 'Unknown',
423
+ parkinggaragearea: 0,
424
+ parkingcarport: 0,
425
+ parkingcarportarea: 0,
426
+ hvaccoolingdetail: 'unknown',
427
+ hvacheatingdetail: 'NONE',
428
+ hvacheatingfuel: 'unknown',
429
+ utilitiessewageusage: 'unknown',
430
+ utilitieswatersource: 'unknown',
431
+ utilitiesmobilehomehookupflag: 'UNKNOWN',
432
+ foundation: 'unknown',
433
+ construction: 'WOOD',
434
+ interiorstructure: 'unknown',
435
+ plumbingfixturescount: 0,
436
+ constructionfireresistanceclass: 104,
437
+ assafetyfiresprinklersflag: 'UNKNOWN',
438
+ flooringmaterialprimary: 'unknown',
439
+ bathcount: 0,
440
+ bathpartialcount: 0,
441
+ bedroomscount: 0,
442
+ roomscount: 0,
443
+ storiescount: 1,
444
+ unitscount: 0,
445
+ fireplace: 'unknown',
446
+ fireplacecount: 0,
447
+ structurestyle: 'unknown',
448
+ exterior1code: 'UNKNOWN',
449
+ roofmaterial: 'unknown',
450
+ roofconstruction: 'unknown',
451
+ porchcode: 'unknown',
452
+ pool: 'UNKNOWN',
453
+ poolarea: 0,
454
+ buildingscount: 0,
455
+ modifiedon: '2021-10-22T00:00:00T00:00:00.000Z',
456
+ location: '33.97984,-118.25633',
457
+ },
458
+ ];
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const globals_1 = require("@jest/globals");
4
+ const index_1 = require("../index");
5
+ const setup_1 = require("./setup");
6
+ describe('property-use-group-converter', () => {
7
+ let base;
8
+ (0, globals_1.beforeAll)(async () => {
9
+ const client = await (0, setup_1.openConnection)();
10
+ base = client.db('test');
11
+ });
12
+ describe('processor', () => {
13
+ let processor;
14
+ (0, globals_1.beforeAll)(async () => {
15
+ await (0, setup_1.setupProperties)(base);
16
+ processor = (0, index_1.configureProcessor)({
17
+ workers: 1,
18
+ mongoDb: base,
19
+ });
20
+ const { propertyUseGroupProcessor } = processor;
21
+ await propertyUseGroupProcessor.start('90001-90002');
22
+ });
23
+ (0, globals_1.test)('should convert existing field to captial-case', async () => {
24
+ const result = await new Promise(resolve => {
25
+ base.collection('properties')
26
+ .find({
27
+ propertyusegroup: {
28
+ $exists: true,
29
+ },
30
+ })
31
+ .toArray((err, res) => {
32
+ resolve(res);
33
+ });
34
+ });
35
+ (0, globals_1.expect)(result[0].propertyusegroup).toBe('COMMERCIAL');
36
+ (0, globals_1.expect)(result[1].propertyusegroup).toBe('COMMERCIAL');
37
+ });
38
+ });
39
+ describe('aggregate', () => {
40
+ (0, globals_1.beforeAll)(async () => {
41
+ await (0, setup_1.setupProperties)(base);
42
+ });
43
+ (0, globals_1.test)('should convert existing field to captial-case', async () => {
44
+ await (0, index_1.propertyUseGroupProcessorAggregate)(base);
45
+ const result = await new Promise(resolve => {
46
+ base.collection('properties')
47
+ .find({
48
+ propertyusegroup: {
49
+ $exists: true,
50
+ },
51
+ })
52
+ .toArray((err, res) => {
53
+ resolve(res);
54
+ });
55
+ });
56
+ (0, globals_1.expect)(result[0].propertyusegroup).toBe('COMMERCIAL');
57
+ (0, globals_1.expect)(result[1].propertyusegroup).toBe('COMMERCIAL');
58
+ });
59
+ });
60
+ afterAll(async () => {
61
+ await (0, setup_1.closeConnection)();
62
+ });
63
+ });
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.closeConnection = exports.openConnection = exports.setupProperties = void 0;
4
+ const mongodb_1 = require("mongodb");
5
+ const mock_1 = require("./mock");
6
+ async function setupProperties(base) {
7
+ try {
8
+ await base.dropCollection('properties');
9
+ }
10
+ catch (e) {
11
+ // ignore
12
+ }
13
+ try {
14
+ await base.createCollection('properties');
15
+ await base.collection('properties').insertMany(mock_1.MOCK);
16
+ }
17
+ catch (e) {
18
+ // ignore
19
+ }
20
+ }
21
+ exports.setupProperties = setupProperties;
22
+ const url = `mongodb://${process.env.MONGO_HOSTNAME ?? 'localhost'}:27017`;
23
+ const mongoClient = new mongodb_1.MongoClient(url, {
24
+ useNewUrlParser: true,
25
+ useUnifiedTopology: true,
26
+ poolSize: 60,
27
+ });
28
+ let connectedMongoClient;
29
+ const openConnection = async () => {
30
+ connectedMongoClient = await mongoClient.connect();
31
+ return connectedMongoClient;
32
+ };
33
+ exports.openConnection = openConnection;
34
+ const closeConnection = () => connectedMongoClient?.close();
35
+ exports.closeConnection = closeConnection;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.calculateFilterCount = void 0;
4
+ const utils_1 = require("../utils/utils");
5
+ const calculateFilterCount = (properties) => {
6
+ const filterCount = {
7
+ propertyusegroup: {},
8
+ propertyusestandardized: {},
9
+ hvaccoolingdetail: {},
10
+ hvacheatingdetail: {},
11
+ utilitieswatersource: {},
12
+ flooringmaterialprimary: {},
13
+ };
14
+ properties.forEach(p => {
15
+ if (p.propertyusestandardized.toLowerCase() !== utils_1.UNKNOWN &&
16
+ p.propertyusegroup.toLowerCase() !== utils_1.UNKNOWN) {
17
+ const propertyUseKey = p.propertyusestandardized + utils_1.SEPARATOR_CHARACTER + p.propertyusegroup;
18
+ filterCount.propertyusestandardized[propertyUseKey] =
19
+ filterCount.propertyusestandardized[propertyUseKey] || 0;
20
+ filterCount.propertyusestandardized[propertyUseKey] += 1;
21
+ filterCount.propertyusegroup[p.propertyusegroup] =
22
+ filterCount.propertyusegroup[p.propertyusegroup] || 0;
23
+ filterCount.propertyusegroup[p.propertyusegroup] += 1;
24
+ }
25
+ if (p.hvaccoolingdetail.toLowerCase() !== utils_1.UNKNOWN) {
26
+ filterCount.hvaccoolingdetail[p.hvaccoolingdetail] =
27
+ filterCount.hvaccoolingdetail[p.hvaccoolingdetail] || 0;
28
+ filterCount.hvaccoolingdetail[p.hvaccoolingdetail] += 1;
29
+ }
30
+ if (p.hvacheatingdetail.toLowerCase() !== utils_1.UNKNOWN) {
31
+ filterCount.hvacheatingdetail[p.hvacheatingdetail] =
32
+ filterCount.hvacheatingdetail[p.hvacheatingdetail] || 0;
33
+ filterCount.hvacheatingdetail[p.hvacheatingdetail] += 1;
34
+ }
35
+ if (p.utilitieswatersource.toLowerCase() !== utils_1.UNKNOWN) {
36
+ filterCount.utilitieswatersource[p.utilitieswatersource] =
37
+ filterCount.utilitieswatersource[p.utilitieswatersource] || 0;
38
+ filterCount.utilitieswatersource[p.utilitieswatersource] += 1;
39
+ }
40
+ if (p.flooringmaterialprimary.toLowerCase() !== utils_1.UNKNOWN) {
41
+ filterCount.flooringmaterialprimary[p.flooringmaterialprimary] =
42
+ filterCount.flooringmaterialprimary[p.flooringmaterialprimary] || 0;
43
+ filterCount.flooringmaterialprimary[p.flooringmaterialprimary] += 1;
44
+ }
45
+ });
46
+ const propertyusegroupCounts = Object.keys(filterCount.propertyusegroup).map(name => ({
47
+ name,
48
+ count: filterCount.propertyusegroup[name],
49
+ }));
50
+ const propertyusestandardizedCounts = Object.keys(filterCount.propertyusestandardized).map(key => {
51
+ const [name, groupName] = key.split(utils_1.SEPARATOR_CHARACTER);
52
+ return {
53
+ name,
54
+ groupName,
55
+ count: filterCount.propertyusestandardized[key],
56
+ };
57
+ });
58
+ const hvaccoolingdetailCounts = Object.keys(filterCount.hvaccoolingdetail).map(name => ({
59
+ name,
60
+ count: filterCount.hvaccoolingdetail[name],
61
+ }));
62
+ const flooringmaterialprimaryCounts = Object.keys(filterCount.flooringmaterialprimary).map(name => ({
63
+ name,
64
+ count: filterCount.flooringmaterialprimary[name],
65
+ }));
66
+ const hvacheatingdetailCounts = Object.keys(filterCount.hvacheatingdetail).map(name => ({
67
+ name,
68
+ count: filterCount.hvacheatingdetail[name],
69
+ }));
70
+ const utilitieswatersourceCounts = Object.keys(filterCount.utilitieswatersource).map(name => ({
71
+ name,
72
+ count: filterCount.utilitieswatersource[name],
73
+ }));
74
+ return {
75
+ propertyusegroupCounts,
76
+ propertyusestandardizedCounts,
77
+ hvaccoolingdetailCounts,
78
+ hvacheatingdetailCounts,
79
+ utilitieswatersourceCounts,
80
+ flooringmaterialprimaryCounts,
81
+ };
82
+ };
83
+ exports.calculateFilterCount = calculateFilterCount;
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getFilterFetcherProcessor = void 0;
4
+ const calc_filter_count_1 = require("./calc-filter-count");
5
+ function getFilterFetcherProcessor(db) {
6
+ const getPropertiesCollection = async () => {
7
+ return db.collection('properties');
8
+ };
9
+ const getFiltersCollection = async () => {
10
+ return db.collection('filters');
11
+ };
12
+ const getProperties = async (zip) => {
13
+ const collection = await getPropertiesCollection();
14
+ return new Promise((resolve, reject) => {
15
+ collection
16
+ .find({ zipcode: zip }, {
17
+ projection: {
18
+ zipcode: 1,
19
+ propertyusegroup: 1,
20
+ propertyusestandardized: 1,
21
+ hvaccoolingdetail: 1,
22
+ hvacheatingdetail: 1,
23
+ utilitieswatersource: 1,
24
+ flooringmaterialprimary: 1,
25
+ _id: 0, // eslint-disable-line
26
+ },
27
+ })
28
+ .toArray((err, results) => {
29
+ if (err) {
30
+ return reject(err);
31
+ }
32
+ resolve(results);
33
+ });
34
+ });
35
+ };
36
+ const deleteZipFilter = async (zip) => {
37
+ const collection = await getFiltersCollection();
38
+ return new Promise((resolve, reject) => {
39
+ collection.deleteOne({ zipcode: zip }, (err, results) => {
40
+ if (err) {
41
+ return reject(err);
42
+ }
43
+ resolve(results);
44
+ });
45
+ });
46
+ };
47
+ const addZipFilters = async ({ zip, propertyusegroupCounts, propertyusestandardizedCounts, hvaccoolingdetailCounts, hvacheatingdetailCounts, utilitieswatersourceCounts, flooringmaterialprimaryCounts, }) => {
48
+ const collection = await getFiltersCollection();
49
+ return new Promise((resolve, reject) => {
50
+ collection.insertOne({
51
+ zipcode: zip,
52
+ propertyusegroup: propertyusegroupCounts,
53
+ propertyusestandardized: propertyusestandardizedCounts,
54
+ hvaccoolingdetail: hvaccoolingdetailCounts,
55
+ hvacheatingdetail: hvacheatingdetailCounts,
56
+ utilitieswatersource: utilitieswatersourceCounts,
57
+ flooringmaterialprimary: flooringmaterialprimaryCounts,
58
+ }, (err, results) => {
59
+ if (err) {
60
+ return reject(err);
61
+ }
62
+ resolve(results);
63
+ });
64
+ });
65
+ };
66
+ return async function processZip(zip, errors) {
67
+ if (!zip) {
68
+ return;
69
+ }
70
+ try {
71
+ const properties = await getProperties(zip);
72
+ const filters = (0, calc_filter_count_1.calculateFilterCount)(properties);
73
+ await deleteZipFilter(zip);
74
+ await addZipFilters({
75
+ zip,
76
+ ...filters,
77
+ });
78
+ }
79
+ catch (e) {
80
+ errors.add(zip);
81
+ }
82
+ };
83
+ }
84
+ exports.getFilterFetcherProcessor = getFilterFetcherProcessor;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.propertyUseGroupProcessorAggregate = exports.getPropertyUseGroupProcessor = void 0;
4
+ const utils_1 = require("../utils/utils");
5
+ const getPropertiesCollection = async (db) => {
6
+ return db.collection('properties');
7
+ };
8
+ function fixPropertyName(propertyName) {
9
+ return propertyName ? propertyName.toUpperCase() : '';
10
+ }
11
+ function getPropertyUseGroupProcessor(db) {
12
+ const getProperties = async (zip) => {
13
+ const collection = await getPropertiesCollection(db);
14
+ return new Promise((resolve, reject) => {
15
+ collection
16
+ .find({ zipcode: zip }, {
17
+ projection: {
18
+ propertyusegroup: 1,
19
+ },
20
+ })
21
+ .toArray((err, results) => {
22
+ if (err) {
23
+ return reject(err);
24
+ }
25
+ resolve(results);
26
+ });
27
+ });
28
+ };
29
+ function fixPropertyNames(properties) {
30
+ return properties
31
+ .filter(p => p.propertyusegroup &&
32
+ typeof p.propertyusegroup === 'string' &&
33
+ !(0, utils_1.isEmptyField)(p.propertyusegroup))
34
+ .map(p => {
35
+ return {
36
+ ...p,
37
+ propertyusegroup: fixPropertyName(p.propertyusegroup),
38
+ };
39
+ });
40
+ }
41
+ const updateProperties = async (properties) => {
42
+ const collection = await getPropertiesCollection(db);
43
+ const propQuery = properties.map(({ _id, ...p }) => ({
44
+ updateOne: {
45
+ // eslint-disable-next-line @typescript-eslint/naming-convention
46
+ filter: { _id },
47
+ update: { $set: p },
48
+ },
49
+ }));
50
+ return collection.bulkWrite(propQuery, { ordered: true, w: 1 });
51
+ };
52
+ return async function processZip(zip, errors) {
53
+ if (!zip) {
54
+ return;
55
+ }
56
+ try {
57
+ const properties = await getProperties(zip);
58
+ if (!properties || !properties.length) {
59
+ return;
60
+ }
61
+ const convertedProperties = fixPropertyNames(properties);
62
+ if (!convertedProperties.length) {
63
+ return;
64
+ }
65
+ await updateProperties(convertedProperties);
66
+ }
67
+ catch (e) {
68
+ errors.add(zip);
69
+ }
70
+ };
71
+ }
72
+ exports.getPropertyUseGroupProcessor = getPropertyUseGroupProcessor;
73
+ async function propertyUseGroupProcessorAggregate(db) {
74
+ const collection = await getPropertiesCollection(db);
75
+ const aggCursor = await collection.aggregate([
76
+ {
77
+ $set: {
78
+ propertyusegroup: {
79
+ $function: {
80
+ body: fixPropertyName.toString(),
81
+ args: ['$propertyusegroup'],
82
+ lang: 'js',
83
+ },
84
+ },
85
+ },
86
+ },
87
+ {
88
+ $merge: {
89
+ into: 'properties',
90
+ on: '_id',
91
+ whenMatched: 'replace',
92
+ whenNotMatched: 'insert',
93
+ },
94
+ },
95
+ ]);
96
+ let result;
97
+ do {
98
+ result = await aggCursor.next(); // eslint-disable-line no-await-in-loop
99
+ } while (result);
100
+ }
101
+ exports.propertyUseGroupProcessorAggregate = propertyUseGroupProcessorAggregate;
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.propertiesAssessorLastSaleDateAggregate = exports.propertyUseGroupProcessorAggregate = exports.configureProcessor = void 0;
4
+ const filter_fetcher_1 = require("./filter-fetcher");
5
+ const fix_property_use_1 = require("./fix-property-use");
6
+ const property_assessorlastsaledate_converter_1 = require("./property-assessorlastsaledate-converter");
7
+ const zip_processor_1 = require("./zip-processor");
8
+ function configureProcessor(config) {
9
+ const filterFetcher = (0, zip_processor_1.zipProcessor)(config, (0, filter_fetcher_1.getFilterFetcherProcessor)(config.mongoDb));
10
+ const assessorLastSaleDateProcessor = (0, zip_processor_1.zipProcessor)(config, (0, property_assessorlastsaledate_converter_1.getAssessorLastSaleDateProcessor)(config.mongoDb));
11
+ const propertyUseGroupProcessor = (0, zip_processor_1.zipProcessor)(config, (0, fix_property_use_1.getPropertyUseGroupProcessor)(config.mongoDb));
12
+ return { filterFetcher, assessorLastSaleDateProcessor, propertyUseGroupProcessor };
13
+ }
14
+ exports.configureProcessor = configureProcessor;
15
+ var fix_property_use_2 = require("./fix-property-use");
16
+ Object.defineProperty(exports, "propertyUseGroupProcessorAggregate", { enumerable: true, get: function () { return fix_property_use_2.propertyUseGroupProcessorAggregate; } });
17
+ var property_assessorlastsaledate_converter_2 = require("./property-assessorlastsaledate-converter");
18
+ Object.defineProperty(exports, "propertiesAssessorLastSaleDateAggregate", { enumerable: true, get: function () { return property_assessorlastsaledate_converter_2.propertiesAssessorLastSaleDateAggregate; } });
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.propertiesAssessorLastSaleDateAggregate = exports.getAssessorLastSaleDateProcessor = void 0;
4
+ const utils_1 = require("../utils/utils");
5
+ const getPropertiesCollection = async (db) => {
6
+ return db.collection('properties');
7
+ };
8
+ function getAssessorLastSaleDateProcessor(db) {
9
+ const getPropertiesAssessorLastSaleDate = async (zip) => {
10
+ const collection = await getPropertiesCollection(db);
11
+ return new Promise((resolve, reject) => {
12
+ collection
13
+ .find({
14
+ zipcode: zip,
15
+ assessorlastsaledate: {
16
+ $exists: true,
17
+ $type: 2,
18
+ },
19
+ }, {
20
+ projection: {
21
+ assessorlastsaledate: 1,
22
+ },
23
+ })
24
+ .toArray((err, results) => {
25
+ if (err) {
26
+ return reject(err);
27
+ }
28
+ resolve(results);
29
+ });
30
+ });
31
+ };
32
+ const updateProperties = async (properties) => {
33
+ const collection = await getPropertiesCollection(db);
34
+ const propQuery = properties.map(({ _id, ...p }) => ({
35
+ updateOne: {
36
+ // eslint-disable-next-line @typescript-eslint/naming-convention
37
+ filter: { _id },
38
+ update: { $set: p },
39
+ },
40
+ }));
41
+ return collection.bulkWrite(propQuery, { ordered: true, w: 1 });
42
+ };
43
+ return async function processZip(zip, errors) {
44
+ if (!zip) {
45
+ return;
46
+ }
47
+ try {
48
+ const properties = await getPropertiesAssessorLastSaleDate(zip);
49
+ if (!properties || !properties.length) {
50
+ return;
51
+ }
52
+ const convertedProperties = (0, utils_1.convertStringToDate)(properties, 'assessorlastsaledate');
53
+ if (!convertedProperties.length) {
54
+ return;
55
+ }
56
+ await updateProperties(convertedProperties);
57
+ }
58
+ catch (e) {
59
+ errors.add(zip);
60
+ }
61
+ };
62
+ }
63
+ exports.getAssessorLastSaleDateProcessor = getAssessorLastSaleDateProcessor;
64
+ async function propertiesAssessorLastSaleDateAggregate(db) {
65
+ const collection = await getPropertiesCollection(db);
66
+ const aggCursor = collection.aggregate([
67
+ {
68
+ $match: {
69
+ assessorlastsaledate: {
70
+ $exists: true,
71
+ $type: 2,
72
+ },
73
+ },
74
+ },
75
+ {
76
+ $set: {
77
+ assessorlastsaledate: {
78
+ $function: {
79
+ body: `function (d) {
80
+ return new Date(d);
81
+ }`,
82
+ args: ['$assessorlastsaledate'],
83
+ lang: 'js',
84
+ },
85
+ },
86
+ },
87
+ },
88
+ {
89
+ $merge: {
90
+ into: 'properties',
91
+ on: '_id',
92
+ whenMatched: 'replace',
93
+ whenNotMatched: 'insert',
94
+ },
95
+ },
96
+ ]);
97
+ let result;
98
+ do {
99
+ result = await aggCursor.next(); // eslint-disable-line no-await-in-loop
100
+ } while (result);
101
+ }
102
+ exports.propertiesAssessorLastSaleDateAggregate = propertiesAssessorLastSaleDateAggregate;
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertStringToDate = exports.convertToZipString = exports.convertObjectToArray = exports.extractUniqueFilterNames = exports.isEmptyField = exports.isNotFullProperty = exports.UNKNOWN = exports.SEPARATOR_CHARACTER = void 0;
4
+ exports.SEPARATOR_CHARACTER = '___';
5
+ exports.UNKNOWN = 'unknown';
6
+ const isNotFullProperty = (property) => {
7
+ if (!property.hvaccoolingdetail) {
8
+ return true;
9
+ }
10
+ return ((0, exports.isEmptyField)(property.hvaccoolingdetail) ||
11
+ (0, exports.isEmptyField)(property.hvacheatingdetail) ||
12
+ (0, exports.isEmptyField)(property.utilitieswatersource) ||
13
+ (0, exports.isEmptyField)(property.propertyusegroup) ||
14
+ (0, exports.isEmptyField)(property.propertyusestandardized) ||
15
+ (0, exports.isEmptyField)(property.flooringmaterialprimary));
16
+ };
17
+ exports.isNotFullProperty = isNotFullProperty;
18
+ const isEmptyField = (field) => {
19
+ return field.toLowerCase() === exports.UNKNOWN;
20
+ };
21
+ exports.isEmptyField = isEmptyField;
22
+ const extractUniqueFilterNames = (properties) => {
23
+ const hvaccoolingdetail = new Set();
24
+ const hvacheatingdetail = new Set();
25
+ const utilitieswatersource = new Set();
26
+ const propertyusegroup = new Set();
27
+ const propertyusestandardized = new Set();
28
+ const flooringmaterialprimary = new Set();
29
+ properties.forEach(property => {
30
+ hvaccoolingdetail.add(property.hvaccoolingdetail);
31
+ hvacheatingdetail.add(property.hvacheatingdetail);
32
+ propertyusegroup.add(property.propertyusegroup);
33
+ utilitieswatersource.add(property.utilitieswatersource);
34
+ propertyusestandardized.add(property.propertyusestandardized + exports.SEPARATOR_CHARACTER + property.propertyusegroup);
35
+ flooringmaterialprimary.add(property.flooringmaterialprimary);
36
+ });
37
+ const propertyUseObjects = Array.from(propertyusestandardized).map(filter => {
38
+ const split = filter.split(exports.SEPARATOR_CHARACTER);
39
+ return {
40
+ name: split[0],
41
+ typeName: split[1],
42
+ };
43
+ });
44
+ const hvacCoolingSystemNames = Array.from(hvaccoolingdetail);
45
+ const hvacHeatingSystemNames = Array.from(hvacheatingdetail);
46
+ const propertyTypeNames = Array.from(propertyusegroup);
47
+ const waterSourceNames = Array.from(utilitieswatersource);
48
+ const flooringMaterialsNames = Array.from(flooringmaterialprimary);
49
+ return {
50
+ propertyUseObjects,
51
+ propertyTypeNames,
52
+ hvacCoolingSystemNames,
53
+ hvacHeatingSystemNames,
54
+ waterSourceNames,
55
+ flooringMaterialsNames,
56
+ };
57
+ };
58
+ exports.extractUniqueFilterNames = extractUniqueFilterNames;
59
+ const convertObjectToArray = (obj) => {
60
+ const array = [];
61
+ for (const key in obj) {
62
+ array.push(obj[key]);
63
+ }
64
+ return array;
65
+ };
66
+ exports.convertObjectToArray = convertObjectToArray;
67
+ const ZIP_STRING_LENGTH = 5;
68
+ const convertToZipString = (zipNumber) => {
69
+ const zipString = zipNumber + '';
70
+ return zipString.padStart(ZIP_STRING_LENGTH, '0');
71
+ };
72
+ exports.convertToZipString = convertToZipString;
73
+ const convertStringToDate = (props, propName) => {
74
+ return props
75
+ .filter(p => p[propName] &&
76
+ typeof p[propName] === 'string' &&
77
+ !(0, exports.isEmptyField)(p[propName]))
78
+ .map(p => ({ ...p, [propName]: new Date(p[propName]) }));
79
+ };
80
+ exports.convertStringToDate = convertStringToDate;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ /* eslint-disable no-await-in-loop, require-atomic-updates */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.zipProcessor = void 0;
5
+ const utils_1 = require("./utils/utils");
6
+ function zipProcessor(config, processorFn) {
7
+ let processor = null;
8
+ let erroredZips = new Set();
9
+ async function* processZips(zips) {
10
+ if (!zips) {
11
+ return;
12
+ }
13
+ // eslint-disable-next-line prefer-const
14
+ let [startZipNum, endZipNum] = zips.split('-').map(Number);
15
+ if (!endZipNum) {
16
+ endZipNum = startZipNum;
17
+ }
18
+ for (let zipNum = startZipNum; zipNum <= endZipNum;) {
19
+ const processes = [];
20
+ const processZipEnd = zipNum + config.workers - 1;
21
+ for (; zipNum <= endZipNum && zipNum <= processZipEnd; zipNum++) {
22
+ const zip = (0, utils_1.convertToZipString)(zipNum);
23
+ processes.push(processorFn(zip, erroredZips));
24
+ }
25
+ yield await Promise.all(processes);
26
+ }
27
+ while (erroredZips.size) {
28
+ const connectionErrorZipsInner = [...erroredZips];
29
+ erroredZips = new Set();
30
+ for (let i = 0; i <= connectionErrorZipsInner.length; i++) {
31
+ const zip = connectionErrorZipsInner[i];
32
+ yield await processorFn(zip, erroredZips);
33
+ }
34
+ }
35
+ }
36
+ async function clear() {
37
+ if (processor) {
38
+ await processor.return();
39
+ }
40
+ processor = null;
41
+ }
42
+ async function start(zips) {
43
+ processor = processZips(zips);
44
+ try {
45
+ let step = await processor.next();
46
+ while (processor && !step.done) {
47
+ step = await processor.next();
48
+ }
49
+ }
50
+ catch (e) {
51
+ await clear();
52
+ }
53
+ }
54
+ return {
55
+ getProcessor: () => processor,
56
+ start,
57
+ clear,
58
+ };
59
+ }
60
+ exports.zipProcessor = zipProcessor;
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@servicetitan/acquisition-functions",
3
+ "version": "0.1.0",
4
+ "description": "",
5
+ "main": "./dist/index.js",
6
+ "typings": "./dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc -p .",
9
+ "test": "jest --runInBand"
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "devDependencies": {
15
+ "@jest/globals": "29.1.2",
16
+ "@tsconfig/node16": "^1.0.3",
17
+ "@types/jest": "^29.1.0",
18
+ "@types/mongodb": "^3.6.20",
19
+ "@types/node": "^18.6.3",
20
+ "jest": "29.1.2",
21
+ "mongodb": "3.6.3",
22
+ "ts-jest": "29.0.3",
23
+ "ts-node": "^10.9.1"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "cli": {
29
+ "webpack": false
30
+ },
31
+ "gitHead": "30de32ace907277b336c7bc835e659b2a3ffaac9"
32
+ }