python: Start Django project.
This is enough code to load Ledger data into Django models.
This commit is contained in:
parent
02e80b8d08
commit
b32e72b1e8
6 changed files with 193 additions and 0 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,3 +1,5 @@
|
||||||
|
python/db.sqlite3
|
||||||
|
python/**/__pycache__/
|
||||||
Supporters/MYMETA.json
|
Supporters/MYMETA.json
|
||||||
Supporters/MYMETA.yml
|
Supporters/MYMETA.yml
|
||||||
Supporters/Makefile
|
Supporters/Makefile
|
||||||
|
|
87
python/load_ledger.py
Executable file
87
python/load_ledger.py
Executable file
|
@ -0,0 +1,87 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import collections
|
||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import django
|
||||||
|
|
||||||
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'supporters.settings')
|
||||||
|
django.setup()
|
||||||
|
from supporters.models import Payment
|
||||||
|
|
||||||
|
COLUMNS = collections.OrderedDict([
|
||||||
|
('date', '%(format_date(date, "%Y-%m-%d"))'),
|
||||||
|
('entity', '%(quoted(meta("Entity")))'),
|
||||||
|
('payee', '%(quoted(payee))'),
|
||||||
|
('program', '%(quoted(meta("Program")))'),
|
||||||
|
('amount', '%(quoted(display_amount))'),
|
||||||
|
])
|
||||||
|
|
||||||
|
def parse_arguments(arglist):
|
||||||
|
parser = argparse.ArgumentParser(prog='load_ledger')
|
||||||
|
parser.add_argument(
|
||||||
|
'--ledger-from-scratch', default=False, action='store_true',
|
||||||
|
help="""By default, this script imports all Supporter payments,
|
||||||
|
and you can narrow the set of payments it imports by providing
|
||||||
|
additional search criteria as arguments. If you set this flag,
|
||||||
|
the script will not use any default criteria, and only use the criteria
|
||||||
|
you specify.""")
|
||||||
|
parser.add_argument(
|
||||||
|
'--ledger-command', default='ledger', metavar='COMMAND',
|
||||||
|
help="Name or path of ledger executable")
|
||||||
|
parser.add_argument(
|
||||||
|
'ledger_arguments', default=[], nargs=argparse.REMAINDER,
|
||||||
|
help="Additional Ledger search criteria for payments to import")
|
||||||
|
args = parser.parse_args(arglist)
|
||||||
|
if args.ledger_arguments and (args.ledger_arguments[0] == '--'):
|
||||||
|
del args.ledger_arguments[0]
|
||||||
|
base_cmdline = [
|
||||||
|
args.ledger_command, 'csv',
|
||||||
|
'--csv-format', ','.join(COLUMNS.values()) + '\n',
|
||||||
|
'--sort', 'date',
|
||||||
|
]
|
||||||
|
if args.ledger_from_scratch:
|
||||||
|
args.ledger_cmdline = base_cmdline + args.ledger_arguments
|
||||||
|
else:
|
||||||
|
args.ledger_cmdline = (base_cmdline
|
||||||
|
+ ['--limit', 'tag("Program") =~ /:Supporters:/']
|
||||||
|
+ args.ledger_arguments
|
||||||
|
+ ['/^Income:/'])
|
||||||
|
return args
|
||||||
|
|
||||||
|
def load_ledger(ledger_cmdline):
|
||||||
|
ledger_env = os.environ.copy()
|
||||||
|
for lang_envvar in ['LC_ALL', 'LC_CTYPE', 'LANG']:
|
||||||
|
try:
|
||||||
|
current_language = ledger_env[lang_envvar].split('.', 1)[0]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
lang_envvar = 'LC_CTYPE'
|
||||||
|
current_language = 'en_US'
|
||||||
|
ledger_env[lang_envvar] = current_language + '.utf8'
|
||||||
|
ledger = subprocess.Popen(ledger_cmdline,
|
||||||
|
env=ledger_env,
|
||||||
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
|
ledger.stdin.close()
|
||||||
|
with ledger, open(ledger.stdout.fileno(), encoding='utf-8', closefd=False) as stdout:
|
||||||
|
yield from csv.reader(stdout)
|
||||||
|
assert ledger.returncode == 0, "ledger subprocess failed"
|
||||||
|
|
||||||
|
def save_payments(row_source):
|
||||||
|
with django.db.transaction.atomic():
|
||||||
|
for row in row_source:
|
||||||
|
kwargs = {colname: row[index] for index, colname in enumerate(COLUMNS)}
|
||||||
|
Payment(**kwargs).save()
|
||||||
|
|
||||||
|
def main(arglist):
|
||||||
|
args = parse_arguments(arglist)
|
||||||
|
save_payments(load_ledger(args.ledger_cmdline))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main(None)
|
10
python/manage.py
Executable file
10
python/manage.py
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "supporters.settings")
|
||||||
|
|
||||||
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
|
execute_from_command_line(sys.argv)
|
0
python/supporters/__init__.py
Normal file
0
python/supporters/__init__.py
Normal file
10
python/supporters/models.py
Normal file
10
python/supporters/models.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
class Payment(models.Model):
|
||||||
|
date = models.DateField()
|
||||||
|
entity = models.TextField()
|
||||||
|
payee = models.TextField()
|
||||||
|
program = models.TextField()
|
||||||
|
amount = models.TextField()
|
84
python/supporters/settings.py
Normal file
84
python/supporters/settings.py
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
"""
|
||||||
|
Django settings for supporters project.
|
||||||
|
|
||||||
|
For more information on this file, see
|
||||||
|
https://docs.djangoproject.com/en/1.7/topics/settings/
|
||||||
|
|
||||||
|
For the full list of settings and their values, see
|
||||||
|
https://docs.djangoproject.com/en/1.7/ref/settings/
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||||
|
import os
|
||||||
|
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
# Quick-start development settings - unsuitable for production
|
||||||
|
# See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/
|
||||||
|
|
||||||
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
|
SECRET_KEY = 'c7%o*zll@260k&e98qutdm!0i7cbi9=ll3y1xh*4a!xk+bipnr'
|
||||||
|
|
||||||
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
TEMPLATE_DEBUG = True
|
||||||
|
|
||||||
|
ALLOWED_HOSTS = []
|
||||||
|
|
||||||
|
|
||||||
|
# Application definition
|
||||||
|
|
||||||
|
INSTALLED_APPS = (
|
||||||
|
'supporters',
|
||||||
|
# 'django.contrib.admin',
|
||||||
|
# 'django.contrib.auth',
|
||||||
|
# 'django.contrib.contenttypes',
|
||||||
|
# 'django.contrib.sessions',
|
||||||
|
# 'django.contrib.messages',
|
||||||
|
# 'django.contrib.staticfiles',
|
||||||
|
)
|
||||||
|
|
||||||
|
MIDDLEWARE_CLASSES = (
|
||||||
|
# 'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
# 'django.middleware.common.CommonMiddleware',
|
||||||
|
# 'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
# 'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
|
# 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||||
|
# 'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
|
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
|
)
|
||||||
|
|
||||||
|
# ROOT_URLCONF = 'supporters.urls'
|
||||||
|
|
||||||
|
# WSGI_APPLICATION = 'supporters.wsgi.application'
|
||||||
|
|
||||||
|
|
||||||
|
# Database
|
||||||
|
# https://docs.djangoproject.com/en/1.7/ref/settings/#databases
|
||||||
|
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
|
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Internationalization
|
||||||
|
# https://docs.djangoproject.com/en/1.7/topics/i18n/
|
||||||
|
|
||||||
|
LANGUAGE_CODE = 'en-us'
|
||||||
|
|
||||||
|
TIME_ZONE = 'UTC'
|
||||||
|
|
||||||
|
USE_I18N = True
|
||||||
|
|
||||||
|
USE_L10N = True
|
||||||
|
|
||||||
|
USE_TZ = True
|
||||||
|
|
||||||
|
|
||||||
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
# https://docs.djangoproject.com/en/1.7/howto/static-files/
|
||||||
|
|
||||||
|
STATIC_URL = '/static/'
|
Loading…
Reference in a new issue