@servicetitan/acquisition-functions 0.1.0 → 0.3.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 CHANGED
@@ -14,6 +14,7 @@ const { configureProcessor } = require('@servicetitan/acquisition-functions');
14
14
  const { filterFetcher } = configureProcessor({
15
15
  workers: 10,
16
16
  mongoDb: mongoDbConnectionToDb,
17
+ fetchProperties: (zip) => mongoDbConnectionToDb.collection('properties').find({zipcode: zip}, {_id: 0}),
17
18
  });
18
19
 
19
20
  exports = async () => {
@@ -33,9 +34,5 @@ npm run test
33
34
  In Docker:
34
35
 
35
36
  ```sh
36
- # In root FE directory
37
- lerna run build --scope=@servicetitan/acquisition-functions
38
-
39
- # In package directory
40
- docker-compose up
37
+ docker-compose up --from-exit-code=tests
41
38
  ```
@@ -6,6 +6,7 @@ const setup_1 = require("./setup");
6
6
  describe('property-assessorlastsaledate-converter', () => {
7
7
  let processor;
8
8
  let base;
9
+ let metadata;
9
10
  (0, globals_1.beforeAll)(async () => {
10
11
  const client = await (0, setup_1.openConnection)();
11
12
  base = client.db('test');
@@ -18,7 +19,7 @@ describe('property-assessorlastsaledate-converter', () => {
18
19
  mongoDb: base,
19
20
  });
20
21
  const { assessorLastSaleDateProcessor } = processor;
21
- await assessorLastSaleDateProcessor.start('90001');
22
+ metadata = await assessorLastSaleDateProcessor.start('90001');
22
23
  });
23
24
  (0, globals_1.test)('should convert existing field to date', async () => {
24
25
  const result = await new Promise(resolve => {
@@ -46,6 +47,11 @@ describe('property-assessorlastsaledate-converter', () => {
46
47
  });
47
48
  (0, globals_1.expect)(result[0].assessorlastsaledate).toBe(null);
48
49
  });
50
+ (0, globals_1.test)('should return metadata', async () => {
51
+ (0, globals_1.expect)(metadata.errors).toBe(0);
52
+ (0, globals_1.expect)(metadata.properties).toBe(1);
53
+ (0, globals_1.expect)(metadata.requests).toBe(2);
54
+ });
49
55
  });
50
56
  describe('aggregate', () => {
51
57
  (0, globals_1.beforeEach)(async () => {
@@ -4,19 +4,24 @@ const globals_1 = require("@jest/globals");
4
4
  const index_1 = require("../index");
5
5
  const setup_1 = require("./setup");
6
6
  describe('property-assessorlastsaledate-converter', () => {
7
- let processor;
8
7
  let base;
8
+ let metadata;
9
9
  (0, globals_1.beforeAll)(async () => {
10
10
  const client = await (0, setup_1.openConnection)();
11
11
  base = client.db('test');
12
12
  await (0, setup_1.setupProperties)(base);
13
- processor = (0, index_1.configureProcessor)({
13
+ const { filterFetcher } = (0, index_1.configureProcessor)({
14
14
  workers: 10,
15
15
  mongoDb: base,
16
+ fetchProperties: (0, setup_1.getFetchPropertiesForFilters)(base),
16
17
  });
17
- const { filterFetcher, propertyUseGroupProcessor } = processor;
18
- await propertyUseGroupProcessor.start('90001');
19
- await filterFetcher.start('90001-90002');
18
+ const { propertyUseGroupProcessor } = (0, index_1.configureProcessor)({
19
+ workers: 10,
20
+ mongoDb: base,
21
+ fetchProperties: (0, setup_1.getFetchPropertiesForFix)(base),
22
+ });
23
+ await propertyUseGroupProcessor.start(['90001', '90002']);
24
+ metadata = await filterFetcher.start(['90001', '90002']);
20
25
  });
21
26
  (0, globals_1.test)('should have empty filter for missing zip', async () => {
22
27
  const result = await base.collection('filters').findOne({
@@ -30,6 +35,12 @@ describe('property-assessorlastsaledate-converter', () => {
30
35
  (0, globals_1.expect)(result?.propertyusestandardized.length).toBe(0);
31
36
  (0, globals_1.expect)(result?.utilitieswatersource.length).toBe(0);
32
37
  });
38
+ (0, globals_1.test)('should return metadata', async () => {
39
+ (0, globals_1.expect)(metadata.errors).toBe(0);
40
+ (0, globals_1.expect)(metadata.properties).toBe(2);
41
+ (0, globals_1.expect)(metadata.requests).toBe(6);
42
+ (0, globals_1.expect)(metadata.filters).toBe(2);
43
+ });
33
44
  (0, globals_1.test)('should form correct filter', async () => {
34
45
  const result = await base.collection('filters').findOne({
35
46
  zipcode: '90001',
@@ -11,14 +11,21 @@ describe('property-use-group-converter', () => {
11
11
  });
12
12
  describe('processor', () => {
13
13
  let processor;
14
+ let metadata;
14
15
  (0, globals_1.beforeAll)(async () => {
15
16
  await (0, setup_1.setupProperties)(base);
16
17
  processor = (0, index_1.configureProcessor)({
17
18
  workers: 1,
18
19
  mongoDb: base,
20
+ fetchProperties: (0, setup_1.getFetchPropertiesForFix)(base),
19
21
  });
20
22
  const { propertyUseGroupProcessor } = processor;
21
- await propertyUseGroupProcessor.start('90001-90002');
23
+ metadata = await propertyUseGroupProcessor.start('90001');
24
+ });
25
+ (0, globals_1.test)('should return metadata', async () => {
26
+ (0, globals_1.expect)(metadata.errors).toBe(0);
27
+ (0, globals_1.expect)(metadata.properties).toBe(2);
28
+ (0, globals_1.expect)(metadata.requests).toBe(2);
22
29
  });
23
30
  (0, globals_1.test)('should convert existing field to captial-case', async () => {
24
31
  const result = await new Promise(resolve => {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.closeConnection = exports.openConnection = exports.setupProperties = void 0;
3
+ exports.getFetchPropertiesForFix = exports.getFetchPropertiesForFilters = exports.closeConnection = exports.openConnection = exports.setupProperties = void 0;
4
4
  const mongodb_1 = require("mongodb");
5
5
  const mock_1 = require("./mock");
6
6
  async function setupProperties(base) {
@@ -10,6 +10,12 @@ async function setupProperties(base) {
10
10
  catch (e) {
11
11
  // ignore
12
12
  }
13
+ try {
14
+ await base.dropCollection('filters');
15
+ }
16
+ catch (e) {
17
+ // ignore
18
+ }
13
19
  try {
14
20
  await base.createCollection('properties');
15
21
  await base.collection('properties').insertMany(mock_1.MOCK);
@@ -33,3 +39,32 @@ const openConnection = async () => {
33
39
  exports.openConnection = openConnection;
34
40
  const closeConnection = () => connectedMongoClient?.close();
35
41
  exports.closeConnection = closeConnection;
42
+ const getFetchPropertiesForFilters = (base) => async (zip) => {
43
+ return base
44
+ .collection('properties')
45
+ .find({ zipcode: zip }, {
46
+ projection: {
47
+ zipcode: 1,
48
+ propertyusegroup: 1,
49
+ propertyusestandardized: 1,
50
+ hvaccoolingdetail: 1,
51
+ hvacheatingdetail: 1,
52
+ utilitieswatersource: 1,
53
+ flooringmaterialprimary: 1,
54
+ _id: 0, // eslint-disable-line
55
+ },
56
+ })
57
+ .toArray();
58
+ };
59
+ exports.getFetchPropertiesForFilters = getFetchPropertiesForFilters;
60
+ const getFetchPropertiesForFix = (base) => async (zip) => {
61
+ return base
62
+ .collection('properties')
63
+ .find({ zipcode: zip }, {
64
+ projection: {
65
+ propertyusegroup: 1,
66
+ },
67
+ })
68
+ .toArray();
69
+ };
70
+ exports.getFetchPropertiesForFix = getFetchPropertiesForFix;
@@ -2,83 +2,56 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getFilterFetcherProcessor = void 0;
4
4
  const calc_filter_count_1 = require("./calc-filter-count");
5
- function getFilterFetcherProcessor(db) {
6
- const getPropertiesCollection = async () => {
7
- return db.collection('properties');
8
- };
5
+ function getFilterFetcherProcessor({ mongoDb: db, fetchProperties }) {
9
6
  const getFiltersCollection = async () => {
10
7
  return db.collection('filters');
11
8
  };
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
9
  const deleteZipFilter = async (zip) => {
37
10
  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
- });
11
+ return collection.deleteOne({ zipcode: zip });
46
12
  };
47
13
  const addZipFilters = async ({ zip, propertyusegroupCounts, propertyusestandardizedCounts, hvaccoolingdetailCounts, hvacheatingdetailCounts, utilitieswatersourceCounts, flooringmaterialprimaryCounts, }) => {
48
14
  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
- });
15
+ return collection.insertOne({
16
+ zipcode: zip,
17
+ propertyusegroup: propertyusegroupCounts,
18
+ propertyusestandardized: propertyusestandardizedCounts,
19
+ hvaccoolingdetail: hvaccoolingdetailCounts,
20
+ hvacheatingdetail: hvacheatingdetailCounts,
21
+ utilitieswatersource: utilitieswatersourceCounts,
22
+ flooringmaterialprimary: flooringmaterialprimaryCounts,
64
23
  });
65
24
  };
66
25
  return async function processZip(zip, errors) {
26
+ const meta = {
27
+ requests: 0,
28
+ errors: 0,
29
+ properties: 0,
30
+ filters: 0,
31
+ };
67
32
  if (!zip) {
68
- return;
33
+ return meta;
69
34
  }
70
35
  try {
71
- const properties = await getProperties(zip);
36
+ const properties = await fetchProperties(zip);
37
+ meta.requests += 1;
38
+ meta.properties += properties.length;
72
39
  const filters = (0, calc_filter_count_1.calculateFilterCount)(properties);
73
40
  await deleteZipFilter(zip);
41
+ meta.requests += 1;
74
42
  await addZipFilters({
75
43
  zip,
76
44
  ...filters,
77
45
  });
46
+ meta.filters += 1;
47
+ meta.requests += 1;
78
48
  }
79
49
  catch (e) {
50
+ console.error(e);
80
51
  errors.add(zip);
52
+ meta.errors += 1;
81
53
  }
54
+ return meta;
82
55
  };
83
56
  }
84
57
  exports.getFilterFetcherProcessor = getFilterFetcherProcessor;
@@ -8,24 +8,7 @@ const getPropertiesCollection = async (db) => {
8
8
  function fixPropertyName(propertyName) {
9
9
  return propertyName ? propertyName.toUpperCase() : '';
10
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
- };
11
+ function getPropertyUseGroupProcessor({ mongoDb: db, fetchProperties }) {
29
12
  function fixPropertyNames(properties) {
30
13
  return properties
31
14
  .filter(p => p.propertyusegroup &&
@@ -50,23 +33,34 @@ function getPropertyUseGroupProcessor(db) {
50
33
  return collection.bulkWrite(propQuery, { ordered: true, w: 1 });
51
34
  };
52
35
  return async function processZip(zip, errors) {
36
+ const meta = {
37
+ requests: 0,
38
+ errors: 0,
39
+ properties: 0,
40
+ };
53
41
  if (!zip) {
54
- return;
42
+ return meta;
55
43
  }
56
44
  try {
57
- const properties = await getProperties(zip);
45
+ const properties = await fetchProperties(zip);
46
+ meta.requests += 1;
58
47
  if (!properties || !properties.length) {
59
- return;
48
+ return meta;
60
49
  }
61
50
  const convertedProperties = fixPropertyNames(properties);
62
51
  if (!convertedProperties.length) {
63
- return;
52
+ return meta;
64
53
  }
65
54
  await updateProperties(convertedProperties);
55
+ meta.properties += convertedProperties.length;
56
+ meta.requests += 1;
66
57
  }
67
58
  catch (e) {
59
+ console.error(e);
68
60
  errors.add(zip);
61
+ meta.errors += 1;
69
62
  }
63
+ return meta;
70
64
  };
71
65
  }
72
66
  exports.getPropertyUseGroupProcessor = getPropertyUseGroupProcessor;
package/dist/index.js CHANGED
@@ -6,9 +6,9 @@ const fix_property_use_1 = require("./fix-property-use");
6
6
  const property_assessorlastsaledate_converter_1 = require("./property-assessorlastsaledate-converter");
7
7
  const zip_processor_1 = require("./zip-processor");
8
8
  function configureProcessor(config) {
9
- const filterFetcher = (0, zip_processor_1.zipProcessor)(config, (0, filter_fetcher_1.getFilterFetcherProcessor)(config.mongoDb));
9
+ const filterFetcher = (0, zip_processor_1.zipProcessor)(config, (0, filter_fetcher_1.getFilterFetcherProcessor)(config));
10
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));
11
+ const propertyUseGroupProcessor = (0, zip_processor_1.zipProcessor)(config, (0, fix_property_use_1.getPropertyUseGroupProcessor)(config));
12
12
  return { filterFetcher, assessorLastSaleDateProcessor, propertyUseGroupProcessor };
13
13
  }
14
14
  exports.configureProcessor = configureProcessor;
@@ -8,26 +8,19 @@ const getPropertiesCollection = async (db) => {
8
8
  function getAssessorLastSaleDateProcessor(db) {
9
9
  const getPropertiesAssessorLastSaleDate = async (zip) => {
10
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
- });
11
+ return collection
12
+ .find({
13
+ zipcode: zip,
14
+ assessorlastsaledate: {
15
+ $exists: true,
16
+ $type: 2,
17
+ },
18
+ }, {
19
+ projection: {
20
+ assessorlastsaledate: 1,
21
+ },
22
+ })
23
+ .toArray();
31
24
  };
32
25
  const updateProperties = async (properties) => {
33
26
  const collection = await getPropertiesCollection(db);
@@ -41,23 +34,34 @@ function getAssessorLastSaleDateProcessor(db) {
41
34
  return collection.bulkWrite(propQuery, { ordered: true, w: 1 });
42
35
  };
43
36
  return async function processZip(zip, errors) {
37
+ const meta = {
38
+ requests: 0,
39
+ errors: 0,
40
+ properties: 0,
41
+ };
44
42
  if (!zip) {
45
- return;
43
+ return meta;
46
44
  }
47
45
  try {
48
46
  const properties = await getPropertiesAssessorLastSaleDate(zip);
47
+ meta.requests += 1;
49
48
  if (!properties || !properties.length) {
50
- return;
49
+ return meta;
51
50
  }
52
51
  const convertedProperties = (0, utils_1.convertStringToDate)(properties, 'assessorlastsaledate');
53
52
  if (!convertedProperties.length) {
54
- return;
53
+ return meta;
55
54
  }
56
55
  await updateProperties(convertedProperties);
56
+ meta.properties += convertedProperties.length;
57
+ meta.requests += 1;
57
58
  }
58
59
  catch (e) {
60
+ console.error(e);
61
+ meta.errors += 1;
59
62
  errors.add(zip);
60
63
  }
64
+ return meta;
61
65
  };
62
66
  }
63
67
  exports.getAssessorLastSaleDateProcessor = getAssessorLastSaleDateProcessor;
@@ -10,19 +10,25 @@ function zipProcessor(config, processorFn) {
10
10
  if (!zips) {
11
11
  return;
12
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));
13
+ if (Array.isArray(zips)) {
14
+ // eslint-disable-next-line prefer-const
15
+ let [startZipNum, endZipNum] = zips.map(Number);
16
+ if (!endZipNum) {
17
+ endZipNum = startZipNum;
24
18
  }
25
- yield await Promise.all(processes);
19
+ for (let zipNum = startZipNum; zipNum <= endZipNum;) {
20
+ const processes = [];
21
+ const processZipEnd = zipNum + config.workers - 1;
22
+ for (; zipNum <= endZipNum && zipNum <= processZipEnd; zipNum++) {
23
+ const zip = (0, utils_1.convertToZipString)(zipNum);
24
+ processes.push(processorFn(zip, erroredZips));
25
+ }
26
+ yield await Promise.all(processes);
27
+ }
28
+ }
29
+ else {
30
+ const zip = (0, utils_1.convertToZipString)(Number(zips));
31
+ yield await processorFn(zip, erroredZips);
26
32
  }
27
33
  while (erroredZips.size) {
28
34
  const connectionErrorZipsInner = [...erroredZips];
@@ -39,17 +45,39 @@ function zipProcessor(config, processorFn) {
39
45
  }
40
46
  processor = null;
41
47
  }
48
+ const meta = {
49
+ requests: 0,
50
+ properties: 0,
51
+ errors: 0,
52
+ filters: 0,
53
+ };
54
+ function sumMetas(metas) {
55
+ if (!metas) {
56
+ return;
57
+ }
58
+ return (Array.isArray(metas) ? metas : [metas]).forEach(m => {
59
+ meta.requests += m.requests;
60
+ meta.properties += m.properties;
61
+ meta.errors += m.errors;
62
+ if (m.filters) {
63
+ meta.filters += m.filters;
64
+ }
65
+ });
66
+ }
42
67
  async function start(zips) {
43
68
  processor = processZips(zips);
44
69
  try {
45
- let step = await processor.next();
46
- while (processor && !step.done) {
47
- step = await processor.next();
70
+ let isDone = false;
71
+ while (processor && !isDone) {
72
+ const step = await processor.next();
73
+ sumMetas(step.value);
74
+ isDone = Boolean(step.done);
48
75
  }
49
76
  }
50
77
  catch (e) {
51
78
  await clear();
52
79
  }
80
+ return meta;
53
81
  }
54
82
  return {
55
83
  getProcessor: () => processor,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/acquisition-functions",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "",
5
5
  "main": "./dist/index.js",
6
6
  "typings": "./dist/index.d.ts",
@@ -28,5 +28,5 @@
28
28
  "cli": {
29
29
  "webpack": false
30
30
  },
31
- "gitHead": "30de32ace907277b336c7bc835e659b2a3ffaac9"
31
+ "gitHead": "c4cdbc238e286e33a9eea2b8945e2820b137b916"
32
32
  }