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