mirror of https://github.com/logos-co/open-law.git
book creation
This commit is contained in:
parent
ca5e3c8fd7
commit
6cd10b7378
|
@ -21,7 +21,7 @@ def create_app(environment="development"):
|
||||||
main_blueprint,
|
main_blueprint,
|
||||||
auth_blueprint,
|
auth_blueprint,
|
||||||
user_blueprint,
|
user_blueprint,
|
||||||
books_blueprint,
|
book_blueprint,
|
||||||
home_blueprint,
|
home_blueprint,
|
||||||
)
|
)
|
||||||
from app.models import (
|
from app.models import (
|
||||||
|
@ -48,7 +48,7 @@ def create_app(environment="development"):
|
||||||
app.register_blueprint(auth_blueprint)
|
app.register_blueprint(auth_blueprint)
|
||||||
app.register_blueprint(main_blueprint)
|
app.register_blueprint(main_blueprint)
|
||||||
app.register_blueprint(user_blueprint)
|
app.register_blueprint(user_blueprint)
|
||||||
app.register_blueprint(books_blueprint)
|
app.register_blueprint(book_blueprint)
|
||||||
app.register_blueprint(home_blueprint)
|
app.register_blueprint(home_blueprint)
|
||||||
|
|
||||||
# Set up flask login.
|
# Set up flask login.
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
# flake8: noqa F401
|
# flake8: noqa F401
|
||||||
from .auth import LoginForm
|
from .auth import LoginForm
|
||||||
from .user import UserForm, NewUserForm
|
from .user import UserForm, NewUserForm
|
||||||
|
from .book import CreateBookForm
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, SubmitField
|
||||||
|
from wtforms.validators import DataRequired, Length
|
||||||
|
|
||||||
|
|
||||||
|
class CreateBookForm(FlaskForm):
|
||||||
|
label = StringField("Label", [DataRequired(), Length(6, 1024)])
|
||||||
|
submit = SubmitField("Add new book")
|
|
@ -3,7 +3,8 @@
|
||||||
<div id="add-book-modal" tabindex="-1" aria-hidden="true" class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
<div id="add-book-modal" tabindex="-1" aria-hidden="true" class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
||||||
<div class="relative w-full max-w-2xl max-h-full">
|
<div class="relative w-full max-w-2xl max-h-full">
|
||||||
<!-- Modal content -->
|
<!-- Modal content -->
|
||||||
<form action="#" method="post" class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
<form action="{{ url_for('book.create') }}" method="post" class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
||||||
|
|
||||||
<input type="hidden" name="user_id" id="user-edit-id" value="0" />
|
<input type="hidden" name="user_id" id="user-edit-id" value="0" />
|
||||||
<input type="hidden" name="next_url" id="user-edit-next_url" value="" />
|
<input type="hidden" name="next_url" id="user-edit-next_url" value="" />
|
||||||
<!-- Modal header -->
|
<!-- Modal header -->
|
||||||
|
@ -15,8 +16,8 @@
|
||||||
<div class="p-6 space-y-6">
|
<div class="p-6 space-y-6">
|
||||||
<div class="grid grid-cols-6 gap-6">
|
<div class="grid grid-cols-6 gap-6">
|
||||||
<div class="col-span-6 sm:col-span-3">
|
<div class="col-span-6 sm:col-span-3">
|
||||||
<label for="book_label" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" >Label</label >
|
<label for="label" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white" >Label</label >
|
||||||
<input type="text" name="book_label" class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Name" required />
|
<input type="text" name="label" id="label" class="shadow-sm bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-600 focus:border-blue-600 block w-full p-2.5 dark:bg-gray-600 dark:border-gray-500 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="Name" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a
|
<a
|
||||||
href="{{ url_for('books.get_all') }}"
|
href="{{ url_for('book.get_all') }}"
|
||||||
class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
class="flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700">
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" /> </svg>
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" /> </svg>
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
from .auth import auth_blueprint
|
from .auth import auth_blueprint
|
||||||
from .main import main_blueprint
|
from .main import main_blueprint
|
||||||
from .user import bp as user_blueprint
|
from .user import bp as user_blueprint
|
||||||
from .books import bp as books_blueprint
|
from .book import bp as book_blueprint
|
||||||
from .home import bp as home_blueprint
|
from .home import bp as home_blueprint
|
||||||
|
|
|
@ -1,17 +1,11 @@
|
||||||
from flask import (
|
from flask import Blueprint, render_template, flash, redirect, url_for
|
||||||
Blueprint,
|
|
||||||
render_template,
|
|
||||||
flash,
|
|
||||||
redirect,
|
|
||||||
url_for,
|
|
||||||
)
|
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
|
|
||||||
from app import models as m
|
from app import models as m
|
||||||
from app import forms as f
|
from app import forms as f
|
||||||
from app.logger import log
|
from app.logger import log
|
||||||
|
|
||||||
bp = Blueprint("books", __name__, url_prefix="/books")
|
bp = Blueprint("book", __name__, url_prefix="/book")
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/", methods=["GET"])
|
@bp.route("/", methods=["GET"])
|
||||||
|
@ -37,12 +31,19 @@ def get_all():
|
||||||
@bp.route("/create", methods=["POST"])
|
@bp.route("/create", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def create():
|
def create():
|
||||||
form = f.NewBookForm()
|
form = f.CreateBookForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
book = m.Book(
|
book = m.Book(
|
||||||
label=form.label.data,
|
label=form.label.data,
|
||||||
)
|
)
|
||||||
log(log.INFO, "Form submitted. User: [%s]", book)
|
log(log.INFO, "Form submitted. Book: [%s]", book)
|
||||||
flash("Book added!", "success")
|
flash("Book added!", "success")
|
||||||
book.save()
|
book.save()
|
||||||
return redirect(url_for("books.get_all"))
|
return redirect(url_for("book.get_all"))
|
||||||
|
else:
|
||||||
|
log(log.ERROR, "Book create errors: [%s]", form.errors)
|
||||||
|
for field, errors in form.errors.items():
|
||||||
|
field_label = form._fields[field].label.text
|
||||||
|
for error in errors:
|
||||||
|
flash(error.replace("Field", field_label), "danger")
|
||||||
|
return redirect(url_for("book.get_all"))
|
|
@ -0,0 +1,60 @@
|
||||||
|
from flask import current_app as Response
|
||||||
|
from flask.testing import FlaskClient
|
||||||
|
|
||||||
|
from app import models as m
|
||||||
|
from tests.utils import login
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_book(client: FlaskClient):
|
||||||
|
login(client)
|
||||||
|
|
||||||
|
BOOK_NAME = "Test Book"
|
||||||
|
|
||||||
|
# label len < 6
|
||||||
|
response: Response = client.post(
|
||||||
|
"/book/create",
|
||||||
|
data=dict(
|
||||||
|
label="12345",
|
||||||
|
),
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert b"Label must be between 6 and 1024 characters long." in response.data
|
||||||
|
|
||||||
|
book = m.Book.query.filter_by(label=BOOK_NAME).first()
|
||||||
|
|
||||||
|
assert not book
|
||||||
|
assert not m.Book.query.count()
|
||||||
|
|
||||||
|
# label len > 1024
|
||||||
|
response: Response = client.post(
|
||||||
|
"/book/create",
|
||||||
|
data=dict(
|
||||||
|
label="".join(["0" for _ in range(1025)]),
|
||||||
|
),
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert b"Label must be between 6 and 1024 characters long." in response.data
|
||||||
|
|
||||||
|
book = m.Book.query.filter_by(label=BOOK_NAME).first()
|
||||||
|
|
||||||
|
assert not book
|
||||||
|
assert not m.Book.query.count()
|
||||||
|
|
||||||
|
response: Response = client.post(
|
||||||
|
"/book/create",
|
||||||
|
data=dict(
|
||||||
|
label=BOOK_NAME,
|
||||||
|
),
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert b"Book added!" in response.data
|
||||||
|
|
||||||
|
book = m.Book.query.filter_by(label=BOOK_NAME).first()
|
||||||
|
|
||||||
|
assert book
|
Loading…
Reference in New Issue