Initial commit

This commit is contained in:
Lasse Wiedemann 2025-03-30 22:16:44 +02:00
commit 4d8ab562c2
9 changed files with 163 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
data/
src/rules.yaml
venv/

53
README.md Normal file
View File

@ -0,0 +1,53 @@
# CSV to Beancount Converter
This project provides a simple utility to convert CSV files into Beancount format. It is designed to facilitate the management of financial data by transforming structured CSV data into a format compatible with Beancount accounting software.
## Project Structure
```
csv-to-beancount
├── src
│ ├── main.py # Entry point for the application
│ ├── converter.py # Logic for converting CSV to Beancount
│ ├── config.py # Configuration settings loader
│ └── utils
│ └── file_utils.py # Utility functions for file operations
├── config
│ └── settings.yaml # Configuration file with fixed values
├── data
│ └── example.csv # Sample CSV file for conversion
├── requirements.txt # Project dependencies
└── README.md # Project documentation
```
## Installation
To set up the project, clone the repository and install the required dependencies:
```bash
git clone <repository-url>
cd csv-to-beancount
pip install -r requirements.txt
```
## Usage
To convert a CSV file to Beancount format, run the following command:
```bash
python src/main.py data/example.csv
```
This command will read the specified CSV file, convert its contents into Beancount format using the defined mappings in the configuration file, and save the output to a Beancount file.
## Configuration
The configuration settings are stored in `config/settings.yaml`. You can modify this file to adjust account mappings and other constants used during the conversion process.
## Contributing
Contributions are welcome! Please feel free to submit a pull request or open an issue for any enhancements or bug fixes.
## License
This project is licensed under the MIT License. See the LICENSE file for more details.

9
config/settings.yaml Normal file
View File

@ -0,0 +1,9 @@
account_mappings:
giro_account: "Assets:Bank:Giro"
fixed_values:
currency: "EUR"
default_transaction_type: "expense"
file_mappings:
example.csv: "Assets:Bank:Giro"

2
requirements.txt Normal file
View File

@ -0,0 +1,2 @@
PyYAML
pandas

8
src/config.py Normal file
View File

@ -0,0 +1,8 @@
from pathlib import Path
import yaml
def load_config():
config_path = Path(__file__).parent.parent / 'config' / 'settings.yaml'
with open(config_path, 'r') as file:
config = yaml.safe_load(file)
return config

15
src/converter.py Normal file
View File

@ -0,0 +1,15 @@
def convert_csv_to_beancount(csv_data, config):
beancount_entries = []
for row in csv_data:
date = row['date']
amount = row['amount']
description = row['description']
# Map the account based on the configuration
account = config['account_mappings'].get(row['filename'], 'Assets:Bank:Giro')
entry = f"{date} * \"{description}\"\n {account} {amount}\n"
beancount_entries.append(entry)
return ''.join(beancount_entries)

43
src/converter_new.py Normal file
View File

@ -0,0 +1,43 @@
import yaml
class BeancountConverter:
def __init__(self, rules_file):
with open(rules_file, 'r') as f:
self.rule_definitions = yaml.safe_load(f)
# Compile rules
self.rules = self._compile_rules(self.rule_definitions)
def _compile_rules(self, rule_defs):
compiled_rules = []
for rule in rule_defs:
conditions = []
# Build conditions from rule definition
if 'description_contains' in rule:
terms = rule['description_contains']
conditions.append(
lambda t, terms=terms: any(term.upper() in t.get('description', '').upper() for term in terms)
)
if 'amount_range' in rule:
min_val = rule['amount_range'].get('min', float('-inf'))
max_val = rule['amount_range'].get('max', float('inf'))
conditions.append(
lambda t, min_val=min_val, max_val=max_val:
min_val <= float(t.get('amount', 0)) <= max_val
)
# Combine all conditions
compiled_rule = (
lambda t, conditions=conditions: all(cond(t) for cond in conditions),
rule['target_account']
)
compiled_rules.append(compiled_rule)
# Add default rule
compiled_rules.append((lambda t: True, 'Expenses:Uncategorized'))
return compiled_rules

22
src/main.py Normal file
View File

@ -0,0 +1,22 @@
import pandas as pd
from src.config import load_config
from src.converter import convert_csv_to_beancount
from src.utils.file_utils import write_beancount
def main():
# Load configuration settings
config = load_config()
# Read the CSV file
csv_file_path = 'data/example.csv'
csv_data = pd.read_csv(csv_file_path)
# Convert CSV data to Beancount format
beancount_entries = convert_csv_to_beancount(csv_data, config)
# Write the Beancount entries to a file
output_file_path = 'data/output.beancount'
write_beancount(output_file_path, beancount_entries)
if __name__ == "__main__":
main()

8
src/utils/file_utils.py Normal file
View File

@ -0,0 +1,8 @@
def read_csv(filepath):
import pandas as pd
return pd.read_csv(filepath)
def write_beancount(filepath, beancount_entries):
with open(filepath, 'w') as f:
for entry in beancount_entries:
f.write(entry + '\n')