Skip to content

Commit f0caf3f

Browse files
committed
feat(CO2): add column in db for emissions
1 parent e3fb96e commit f0caf3f

10 files changed

Lines changed: 122 additions & 21 deletions

File tree

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"data:dump": "node dist/cmd/dataDump.js",
1414
"data:download": "node dist/cmd/dataDownload.js",
1515
"data:load": "node dist/cmd/dataLoad.js",
16+
"data:patchEmissions": "node dist/cmd/dataPatchEmissions.js",
1617
"data:status": "node dist/cmd/dataStatus.js",
1718
"job:init": "npm run db:setup && npm run job:update",
1819
"job:update": "npm run data:download && npm run data:load",
@@ -21,6 +22,7 @@
2122
"data:dump:dev": "ts-node src/cmd/dataDump.ts",
2223
"data:download:dev": "ts-node src/cmd/dataDownload.ts",
2324
"data:load:dev": "ts-node src/cmd/dataLoad.ts",
25+
"data:patchEmissions:dev": "ts-node src/cmd/dataPatchEmissions.ts",
2426
"data:status:dev": "ts-node src/cmd/dataStatus.ts",
2527
"job:init:dev": "npm run db:setup:dev && npm run job:update:dev",
2628
"job:update:dev": "npm run data:download:dev && npm run data:load:dev",

src/cmd/dataPatchEmissions.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import ProgressBar from 'progress';
2+
import config from '../config';
3+
import { PoolClient } from 'pg';
4+
5+
const batchSize = 10000;
6+
7+
async function run(): Promise<void> {
8+
const pool = await config.pg();
9+
10+
const client = await pool.connect();
11+
try {
12+
// Fetch all product hashes
13+
config.logger.info('Fetching all product hashes');
14+
const productHashes = await getProductHashes(client);
15+
16+
const progressBar = new ProgressBar(
17+
'-> loading [:bar] :percent (:etas remaining)',
18+
{
19+
width: 40,
20+
complete: '=',
21+
incomplete: ' ',
22+
renderThrottle: 500,
23+
total: productHashes.length,
24+
}
25+
);
26+
27+
// For each product hash, insert by batch the emissions data into the product table
28+
config.logger.info('Updating product emissions');
29+
for (let i = 0; i < productHashes.length; i += batchSize) {
30+
const batch = productHashes.slice(i, i + batchSize);
31+
32+
await client.query('BEGIN');
33+
34+
await Promise.all(batch.map((productHash) => {
35+
updateProductEmissions(client, productHash);
36+
progressBar.tick();
37+
}));
38+
39+
await client.query('COMMIT');
40+
}
41+
42+
// await setEmissionsUpdateSuccessful(client);
43+
} catch (e) {
44+
await client.query('ROLLBACK');
45+
46+
// await setEmissionsUpdateFailed(client);
47+
48+
throw e;
49+
}
50+
51+
}
52+
53+
async function getProductHashes(client: PoolClient): Promise<string[]> {
54+
const result = await client.query(
55+
`
56+
SELECT "productHash"
57+
FROM "products"
58+
`
59+
);
60+
61+
return result.rows.map((row) => row.productHash);
62+
}
63+
64+
async function updateProductEmissions(client: PoolClient, productHash: string): Promise<void> {
65+
const emissionsData = generateEmissionsData();
66+
await client.query(
67+
`
68+
UPDATE "products"
69+
SET "emissions" = $1
70+
WHERE "productHash" = $2
71+
`,
72+
[emissionsData, productHash]
73+
);
74+
}
75+
76+
const generateEmissionsData = () => {
77+
return JSON.stringify([{
78+
emissionHash: 'SampleEmissionHash',
79+
unit: 'kgeqCO2',
80+
CO2e: Math.random() * 100,
81+
effectiveDateStart: '2020-01-01',
82+
effectiveDateEnd: '2024-12-31',
83+
startUsageAmount: 0,
84+
endUsageAmount: 100,
85+
description: 'Sample Description',
86+
}]);
87+
};
88+
89+
config.logger.info('Starting: loading data into DB');
90+
run()
91+
.then(() => {
92+
config.logger.info('Completed: loading data into DB');
93+
process.exit(0);
94+
})
95+
.catch((err) => {
96+
config.logger.error(err);
97+
process.exit(1);
98+
});

src/db/query.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// In order to make upserting more efficient, prices in postgres are stored as a map of priceHash -> prices.
22
import format from 'pg-format';
3-
import { Price, Product, ProductAttributes } from './types';
3+
import { Emission, Price, Product, ProductAttributes } from './types';
44
import config from '../config';
55

66
type AttributeFilter = {
@@ -10,7 +10,7 @@ type AttributeFilter = {
1010
value_regex?: string;
1111
};
1212

13-
type ProductWithPriceMap = {
13+
type ProductWithPriceAndEmissionsMap = {
1414
productHash: string;
1515
sku: string;
1616
vendorName: string;
@@ -19,10 +19,11 @@ type ProductWithPriceMap = {
1919
productFamily: string;
2020
attributes: ProductAttributes;
2121
prices: { [priceHash: string]: Price[] };
22+
emissions: { [emissionHash: string]: Emission[] };
2223
};
2324

24-
function flattenPrices(p: ProductWithPriceMap): Product {
25-
return { ...p, prices: Object.values(p.prices).flat() };
25+
function flattenPrices(p: ProductWithPriceAndEmissionsMap): Product {
26+
return { ...p, prices: Object.values(p.prices).flat(), emissions: Object.values(p.emissions).flat() };
2627
}
2728

2829
// eslint-disable-next-line import/prefer-default-export
@@ -53,7 +54,7 @@ export async function findProducts(
5354
}
5455

5556
const response = await pool.query(sql);
56-
const products = response.rows as ProductWithPriceMap[];
57+
const products = response.rows as ProductWithPriceAndEmissionsMap[];
5758
return products.map((product) => flattenPrices(product));
5859
}
5960

src/db/setup.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ export async function createProductsTable(
1818
service text NOT NULL,
1919
"productFamily" text DEFAULT ''::text NOT NULL,
2020
attributes jsonb NOT NULL,
21-
prices jsonb NOT NULL,
21+
prices jsonb NOT NULL,
22+
emissions jsonb NOT NULL DEFAULT '[]'::jsonb,
2223
CONSTRAINT %I PRIMARY KEY("productHash")
23-
)
24+
)
2425
`,
2526
tableName,
2627
`${tableName}_pkey`
@@ -72,7 +73,7 @@ export async function createStatsTable(
7273
total_runs bigint DEFAULT 0,
7374
ci_runs bigint DEFAULT 0,
7475
non_ci_runs bigint DEFAULT 0
75-
)
76+
)
7677
`,
7778
tableName
7879
)
@@ -101,7 +102,7 @@ export async function createInstallsTable(
101102
(
102103
install_id uuid PRIMARY KEY NOT NULL,
103104
created_at timestamp DEFAUlT NOW() NOT NULL
104-
)
105+
)
105106
`,
106107
tableName
107108
)

src/db/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export type Product = {
99
productFamily: string;
1010
attributes: ProductAttributes;
1111
prices: Price[];
12+
emissions: Emission[];
1213
};
1314

1415
export type Price = {

src/resolvers.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,12 @@ const getResolvers = <TContext>(
8282
return prices;
8383
},
8484
emissions: async (product: Product, args: EmissionsArgs): Promise<Emission[]> => {
85-
const emission = {
86-
emissionHash: 'sampleEmissionHash',
87-
unit: 'kgeqCO2',
88-
CO2e: '10',
89-
effectiveDateStart: '2021-01-01',
90-
effectiveDateEnd: '2021-12-31',
91-
startUsageAmount: '0',
92-
endUsageAmount: '100',
93-
description: 'test',
94-
};
95-
96-
return [emission];
85+
86+
const emissions = mingo
87+
.find(product.emissions, transformFilter(args.filter))
88+
.all() as Emission[];
89+
90+
return emissions;
9791
},
9892
},
9993
Price:

src/scrapers/awsBulk.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ function parseProduct(productJson: ProductJson) {
207207
sku: productJson.sku,
208208
attributes: productJson.attributes,
209209
prices: [],
210+
emissions: [],
210211
};
211212

212213
product.productHash = generateProductHash(product);

src/scrapers/azureRetail.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ function parseProduct(productJson: ProductJson): Product {
139139
meterName: productJson.meterName,
140140
},
141141
prices: [],
142+
emissions: [],
142143
};
143144

144145
product.productHash = generateProductHash(product);

src/scrapers/gcpCatalog.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ function parseProduct(productJson: ProductJson, region: string): Product {
191191
resourceGroup: productJson.category.resourceGroup,
192192
},
193193
prices: [],
194+
emissions: [],
194195
};
195196

196197
product.productHash = generateProductHash(product);

src/scrapers/gcpMachineTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ async function scrape(): Promise<void> {
129129
machineType: name,
130130
},
131131
prices: [],
132+
emissions: [],
132133
};
133134

134135
product.productHash = generateProductHash(product);

0 commit comments

Comments
 (0)