Skip to content

Commit

Permalink
Merge pull request #134 from Klimatbyran/feature/clarify-verified-data
Browse files Browse the repository at this point in the history
Feature/clarify verified data
  • Loading branch information
Greenheart authored Jun 18, 2024
2 parents bb1a3c2 + 59e7bd2 commit 07f838a
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 81 deletions.
15 changes: 15 additions & 0 deletions src/components/VerifiedBadge.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
import { cn } from '@/lib/utils'
import LucideBadgeCheck from 'icons:astro/lucide/badge-check'
interface Props {
title?: string
class?: string
}
const { title = 'Verifierat', class: className } = Astro.props
---

<div class={cn('cursor-help p-1', className)} {title} aria-label={title}>
<LucideBadgeCheck class="h-4 w-4 text-green-250" />
</div>
43 changes: 24 additions & 19 deletions src/components/company/CompanyEmissions.astro
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
---
import { getCompanyName, type CompanyData } from '@/data/companyData'
import { type CompanyData } from '@/data/companyData'
import { Card } from '../ui/card'
import Scope3Emissions from './Scope3Emissions.astro'
import { cn } from '@/lib/utils'
import VerifiedBadge from '../VerifiedBadge.astro'
import type { EmissionsScope } from '@/data/companyData'
import type { Emissions } from '@/data/companyData'
interface Props {
company: CompanyData
Expand All @@ -12,34 +15,43 @@ interface Props {
const { company, year } = Astro.props
const emissions = company.emissions[year]
const facit = company.facit?.emissions?.[year]
const wiki = company.wikidata?.emissions?.[year]
const totalBiogenicEmissions = emissions
? (emissions.scope1.biogenic || 0) +
(emissions.scope2.biogenic || 0) +
(emissions.scope3.biogenic || 0)
: 0
function getValueAndVerified(scope: 'scope1' | 'scope2' | 'scope3') {
const facitValue = facit?.[scope]?.emissions
const wikiValue = wiki?.[scope]?.emissions
const value = emissions[scope].emissions
return {
value: facitValue ?? wikiValue ?? value,
verified: facitValue !== null || wikiValue !== null,
}
}
const scopeEmissionsList = emissions
? [
{
title: 'Egna utsläpp (scope 1)',
description:
'Utsläpp från egna källor eller kontrollerade av organisationen.',
value: emissions.scope1.emissions,
verified: emissions.scope1.verified,
...getValueAndVerified('scope1'),
},
{
title: 'Indirekta (scope 2)',
description:
'Indirekta utsläpp från produktion av köpt el, ånga, värme och kyla som konsumeras av organisationen.',
value: emissions.scope2.emissions,
verified: emissions.scope2.verified,
...getValueAndVerified('scope2'),
},
{
title: 'Värdekedjan (scope 3)',
description: 'Indirekta utsläpp från organisationens värdekedja.',
value: emissions.scope3.emissions,
verified: emissions.scope3.verified,
...getValueAndVerified('scope3'),
},
]
: []
Expand All @@ -51,8 +63,6 @@ const biogenicEmissions = {
value: totalBiogenicEmissions,
verified: null,
}
const companyName = getCompanyName(company)
---

<Card class="grid gap-8" level={1}>
Expand All @@ -70,19 +80,14 @@ const companyName = getCompanyName(company)
emission.value ? (
<div class="flex items-center justify-between">
<div>
<h4 class="font-medium">{emission.title}</h4>
<h4 class="flex items-center gap-1 font-medium">
{emission.title} {emission.verified && <VerifiedBadge />}
</h4>
{emission.description && (
<p class="text-sm text-muted">{emission.description}</p>
)}
</div>
<span
class={cn(
'rounded-full px-4 py-2 font-bold md:text-base',
emission.verified
? 'bg-blue-250 text-blue-950'
: 'bg-gray-800',
)}
>
<span class="rounded-full bg-gray-800 px-4 py-2 font-bold md:text-base">
{emission.value.toLocaleString('sv-SE')}
</span>
</div>
Expand All @@ -92,7 +97,7 @@ const companyName = getCompanyName(company)
) : null
}

<Scope3Emissions emissions={emissions.scope3.categories} {companyName} />
<Scope3Emissions {company} {year} />

{
totalBiogenicEmissions ? (
Expand Down
6 changes: 5 additions & 1 deletion src/components/company/CompanyFacts.astro
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
import { getCompanyName, type CompanyData } from '@/data/companyData'
import { Card } from '../ui/card'
import VerifiedBadge from '../VerifiedBadge.astro'
interface Props {
company: CompanyData
Expand Down Expand Up @@ -110,7 +111,10 @@ const totalEmissionsVerified =
emissions.scope3.emissions) && (
<Card level={2} class="flex flex-col justify-between gap-4">
<div class="flex items-start justify-between">
<h3>Totala utsläpp (2023)</h3>
<h3 class="flex items-center gap-1">
Totala utsläpp (2023){' '}
{totalEmissionsVerified && <VerifiedBadge />}
</h3>
<span class="text-muted">(ton CO₂e)</span>
</div>

Expand Down
40 changes: 32 additions & 8 deletions src/components/company/Scope3Emissions.astro
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
---
import type { EmissionsScope, Scope3Category } from '@/data/companyData'
import {
getCompanyName,
type CompanyData,
type EmissionsScope,
type Scope3Category,
} from '@/data/companyData'
import Fa6SolidGifts from 'icons:astro/fa6-solid/gifts'
import MingcuteWashMachineLine from 'icons:astro/mingcute/wash-machine-line'
import IconParkOutlineSolarEnergyOne from 'icons:astro/icon-park-outline/solar-energy-one'
Expand All @@ -19,11 +24,11 @@ import Scope3EmissionsCategory from '../company/Scope3EmissionsCategory.astro'
import { Card } from '../ui/card'
interface Props {
emissions: EmissionsScope['categories']
companyName: string
company: CompanyData
year: number
}
const { emissions, companyName } = Astro.props
const { company, year } = Astro.props
type CategoryDefinition = {
title: string
Expand All @@ -34,6 +39,7 @@ type CategoryDefinition = {
type ReportedCategoryDefinition = CategoryDefinition & {
value: number | null
category: Scope3Category
verified: boolean
}
export const categoryDefinitions: Record<Scope3Category, CategoryDefinition> = {
Expand Down Expand Up @@ -132,13 +138,26 @@ function getReportedCategories(
): ReportedCategoryDefinition[] {
return Object.entries(categoryDefinitions).reduce(
(categories, [category, definition]) => {
const facitValue =
company.facit?.emissions?.[year]?.scope3?.categories?.[
category as Scope3Category
] ?? null
const wikiValue =
(company.wikidata?.emissions &&
Object.values(company.wikidata?.emissions).findLast(
(yearlyEmissions) => yearlyEmissions.year === String(year),
)?.scope3?.categories?.[category as Scope3Category]) ??
null
const value = emissions?.[category as Scope3Category] ?? null
if (value || definition) {
if (facitValue || wikiValue || value || definition) {
categories.push({
...definition,
category: category as Scope3Category,
value,
value: facitValue ?? wikiValue ?? value,
verified: facitValue !== null || wikiValue !== null,
})
}
return categories
Expand All @@ -147,6 +166,9 @@ function getReportedCategories(
)
}
const companyName = getCompanyName(company)
const emissions = company.emissions[year].scope3.categories
const reportedCategories = getReportedCategories(emissions)
const reportedCount = reportedCategories.filter((c) => c.value !== null).length
Expand Down Expand Up @@ -197,11 +219,12 @@ const nonStandardCategoriesCount = getNonStandardCategories(emissions).length
Uppströms
<span class="pr-4 text-base text-muted xs:text-lg">(ton CO₂e)</span>
</h4>
{upstream.map(({ Icon, title, description, value }) => (
{upstream.map(({ Icon, title, description, value, verified }) => (
<Scope3EmissionsCategory
{Icon}
{title}
{description}
{verified}
value={value?.toLocaleString('sv-SE')}
/>
))}
Expand All @@ -214,11 +237,12 @@ const nonStandardCategoriesCount = getNonStandardCategories(emissions).length
Nedströms
<span class="pr-4 text-base text-muted xs:text-lg">(ton CO₂e)</span>
</h4>
{downstream.map(({ Icon, title, description, value }) => (
{downstream.map(({ Icon, title, description, value, verified }) => (
<Scope3EmissionsCategory
{Icon}
{title}
{description}
{verified}
value={value?.toLocaleString('sv-SE')}
/>
))}
Expand Down
18 changes: 13 additions & 5 deletions src/components/company/Scope3EmissionsCategory.astro
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
---
import { cn } from '@/lib/utils'
import VerifiedBadge from '../VerifiedBadge.astro'
interface Props {
Icon?: astroHTML.JSX.Element
title: string
description: string
value?: string | null
verified?: string | null
verified?: boolean | null
}
const { Icon, title, description, value, verified } = Astro.props
Expand All @@ -30,14 +31,21 @@ const hasValue = value !== null && value !== undefined
}
</div>
<div>
<div class={cn('font-medium', !hasValue && 'text-muted')}>{title}</div>
<div class="text-xs text-muted sm:text-sm">{description}</div>
<h4
class={cn(
'font-medium flex gap-1 items-center',
!hasValue && 'text-muted',
)}
>
{title}
{verified && <VerifiedBadge />}
</h4>
<p class="text-xs text-muted sm:text-sm">{description}</p>
</div>
</div>
<span
class={cn(
'rounded-full px-4 py-2 text-sm font-bold',
verified ? 'bg-blue-250 text-blue-950' : 'bg-gray-800',
'rounded-full px-4 py-2 text-sm font-bold bg-gray-800',
!hasValue && 'text-muted',
)}>{value ?? '-'}</span
>
Expand Down
3 changes: 3 additions & 0 deletions src/data/companies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ const cachedCompanies = createCache<'companies', CompanyData[]>({
maxAge: FIVE_MINUTES,
})

// TODO: improve filtering to only keep the companies that have facit or wikidata properties
// And secondly, prefer later hits (as long as they include more information

const keepUniqueCompanies = (c: CompanyData, i: number, array: CompanyData[]) =>
i === array.findLastIndex((company) => company.wikidataId === c.wikidataId)

Expand Down
60 changes: 12 additions & 48 deletions src/data/companyData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,53 +107,6 @@ export interface Initiative {
scope: string
}

/*
{
"node": "Q123456",
"url": "https://www.wikidata.org/wiki/Q123456",
"logo": "https://commons.wikimedia.org/wiki/File:Example.jpg",
"label": "Company Name",
"description": "Company Description",
"url": "https://example.com",
"emissions": [
{
"year": "2019",
"reference": "https://example.com",
"scope1": {
"emissions": 1234,
"biogenic": 123,
"unit": "tCO2e"
},
"scope2": {
"emissions": 1235,
"unit": "tCO2e",
"mb": 1235,
"lb": 125
},
"scope3": {
"emissions": null,
"unit": "tCO2e",
"categories": {
"1_purchasedGoods": 100000000,
"2_capitalGoods": 100000000,
"3_fuelAndEnergyRelatedActivities": 100000000,
"4_upstreamTransportationAndDistribution": 100000000,
"5_wasteGeneratedInOperations": 100000000,
"6_businessTravel": 100000000,
"7_employeeCommuting": 100000000,
"8_upstreamLeasedAssets": 100000000,
"9_downstreamTransportationAndDistribution": 100000000,
"10_processingOfSoldProducts": 100000000,
"11_useOfSoldProducts": 100000000,
"12_endOfLifeTreatmentOfSoldProducts": 100000000,
"13_downstreamLeasedAssets": 100000000,
"14_franchises": 100000000,
"15_investments": 100000000,
"16_other": 100000000
}
}
}
*/
export interface Wikidata {
node: string
url: string
Expand All @@ -171,6 +124,14 @@ export type FiscalYear = {
endMonth: number
}

// TODO: EmissionScope actually doesn't include `vefrified` for facit.
// Update the type for EmissionScope and omit the verified field, but only when used as part of Facit.
export type Facit = {
companyName: string
url: string
emissions: Emissions
}

export interface CompanyData {
companyName: string
description: string
Expand All @@ -187,14 +148,17 @@ export interface CompanyData {
initiatives: Initiative[]
reliability: string
wikidata: Wikidata
facit?: Facit
needsReview: boolean
publicComment: string
reviewComment: string
fiscalYear?: FiscalYear
}

export function getCompanyName(company: CompanyData) {
return company.wikidata?.label || company.companyName
return (
company.facit?.companyName || company.wikidata?.label || company.companyName
)
}

export function getCompanyURL(company: CompanyData) {
Expand Down

0 comments on commit 07f838a

Please sign in to comment.