Skip to content

Commit c67512e

Browse files
committed
feat(database): add carbon footprint column in database and a script to patch the data
1 parent e3fb96e commit c67512e

13 files changed

Lines changed: 566 additions & 23 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: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import ProgressBar from 'progress';
2+
import config from '../config';
3+
import emissionsCoef from '../data/emissions/coefficients.json';
4+
import { PoolClient } from 'pg';
5+
6+
async function run(): Promise<void> {
7+
const pool = await config.pg();
8+
9+
const client = await pool.connect();
10+
11+
const progressBar = new ProgressBar(
12+
'-> loading [:bar] :percent (:etas remaining)',
13+
{
14+
width: 40,
15+
complete: '=',
16+
incomplete: ' ',
17+
renderThrottle: 500,
18+
total: Object.keys(emissionsCoef).length,
19+
}
20+
);
21+
22+
// For each coefficient in the JSON file, update the corresponding products.
23+
config.logger.info('Updating product emissions');
24+
25+
for (const [key, coefficient] of Object.entries(emissionsCoef)) {
26+
const [_, skuId, region] = key.split('_');
27+
await updateProductEmissions(client, skuId, region, coefficient);
28+
progressBar.tick();
29+
}
30+
}
31+
32+
const updateProductEmissions = async (
33+
client: PoolClient,
34+
skuId: string,
35+
region: string,
36+
coefficient: number
37+
): Promise<void> => {
38+
const emissionData = JSON.stringify([
39+
{
40+
emissionHash: 'emissionHash',
41+
unit: 'kgCO2e',
42+
emissions: coefficient,
43+
startUsageAmount: 0,
44+
},
45+
]);
46+
47+
await client.query(
48+
`
49+
UPDATE "products"
50+
SET "emissions" = $1
51+
WHERE "sku" = $2 AND "region" = $3
52+
`,
53+
[emissionData, skuId, region]
54+
);
55+
};
56+
57+
config.logger.info('Starting: loading data into DB');
58+
run()
59+
.then(() => {
60+
config.logger.info('Completed: loading data into DB');
61+
process.exit(0);
62+
})
63+
.catch((err) => {
64+
config.logger.error(err);
65+
process.exit(1);
66+
});

src/data/emissions/coefficients.json

Lines changed: 473 additions & 0 deletions
Large diffs are not rendered by default.

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: 2 additions & 1 deletion
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 = {
@@ -30,7 +31,7 @@ export type Price = {
3031
export type Emission = {
3132
emissionHash: string;
3233
unit: string;
33-
CO2e: string;
34+
emissions: string;
3435
effectiveDateStart: string;
3536
effectiveDateEnd?: string;
3637
startUsageAmount?: string;

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);

0 commit comments

Comments
 (0)