# cpv-eu

A TypeScript/JavaScript library for working with Common Procurement Vocabulary (CPV) codes used in EU public procurement. Supports **24 European languages** including English, German, French, Swedish and more.

## What is CPV?

The **Common Procurement Vocabulary (CPV)** is a standardized classification system used throughout the European Union for public procurement. It consists of a main vocabulary for defining the subject of a contract, and a supplementary vocabulary for adding further qualitative information.

## Features

- **9,454 CPV Codes**: Complete EU CPV vocabulary
- **Hierarchical Data**: Full CPV hierarchy with parent-child relationships
- **Multi-language Support**: 24 European languages including EN, DE, FR, SV, ES, IT, NL, PL, PT, and more
- **TypeScript Support**: Full type definitions included

## Installation

```bash
npm install cpv-eu
```

## Quick Start

```typescript
import { 
  getLabelByCPVCode, 
  getCategoryChildrenByCPVCode, 
  getAllCategories,
  getAllRaw,
  SupportedLanguages 
} from 'cpv-eu';

// Get a label for a CPV code - e.g.
const label = getLabelByCPVCode("45000000", "en");
console.log(label);

// Get children of a CPV code - e.g.
const children = getCategoryChildrenByCPVCode("45", "en");
console.log(children);

// Get all categories in a language - e.g.
const allCategories = getAllCategories("en", 2); // Up to level 2
console.log(allCategories);
```

## Core Functions

### `getLabelByCPVCode(cpvCode: string, language: SupportedLanguages): string`

Get the label for a specific CPV code in the specified language.

```typescript
import { getLabelByCPVCode, SupportedLanguages } from 'cpv-eu';

// Get English label
const label = getLabelByCPVCode("45000000", "en");
// Returns: "Construction work"

// Get Swedish label
const swedishLabel = getLabelByCPVCode("45000000", "sv");
// Returns: "Byggarbeten"

// Get German label
const germanLabel = getLabelByCPVCode("45000000", "de");
// Returns: "Bauarbeiten"
```

### `getCategoryChildrenByCPVCode(cpvCode: string, language: SupportedLanguages): StructuredCPVItem[]`

Get the immediate children of a CPV code.

```typescript
import { getCategoryChildrenByCPVCode } from 'cpv-eu';

// Get children of construction work
const children = getCategoryChildrenByCPVCode("45000000", "en");
// Returns array of child categories like:
// [
//   {
//     code: "45100000",
//     label: "Site preparation work",
//     emoji: "🏗️",
//     level: 2,
//     parent: "45000000"
//   },
//   // ... more children
// ]
```

### `getAllCategories(language: SupportedLanguages, maxLevel?: 1 | 2 | 3 | 4): StructuredCPVItem[]`

Get all CPV categories in a hierarchical structure.

```typescript
import { getAllCategories } from 'cpv-eu';

// Get all categories up to level 2
const categories = getAllCategories("en", 2);

// Get all categories (default: up to level 4)
const allCategories = getAllCategories("en");
```

### `getAllRaw(): CPVItem[]`

Get all raw CPV data items.

```typescript
import { getAllRaw } from 'cpv-eu';

const allItems = getAllRaw();
console.log(`Total CPV items: ${allItems.length}`); // 9,454
```

### Data Types

#### `CPVItem`

```typescript
interface CPVItem {
  code: string;           // 8-digit CPV code
  labels: Record<string, string>; // Multi-language labels
  emoji: string;          // Category emoji
  level: number;          // Hierarchy level (1-4)
  parent?: string;        // Parent CPV code (if any)
}
```

#### `StructuredCPVItem`

```typescript
interface StructuredCPVItem {
  code: string;
  label: string;          // Label in specified language
  emoji: string;
  level: number;
  parent?: string;
  children?: StructuredCPVItem[]; // Nested children
}
```

#### `SupportedLanguages`

```typescript
type SupportedLanguages = 
  | "en" | "de" | "fr" | "sv" | "es" | "it" | "nl" | "pl" | "pt"
  | "bg" | "cs" | "da" | "et" | "el" | "fi" | "hu" | "ga" | "hr"
  | "lv" | "lt" | "mt" | "ro" | "sk" | "sl";
```

## CPV Code Structure

CPV codes follow a hierarchical structure:

- **Level 1** (XX000000): Division (45 categories)
- **Level 2** (XXXX0000): Group
- **Level 3** (XXXXXX00): Class  
- **Level 4** (XXXXXXXX): Category

### Example Hierarchy

```
45 (Construction work)
├── 4510 (Site preparation work)
│   ├── 451100 (Demolition work)
│   └── 451200 (Test drilling and boring work)
└── 4520 (Works for complete or part construction)
    ├── 452100 (Building construction work)
    └── 452200 (Engineering works and construction works)
```

## Supported Languages

The library supports all 24 official EU languages:

| Language | Code | Language | Code |
|----------|------|----------|------|
| English | `en` | German | `de` |
| French | `fr` | Swedish | `sv` |
| Spanish | `es` | Italian | `it` |
| Dutch | `nl` | Polish | `pl` |
| Portuguese | `pt` | Bulgarian | `bg` |
| Czech | `cs` | Danish | `da` |
| Estonian | `et` | Greek | `el` |
| Finnish | `fi` | Hungarian | `hu` |
| Irish | `ga` | Croatian | `hr` |
| Latvian | `lv` | Lithuanian | `lt` |
| Maltese | `mt` | Romanian | `ro` |
| Slovak | `sk` | Slovenian | `sl` |

## Examples

### Get Construction Categories

```typescript
import { getCategoryChildrenByCPVCode } from 'cpv-eu';

const constructionCategories = getCategoryChildrenByCPVCode("45000000", "en");
constructionCategories.forEach(category => {
  console.log(`${category.code}: ${category.label} ${category.emoji}`);
});
```

### Search by Label

```typescript
import { getAllRaw } from 'cpv-eu';

const searchTerm = "construction";
const allItems = getAllRaw();

const matches = allItems.filter(item => 
  Object.values(item.labels).some(label => 
    label.toLowerCase().includes(searchTerm.toLowerCase())
  )
);

console.log(`Found ${matches.length} items containing "${searchTerm}"`);
```

### Build Category Tree

```typescript
import { getAllCategories } from 'cpv-eu';

function printCategoryTree(categories, indent = 0) {
  categories.forEach(category => {
    console.log('  '.repeat(indent) + `${category.code}: ${category.label} ${category.emoji}`);
    if (category.children) {
      printCategoryTree(category.children, indent + 1);
    }
  });
}

const tree = getAllCategories("en", 3);
printCategoryTree(tree);
```

## Development

For development setup, build instructions, and contributing guidelines, see [README-DEV.md](README-DEV.md).

### Quick Testing

```typescript
// Test individual functions
import { getLabelByCPVCode } from 'cpv-eu';

console.log(getLabelByCPVCode("45000000", "en")); // "Construction work"
console.log(getLabelByCPVCode("45000000", "sv")); // "Byggarbeten"
```

## Data Source

The CPV data is sourced from the official EU TED (Tenders Electronic Daily) Excel files and includes:

- **9,454 CPV codes** across all levels
- **24 language translations** for each code
- **Hierarchical relationships** with parent-child links
- **Emoji mappings** for visual categorization
- **Level classification** (1-4) for each code

## ⚠️ Emoji Matching Disclaimer

The emoji mappings in this library are **automatically generated** based on keyword matching in CPV labels. While they provide visual categorization, they are **not 100% accurate** and should be used with caution in production environments.

**Limitations:**
- Emoji selection is based on simple keyword matching
- Some categories may have inappropriate or misleading emojis
- The matching algorithm may not capture all nuances of CPV categories
- Emojis are for visual reference only and should not be used for categorization logic

**Recommendation:** Use emojis for display purposes only, not for programmatic decision-making or filtering.

## License

MIT License - see [LICENSE](LICENSE) file for details.

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

**GitHub Repository:** [https://github.com/samhallskod/cpv-eu](https://github.com/samhallskod/cpv-eu)

## Support

For support, please open an issue on GitHub or contact the maintainers.

---

**Made with ❤️ by [Samhällskod](https://samhallskod.se)**