Skip to content

Commit

Permalink
Merge pull request #510 from Klimatbyran/staging
Browse files Browse the repository at this point in the history
Update garbo to production
  • Loading branch information
Greenheart authored Dec 17, 2024
2 parents 24eb5c1 + 72fad12 commit b5f1dd2
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 5 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ flowchart TB

## Get started

Ensure you have Node.js version 22.0.0 or higher installed. You will also need Docker to run Redis, PostgreSQL, and ChromaDB containers.
Ensure you have Node.js version 22.0.0 or higher installed. You will also need [Docker](https://www.docker.com/) (or [Podman](https://podman-desktop.io/)) to run containers.

### Setting up environment variables

Expand Down Expand Up @@ -178,7 +178,7 @@ Well done! You've now set up the `garbo` backend and are ready to start developm

These steps can be useful to test DB migrations with data similar to the production environment.

1. Recommended: Create a local test DB. This allows you to more easily
1. Recommended: Create a local test DB. This allows you to keep your regular development DB intact.

```sh
docker run -d -p 5432:5432 --name garbo_test_postgres -e POSTGRES_PASSWORD=mysecretpassword postgres
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"dev-api": "node --import tsx --watch src/index.ts --api-only",
"dev": "concurrently \"npm run dev-board\" \"npm run dev-workers\"",
"import": "node --import=tsx scripts/import-spreadsheet-companies.ts",
"update-report-url": "node --import=tsx scripts/update-spreadsheet-companies-urls.ts",
"test": "jest",
"prisma": "prisma",
"reset": "node --import tsx scripts/dev-reset.ts"
Expand Down
2 changes: 1 addition & 1 deletion prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ async function seedUsers() {
},
{
email: '[email protected]',
name: 'Alexandra Palmquist',
name: 'Alex (Klimatkollen)',
},
],
})
Expand Down
4 changes: 2 additions & 2 deletions scripts/import-spreadsheet-companies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const TOKENS = tokens.reduce<{ garbo: string; alex: string }>(
{} as any
)

const USERS = {
export const USERS = {
garbo: {
email: '[email protected]',
token: TOKENS.garbo,
Expand Down Expand Up @@ -289,7 +289,7 @@ function getReportingPeriods(
/**
* Get an array of numbers from start to end, with inclusive boundaries.
*/
function range(start: number, end: number) {
export function range(start: number, end: number) {
return Array.from({ length: end - start + 1 }, (_, i) => start + i)
}

Expand Down
121 changes: 121 additions & 0 deletions scripts/update-spreadsheet-companies-urls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { isMainModule } from './utils'
import 'dotenv/config'
import ExcelJS from 'exceljs'
import { resolve } from 'path'
import apiConfig from '../src/config/api'
import { postJSON, range, USERS } from './import-spreadsheet-companies'

const workbook = new ExcelJS.Workbook()
await workbook.xlsx.readFile(resolve('src/data/Company GHG data.xlsx'))

function getSheetHeaders({
sheet,
row,
}: {
sheet: ExcelJS.Worksheet
row: number
}) {
return Object.values(sheet.getRow(row).values!)
}

const { baseURL } = apiConfig

const HIDDEN_FROM_API = new Set([
'Q22629259',
'Q37562781',
'Q489097',
'Q10432209',
'Q5168854',
'Q115167497',
'Q549624',
'Q34',
'Q8301325',
'Q112055015',
'Q97858523',
'Q2438127',
'Q117352880',
'Q115167497',
])

async function updateReportURLs(years: number[]) {
const sheet = workbook.getWorksheet('import')!
const headers = getSheetHeaders({ sheet, row: 2 })

const baseCompanies = sheet
.getSheetValues()
.slice(3)
.reduce<{ wikidataId: string; name: string }[]>((companies, row) => {
if (!row) return companies
const columns = headers.reduce((acc, header, i) => {
acc[header!.toString()] = row[i + 1]?.result || row[i + 1]
return acc
}, {} as any)
if (!HIDDEN_FROM_API.has(columns['Wiki ID'])) {
companies.push({
wikidataId: columns['Wiki ID'],
name: columns['Company'],
})
}
return companies
}, [])

for (const year of years) {
const sheet = workbook.getWorksheet(year.toString())
if (!sheet) continue
const headers = getSheetHeaders({ sheet, row: 2 })

sheet
.getSheetValues()
.slice(3) // Skip header
.forEach((row) => {
if (!row) return

const columns = headers.reduce((acc, header, i) => {
const index = i + 1
acc[header!.toString()] =
row[index]?.result || row[index]?.hyperlink || row[index]
return acc
}, {})

const company = baseCompanies.find(
(c) =>
c.name.trim().toLowerCase() ===
columns['Company']?.trim().toLowerCase()
)

if (company && columns[`URL ${year}`]) {
const body = JSON.stringify({
year: year.toString(),
reportURL: columns[`URL ${year}`],
})

try {
return fetch(
`${baseURL}/companies/${company.wikidataId}/report-url`,
{
body,
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${USERS['garbo'].token}`,
},
}
)
} catch (error) {
console.error('Failed to fetch:', error)
throw error
}
}
})
}
}

async function main() {
const years = range(2015, 2023).reverse()
await updateReportURLs(years)
console.log('✅ Report URLs updated.')
}

if (isMainModule(import.meta.url)) {
await main()
}
31 changes: 31 additions & 0 deletions src/lib/prisma.ts
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,37 @@ export async function upsertReportingPeriod(
})
}

export async function updateReportingPeriodReportURL(
company: Company,
year: string,
reportURL: string
) {
const reportingPeriod = await prisma.reportingPeriod.findUnique({
where: {
reportingPeriodId: {
companyId: company.wikidataId,
year,
},
},
})

if (!reportingPeriod) {
return false
}

return prisma.reportingPeriod.update({
where: {
reportingPeriodId: {
companyId: company.wikidataId,
year,
},
},
data: {
reportURL,
},
})
}

export async function upsertEmissions({
emissionsId,
year,
Expand Down
38 changes: 38 additions & 0 deletions src/routes/updateCompanies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
upsertScope1And2,
deleteInitiative,
deleteGoal,
updateReportingPeriodReportURL,
} from '../lib/prisma'
import {
createMetadata,
Expand Down Expand Up @@ -514,6 +515,43 @@ router.post(
}
)

router.patch(
'/:wikidataId/report-url',
processRequest({
params: z.object({
wikidataId: z.string(),
}),
body: z.object({
year: z.string(),
reportURL: z.string().url(),
}),
}),
async (req, res) => {
const { reportURL, year } = req.body
const company = res.locals.company!

try {
const updatedPeriod = await updateReportingPeriodReportURL(
company,
year,
reportURL
)

res.json({
ok: true,
message: updatedPeriod
? 'Sucessfully updated reportUrl'
: ' No reporting period found',
})
} catch (error) {
throw new GarboAPIError('Failed to update reportUrl', {
original: error,
statusCode: 500,
})
}
}
)

router.use(
'/:wikidataId/:year',
validateReportingPeriod(),
Expand Down

0 comments on commit b5f1dd2

Please sign in to comment.