add sql cli and explain it in the readme

This commit is contained in:
2020-05-14 20:05:02 -05:00
parent 8de802aff5
commit 0dc58ed6a8
6 changed files with 119 additions and 12 deletions

View File

@ -8,7 +8,7 @@ from flask import Flask
from flask_mail import Mail
from flask import render_template
from capsulflask import virt_model
from capsulflask import virt_model, cli
load_dotenv(find_dotenv())
@ -48,13 +48,14 @@ from capsulflask import db
db.init_app(app)
from capsulflask import auth, landing, console, payment, metrics
from capsulflask import auth, landing, console, payment, metrics, cli
app.register_blueprint(landing.bp)
app.register_blueprint(auth.bp)
app.register_blueprint(console.bp)
app.register_blueprint(payment.bp)
app.register_blueprint(metrics.bp)
app.register_blueprint(cli.bp)
app.add_url_rule("/", endpoint="index")

63
capsulflask/cli.py Normal file
View File

@ -0,0 +1,63 @@
import os
import re
from datetime import datetime
import click
from flask.cli import with_appcontext
from flask import Blueprint
from psycopg2 import ProgrammingError
from capsulflask.db import get_model, my_exec_info_message
bp = Blueprint('cli', __name__)
@bp.cli.command('sql')
@click.option('-f', help='script filename')
@click.option('-c', help='sql command')
@with_appcontext
def sql_script(f, c):
"""Run a sql script against the database. script is run 1 command at a time inside a single transaction."""
model = get_model()
script = ""
if f:
filepath = os.path.join(os.getcwd(), f)
if not os.path.isfile(filepath):
raise f"{filepath} is not a file"
with open(filepath, 'rb') as file:
script = file.read().decode("utf8")
elif c:
script = c
else:
click.echo(f"you must provide sql to run either inline with the -c argument or in a file with the -f argument")
return
commands = re.split(";\\s+", script)
for command in commands:
if command.strip() != "":
model.cursor.execute(command)
if re.match("^\\s*select", command, re.IGNORECASE) is not None:
for row in model.cursor.fetchall():
def format_value(x):
if isinstance(x, bool):
return "TRUE" if x else "FALSE"
if not x :
return "null"
if isinstance(x, datetime):
return x.isoformat()
return f"{x}"
click.echo(", ".join(list(map(format_value, row))))
else:
click.echo(f"{model.cursor.rowcount} rows affected.")
model.connection.commit()
@bp.cli.command('cron-task')
@with_appcontext
def cron_task():
print('a')

View File

@ -146,12 +146,12 @@ class DBModel:
def list_payments_for_account(self, email):
self.cursor.execute("""
SELECT dollars, invalidated, created
SELECT id, dollars, invalidated, created
FROM payments WHERE payments.email = %s""",
(email, )
)
return list(map(
lambda x: dict(dollars=x[0], invalidated=x[1], created=x[2]),
lambda x: dict(id=x[0], dollars=x[1], invalidated=x[2], created=x[3]),
self.cursor.fetchall()
))

View File

@ -49,11 +49,12 @@ CREATE TABLE vm_ssh_public_key (
);
CREATE TABLE payments (
id SERIAL,
email TEXT REFERENCES accounts(email) ON DELETE RESTRICT,
created TIMESTAMP NOT NULL DEFAULT NOW(),
dollars NUMERIC(8, 2) NOT NULL,
invalidated BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (email, created)
PRIMARY KEY (email, id)
);
CREATE TABLE login_tokens (
@ -74,8 +75,8 @@ CREATE TABLE payment_sessions (
CREATE TABLE unconfirmed_btcpay_invoices (
id TEXT PRIMARY KEY,
email TEXT REFERENCES accounts(email) ON DELETE RESTRICT,
created TIMESTAMP NOT NULL,
FOREIGN KEY (email, created) REFERENCES payments(email, created) ON DELETE CASCADE
payment_id INTEGER NOT NULL,
FOREIGN KEY (email, payment_id) REFERENCES payments(email, id) ON DELETE CASCADE
);
INSERT INTO os_images (id, template_image_file_name, description)
@ -100,7 +101,7 @@ INSERT INTO accounts (email)
VALUES ('forest.n.johnson@gmail.com');
INSERT INTO payments (email, dollars, created)
VALUES ('forest.n.johnson@gmail.com', 20.00, TO_TIMESTAMP('2020-05-05','YYYY-MM-DD'));
VALUES ('forest.n.johnson@gmail.com', 20.00, TO_TIMESTAMP('2020-05-05','YYYY-MM-DDTHH24-MI-SS'));
INSERT INTO vms (id, email, os, size)
VALUES ('capsul-yi9ffqbjly', 'forest.n.johnson@gmail.com', 'alpine311', 'f1-xx');