import dotenv from 'dotenv';
dotenv.config();
import OpenAI from 'openai';
import { readFileSync, readdirSync } from 'fs';
import { resolve } from 'path';
async function testExisting2021PNGs() {
console.log('π§ͺ Testing OpenAI Vision with Existing 2021 PNG Images');
console.log('ββββββββββββββββββββββββββββββββββββββ\n');
const apiKey = process.env.OPENAI_API_KEY;
if (!apiKey) {
console.error('β OPENAI_API_KEY not found!');
process.exit(1);
}
// Path to the 2021 PNG images
const pngPath = '/Users/josuekongolo/Downloads/mcp/companyiq-mcp/data/pdfs/png_images/999059198/aarsregnskap_999059198-2021';
// List PNG files
const pngFiles = readdirSync(pngPath)
.filter(f => f.endsWith('.png'))
.sort((a, b) => {
const pageA = parseInt(a.match(/page_(\d+)/)?.[1] || '0');
const pageB = parseInt(b.match(/page_(\d+)/)?.[1] || '0');
return pageA - pageB;
});
console.log(`π Found ${pngFiles.length} PNG files in:`);
console.log(` ${pngPath}\n`);
if (pngFiles.length === 0) {
console.error('β No PNG files found!');
process.exit(1);
}
const openai = new OpenAI({ apiKey });
console.log('π‘ Norwegian Γ₯rsregnskap show FULL values in NOK');
console.log(' Values are NOT in thousands (TNOK)');
console.log('\nπ Starting extraction from PNG images...\n');
let combinedData = {
revenue: null,
profit: null,
assets: null,
equity: null,
year: 2021
};
const startTime = Date.now();
// Process max 10 pages (to save API calls)
const maxPages = Math.min(10, pngFiles.length);
for (let i = 0; i < maxPages; i++) {
const pngFile = pngFiles[i];
const pngFilePath = resolve(pngPath, pngFile);
console.log(`π Analyzing ${pngFile} (page ${i + 1}/${maxPages})...`);
try {
// Read PNG file and convert to base64
const imageBuffer = readFileSync(pngFilePath);
const imageBase64 = imageBuffer.toString('base64');
// Call OpenAI Vision API
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{
role: "system",
content: "You are an expert Norwegian accountant reading Γ₯rsregnskap (annual financial statements). Extract key financial metrics accurately. Values in Norwegian financial statements are shown in FULL NOK amounts, NOT thousands."
},
{
role: "user",
content: [
{
type: "text",
text: `Extract these financial metrics from this Norwegian Γ₯rsregnskap page:
1. Driftsinntekter/Salgsinntekt (Total Revenue)
2. Γ
rsresultat (Net Profit/Loss after tax)
3. Sum eiendeler (Total Assets)
4. Sum egenkapital (Total Equity)
IMPORTANT: Norwegian Γ₯rsregnskap display FULL values in NOK, not TNOK.
Example: "361 863 744" means 361,863,744 NOK (NOT thousands).
Return ONLY a JSON object:
{
"revenue": <number or null>,
"profit": <number or null>,
"assets": <number or null>,
"equity": <number or null>,
"year": 2021,
"notes": "<what you found on this page>"
}`
},
{
type: "image_url",
image_url: {
url: `data:image/png;base64,${imageBase64}`
}
}
]
}
],
max_tokens: 500,
temperature: 0
});
const content = response.choices[0]?.message?.content || '';
console.log(` Response preview: ${content.substring(0, 100)}...`);
// Parse JSON from response
const jsonMatch = content.match(/```json\n?([\s\S]*?)\n?```/) || content.match(/\{[\s\S]*\}/);
if (jsonMatch) {
const jsonStr = jsonMatch[1] || jsonMatch[0];
const pageData = JSON.parse(jsonStr);
console.log(` β
Found: ${pageData.notes || 'Data extracted'}`);
// Update combined data
if (pageData.revenue !== null && combinedData.revenue === null) {
combinedData.revenue = pageData.revenue;
console.log(` π° Revenue: ${(pageData.revenue / 1000000).toFixed(1)}M NOK`);
}
if (pageData.profit !== null && combinedData.profit === null) {
combinedData.profit = pageData.profit;
console.log(` π Profit: ${(pageData.profit / 1000000).toFixed(1)}M NOK`);
}
if (pageData.assets !== null && combinedData.assets === null) {
combinedData.assets = pageData.assets;
console.log(` π’ Assets: ${(pageData.assets / 1000000).toFixed(1)}M NOK`);
}
if (pageData.equity !== null && combinedData.equity === null) {
combinedData.equity = pageData.equity;
console.log(` π Equity: ${(pageData.equity / 1000000).toFixed(1)}M NOK`);
}
// Stop if we have all values
if (combinedData.revenue && combinedData.profit && combinedData.assets && combinedData.equity) {
console.log('\nβ
Found all 4 metrics! Stopping search.');
break;
}
} else {
console.log(' β οΈ No data found on this page');
}
} catch (error) {
console.error(` β Error processing ${pngFile}:`, error.message);
}
}
const duration = Math.round((Date.now() - startTime) / 1000);
console.log('\nββββββββββββββββββββββββββββββββββββββ');
console.log('π FINAL EXTRACTION RESULTS FOR 2021');
console.log('ββββββββββββββββββββββββββββββββββββββ\n');
console.log(`β±οΈ Time: ${duration} seconds\n`);
const formatValue = (name, value) => {
if (value === null) {
console.log(`${name}: Not found`);
return;
}
// Format based on size
if (Math.abs(value) < 1000000) {
const thousands = value / 1000;
console.log(`${name}: ${value.toLocaleString('no-NO')} NOK (${thousands.toFixed(1)}K)`);
} else {
const millions = value / 1000000;
console.log(`${name}: ${value.toLocaleString('no-NO')} NOK (${millions.toFixed(1)}M)`);
}
};
formatValue('π° Revenue', combinedData.revenue);
formatValue('π Profit', combinedData.profit);
formatValue('π’ Assets', combinedData.assets);
formatValue('π Equity', combinedData.equity);
// Check if values look reasonable
console.log('\nπ Value Analysis:');
const allValuesReasonable =
(!combinedData.revenue || combinedData.revenue < 10000000000) && // Less than 10 billion
(!combinedData.profit || Math.abs(combinedData.profit) < 1000000000) && // Less than 1 billion
(!combinedData.assets || combinedData.assets < 10000000000) &&
(!combinedData.equity || combinedData.equity < 10000000000);
if (allValuesReasonable) {
console.log('β
Values appear correct (no TNOK multiplication detected)');
console.log(' All values are in reasonable ranges for a Norwegian company');
} else {
console.log('β οΈ Some values seem too large (possible TNOK issue)');
}
// Save the extracted data
console.log('\nπΎ Extracted data for year 2021:');
console.log(JSON.stringify(combinedData, null, 2));
console.log('\nβ
Test completed successfully!');
}
testExisting2021PNGs();