Skip to content

Commit 93a5f85

Browse files
committed
add CO2 estimation
1 parent 9f73b52 commit 93a5f85

File tree

11 files changed

+121
-44
lines changed

11 files changed

+121
-44
lines changed

packages/data-analyzer/src/services/country.ts

+37-8
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,36 @@
11
import geoip from 'geoip-country'
22

3+
import { CO2PerKWHCountry } from '@data-collector/types'
4+
const co2PerKWH = require('../data/co2PerKwhPerCountry.json') as {
5+
[countryISO: string]: CO2PerKWHCountry
6+
}
7+
8+
const valuesArray = Object.values(co2PerKWH)
9+
const total = valuesArray.reduce(
10+
(prevValue, country) => (prevValue += country.CO2perKWH),
11+
0
12+
)
13+
const length = valuesArray.length
14+
15+
const average = total / length
16+
17+
const kwhPerGB = 1.8
18+
const kwhPerByte = kwhPerGB / (1024 * 1024 * 1024)
19+
320
class Country {
21+
// @ts-ignore
22+
private regionNames = new Intl.DisplayNames(['en'], { type: 'region' })
23+
424
getCountry = (ip?: string) => {
25+
const res = { countryCode: '', countryName: '' }
526
try {
6-
if (!ip) return ''
7-
const country = this.lookUp(ip)
8-
return country || ''
27+
if (!ip) return res
28+
res.countryCode = this.lookUp(ip) || ''
29+
res.countryName = this.regionNames.of(res.countryCode)
30+
return res
931
} catch (e) {
1032
console.log(e)
11-
return ''
33+
return res
1234
}
1335
}
1436

@@ -17,10 +39,17 @@ class Country {
1739
return countryInfo?.country || undefined
1840
}
1941

20-
// TODO: Create estimation
21-
calculateEmission = (country?: string) => {
22-
if (!country) return 0
23-
return 0
42+
calculateEmission = ({
43+
size,
44+
countryCode = ''
45+
}: {
46+
size?: number | null
47+
countryCode?: string
48+
}) => {
49+
if (!size) return 0
50+
const infoAboutCountry = co2PerKWH[countryCode]
51+
const CO2perKWH = infoAboutCountry?.CO2perKWH || average
52+
return size * kwhPerByte * CO2perKWH
2453
}
2554
}
2655

packages/data-analyzer/src/services/network-call-controller.ts

+12-7
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,10 @@ export class NetworkCallController {
101101
usageId: string
102102
) => {
103103
const paths = hostOrigin.split('://')
104-
const identifier = paths.length > 1 ? paths[1] : ''
104+
let identifier = paths.length > 1 ? paths[1] : ''
105+
if (identifier.startsWith('www')) {
106+
identifier = identifier.substr(2)
107+
}
105108
const uid = this.getDocId({
106109
identifier,
107110
date: networkCall.date,
@@ -119,17 +122,19 @@ export class NetworkCallController {
119122

120123
setCountryDoc = async (
121124
networkCall: BaseUsageDoc,
122-
country: string,
125+
countryCode: string,
126+
countryName: string,
123127
usageId: string
124128
) => {
125129
const uid = this.getDocId({
126-
identifier: country,
130+
identifier: countryCode,
127131
date: networkCall.date,
128132
userId: networkCall.userId
129133
})
130134
const countryDoc: CountryDoc = {
131135
...networkCall,
132-
country,
136+
countryCode,
137+
countryName,
133138
usageId,
134139
type: USAGE_TYPES.COUNTRY,
135140
uid
@@ -148,8 +153,8 @@ export class NetworkCallController {
148153
const { hostOrigin, size, targetIP } = networkCall
149154

150155
const date = getStartOfDateInUnix(new Date())
151-
const targetCountry = Country.getCountry(targetIP)
152-
const CO2 = Country.calculateEmission(targetCountry)
156+
const { countryCode, countryName } = Country.getCountry(targetIP)
157+
const CO2 = Country.calculateEmission({ size, countryCode })
153158
const usageId = this.getDocId({ userId, date })
154159
const baseUsageDoc: BaseUsageDoc = {
155160
uid: usageId,
@@ -164,7 +169,7 @@ export class NetworkCallController {
164169
const promises = [
165170
this.updateUserStats(baseUsageDoc),
166171
this.setHostDoc(baseUsageDoc, hostOrigin || '', usageId),
167-
this.setCountryDoc(baseUsageDoc, targetCountry, usageId),
172+
this.setCountryDoc(baseUsageDoc, countryCode, countryName, usageId),
168173
this.setUsageDoc(baseUsageDoc)
169174
]
170175
try {

packages/data-collector/manifest.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
"webRequest",
3939
"tabs",
4040
"identity",
41-
"storage"
41+
"storage",
42+
"notifications"
4243
],
4344
"oauth2": {
4445
"client_id": "154072078565-c2qtni1qj3atn2ip7cgdeofpph8jjhhk.apps.googleusercontent.com",

packages/data-collector/src/Firestore.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,13 @@ export class Firestore {
5555
listenToTodaysUsage = (
5656
uid: string,
5757
date: number,
58-
callback: (usage: number) => void
58+
callback: (props: { size: number; CO2: number }) => void
5959
) => {
6060
const d = doc(this.usageCollection, `${uid}-${date}`)
61-
console.log(date)
6261
onSnapshot(d, (doc) => {
6362
const data = doc.data() as BaseUsageDoc
64-
callback(data.size)
63+
const usage = { size: data.size, CO2: data.CO2 }
64+
callback(usage)
6565
})
6666
}
6767
}

packages/data-collector/src/UsageCounter.ts

+27-7
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,18 @@ export class UsageCounter {
88
private totalUsage = 0
99
private usageSinceSubscriptionStarted = 0
1010
private store: typeof Store
11+
private totalCO2 = 0
1112

1213
constructor(store: typeof Store) {
1314
this.store = store
15+
setTimeout(() => {
16+
chrome.notifications.create('', {
17+
title: 'Update on your usage',
18+
message: `You have used ${this.usageToday} bytes today`,
19+
type: 'basic',
20+
iconUrl: './android-chrome-192x192.png'
21+
})
22+
}, 5000)
1423
}
1524

1625
private getUsageLast7Days = async () => {
@@ -26,6 +35,7 @@ export class UsageCounter {
2635
if (!this.store.user) return
2736
const userDoc = await this.store.firestore.getUserDoc(this.store.user.uid)
2837
this.totalUsage = userDoc.totalSize
38+
this.totalCO2 = userDoc.totalCO2
2939
}
3040

3141
private listenToTodaysUsage = async () => {
@@ -37,9 +47,19 @@ export class UsageCounter {
3747
)
3848
}
3949

40-
private handleUsageUpdate = (usage: number) => {
41-
if (!this.usageToday) this.usageToday = usage
42-
this.usageSinceSubscriptionStarted = usage - this.usageToday
50+
private handleUsageUpdate = ({
51+
size,
52+
CO2
53+
}: {
54+
size: number
55+
CO2: number
56+
}) => {
57+
if (!this.usageToday) this.usageToday = size
58+
this.usageSinceSubscriptionStarted = size - this.usageToday
59+
this.usageToday += this.usageSinceSubscriptionStarted
60+
this.usageLast7Days += this.usageSinceSubscriptionStarted
61+
this.totalUsage += this.usageSinceSubscriptionStarted
62+
this.totalCO2 += CO2
4363
this.sendUsageUpdate()
4464
}
4565

@@ -62,10 +82,10 @@ export class UsageCounter {
6282
chrome.runtime.sendMessage({
6383
type: MESSAGE_TYPES.SYNC_REQUESTS,
6484
payload: {
65-
usageToday: (this.usageToday || 0) + this.usageSinceSubscriptionStarted,
66-
usageLast7Days:
67-
this.usageLast7Days + this.usageSinceSubscriptionStarted,
68-
totalUsage: this.totalUsage + this.usageSinceSubscriptionStarted
85+
usageToday: this.usageToday,
86+
usageLast7Days: this.usageLast7Days,
87+
totalUsage: this.totalUsage,
88+
totalCO2: this.totalCO2
6989
}
7090
})
7191
}

packages/data-collector/src/wrapper.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ try {
2626
store.sendUser()
2727
return
2828
case MESSAGE_TYPES.REQUEST_USAGE:
29-
store.dataReporter.sendRequests()
29+
store.usageCounter.sendUsageUpdate()
3030
return
3131
case MESSAGE_TYPES.REQUEST_CREDENTIALS:
3232
store.auth.sendCredentials()

packages/data-presenter/src/components/dashboard/UsageDisplay.tsx

+15-7
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ interface UsageDisplay {
99

1010

1111
const UsageDisplay: FunctionalComponent = () => {
12-
const { usage } = useContext(UsageContext)
12+
const { usage, totalCO2 } = useContext(UsageContext)
1313
const [usageToDisplay, setUsageToDisplay] = useState<UsageDisplay[]>([])
1414

1515

@@ -55,13 +55,21 @@ const UsageDisplay: FunctionalComponent = () => {
5555
}, [usage])
5656

5757
return (
58-
<div className="flex justify-center">
59-
{usageToDisplay.map((usage) => (
60-
<div className="text-center px-3">
61-
<div className="text-4xl font-medium">{usage.usageString}</div>
62-
<div className="text-xs font-light">{usage.usageDescription}</div>
58+
<div>
59+
<div className="flex justify-center">
60+
{usageToDisplay.map((usage) => (
61+
<div className="text-center px-3">
62+
<div className="text-4xl font-medium">{usage.usageString}</div>
63+
<div className="text-xs font-light">{usage.usageDescription}</div>
64+
</div>
65+
))}
66+
</div>
67+
<div className="flex justify-center mt-3">
68+
<div className="text-center">
69+
<div className="text-6xl font-medium">{totalCO2.toFixed(2)}</div>
70+
<div className="text-xs font-light">kg CO2</div>
6371
</div>
64-
))}
72+
</div>
6573
</div>
6674
)
6775
}

packages/data-presenter/src/contexts/UsageContext.tsx

+15-9
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,36 @@ interface UsageContextProps {
88
usageToday: number
99
usageLast7Days: number
1010
totalUsage: number
11-
}
11+
},
12+
totalCO2: number
1213
}
1314

1415
const initialUsage = {
1516
usageToday: 0, usageLast7Days: 0, totalUsage: 0
1617
}
1718

18-
export const UsageContext = createContext<UsageContextProps>({ usage: initialUsage });
19+
export const UsageContext = createContext<UsageContextProps>({ usage: initialUsage, totalCO2: 0 });
1920

2021
const UsageProvider: FunctionComponent = ({ children }) => {
2122
const [usage, setUsage] = useState(initialUsage)
23+
const [totalCO2, setTotalCO2] = useState(0)
2224

23-
useEffect(() => {
24-
chrome.runtime.onMessage.addListener(function (details) {
25-
const { type } = details
25+
const parseMessage = (details: any) => {
26+
const { type } = details
2627
if (type === MESSAGE_TYPES.SYNC_REQUESTS) {
27-
setUsage(details.payload)
28+
const { usageToday, usageLast7Days, totalUsage, totalCO2 } = details.payload
29+
setUsage({ usageToday, usageLast7Days, totalUsage })
30+
setTotalCO2(totalCO2)
2831
}
29-
})
32+
}
33+
34+
useEffect(() => {
35+
chrome.runtime.onMessage.addListener(parseMessage)
3036

31-
chrome.runtime.sendMessage({ type: MESSAGE_TYPES.REQUEST_USAGE })
37+
chrome.runtime.sendMessage({ type: MESSAGE_TYPES.REQUEST_USAGE }, parseMessage)
3238
}, [])
3339

34-
return <UsageContext.Provider value={{ usage }}>{children}</UsageContext.Provider>;
40+
return <UsageContext.Provider value={{ usage, totalCO2 }}>{children}</UsageContext.Provider>;
3541
};
3642

3743
export default UsageProvider;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface CO2PerKWHCountry {
2+
country: string
3+
CO2perKWH: number
4+
source: string
5+
year: number
6+
}

packages/types/src/country-doc.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ import { BaseUsageDoc } from './base-usage-doc'
22

33
export interface CountryDoc extends BaseUsageDoc {
44
usageId: string
5-
country: string
5+
countryCode: string
6+
countryName: string
67
}

packages/types/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export { MESSAGE_TYPES } from './MESSAGE_TYPES'
44
export type { BaseUsageDoc } from './base-usage-doc'
55
export type { CountryDoc } from './country-doc'
66
export type { HostDoc } from './host-doc'
7+
export type { CO2PerKWHCountry } from './co2-per-kwh-country'

0 commit comments

Comments
 (0)