Initial commit
This commit is contained in:
commit
4d8ab562c2
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
data/
|
||||
src/rules.yaml
|
||||
venv/
|
||||
53
README.md
Normal file
53
README.md
Normal 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
9
config/settings.yaml
Normal 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
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
PyYAML
|
||||
pandas
|
||||
8
src/config.py
Normal file
8
src/config.py
Normal 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
15
src/converter.py
Normal 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
43
src/converter_new.py
Normal 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
22
src/main.py
Normal 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
8
src/utils/file_utils.py
Normal 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')
|
||||
Loading…
x
Reference in New Issue
Block a user