Continue with python
This commit is contained in:
@@ -47,6 +47,10 @@ def get_packagelists():
|
||||
return PackageList.query.all()
|
||||
|
||||
|
||||
def get_packagelist_by_id(id):
|
||||
return PackageList.query.filter_by(id=str(id)).first()
|
||||
|
||||
|
||||
def add_packagelist(name, description):
|
||||
try:
|
||||
db.session.add(
|
||||
@@ -54,6 +58,7 @@ def add_packagelist(name, description):
|
||||
)
|
||||
db.session.commit()
|
||||
except sqlalchemy.exc.IntegrityError:
|
||||
db.session.rollback()
|
||||
return False
|
||||
|
||||
|
||||
@@ -70,46 +75,180 @@ def pkglist_table():
|
||||
pkglists = get_packagelists()
|
||||
doc = t.div(id="packagelist-table")
|
||||
with doc:
|
||||
t.h1("Package Lists", _class=style("text-2xl", "mb-5"))
|
||||
t.h1("Package Lists", _class=cls("text-2xl", "mb-5"))
|
||||
with t.table(
|
||||
id="packagelist-table",
|
||||
_class=style("table", "border-collapse", "border", "w-full"),
|
||||
_class=cls(
|
||||
"table",
|
||||
"table-auto",
|
||||
# "border-separate",
|
||||
"border-collapse",
|
||||
"border-spacing-0",
|
||||
"border",
|
||||
"w-full",
|
||||
),
|
||||
):
|
||||
with t.thead(_class=style("bg-gray-200")):
|
||||
with t.thead(_class=cls("bg-gray-200")):
|
||||
t.tr(
|
||||
t.th("Name", _class=style("border", "p-2")),
|
||||
t.th("Description", _class=style("border", "p-2")),
|
||||
t.th(_class=style("border p-2")),
|
||||
t.th("Name", _class=cls("border", "p-2")),
|
||||
t.th("Description", _class=cls("border", "p-2")),
|
||||
t.th(_class=cls("border p-2")),
|
||||
t.th(_class=cls("border p-2")),
|
||||
_class="h-10",
|
||||
)
|
||||
with t.tbody():
|
||||
with t.tbody(data_hx_target="closest tr", data_hx_swap="outerHTML"):
|
||||
for pkglist in pkglists:
|
||||
t.tr(
|
||||
t.td(pkglist.name, _class=style("border", "p-2")),
|
||||
t.td(str(pkglist.description), _class=style("border", "p-2")),
|
||||
t.td(pkglist.name, _class=cls("border", "px-2")),
|
||||
t.td(str(pkglist.description), _class=cls("border", "px-2")),
|
||||
t.td(
|
||||
"x",
|
||||
t.span(_class=cls("mdi", "mdi-delete", "text-xl")),
|
||||
id="delete-packagelist",
|
||||
data_hx_delete=f"/list/{pkglist.id}",
|
||||
data_hx_target="#packagelist-table",
|
||||
data_hx_swap="outerHTML",
|
||||
_class=style(
|
||||
_class=cls(
|
||||
"border",
|
||||
"bg-red-200",
|
||||
"min-w-max",
|
||||
"hover:bg-red-200",
|
||||
"hover:bg-red-400",
|
||||
"cursor-pointer",
|
||||
"w-8",
|
||||
"text-center",
|
||||
),
|
||||
),
|
||||
_class=style("even:bg-gray-100", "hover:bg-purple-200"),
|
||||
t.td(
|
||||
t.span(_class=cls("mdi", "mdi-pencil", "text-xl")),
|
||||
id="edit-packagelist",
|
||||
data_hx_post=f"/list/{pkglist.id}/edit",
|
||||
_class=cls(
|
||||
"border",
|
||||
"bg-blue-200",
|
||||
"hover:bg-blue-400",
|
||||
"cursor-pointer",
|
||||
"w-8",
|
||||
"text-center",
|
||||
),
|
||||
),
|
||||
_class=cls("h-10", "even:bg-gray-100", "hover:bg-purple-200"),
|
||||
)
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
def style(*args):
|
||||
return " ".join(args)
|
||||
def cls(*args):
|
||||
return " ".join([a for a in args if a is not None])
|
||||
|
||||
|
||||
def new_pkglist_form(name=None, description=None, error=False, errormsg=None):
|
||||
assert not (error and not errormsg)
|
||||
with t.form(
|
||||
id="new-pkglist",
|
||||
name="new_pkglist",
|
||||
data_hx_post="/list/",
|
||||
data_hx_target="#pkglist-manager",
|
||||
data_hx_swap="outerHTML",
|
||||
_class=cls("mt-8", "p-5", "border-2", "border-gray-200"),
|
||||
) as doc:
|
||||
with t.div(_class=cls("mb-5", "flex", "flex-row", "items-center")):
|
||||
t.span(_class=cls("mdi", "mdi-playlist-plus", "text-2xl", "mr-4"))
|
||||
t.p("Add new package list", _class=cls("inline", "text-xl"))
|
||||
with t.div(_class=cls("w-11/12", "mx-auto")):
|
||||
with t.div(_class=cls("pb-8")):
|
||||
with t.div(
|
||||
_class=cls("flex", "flex-row", "justify-center", "items-start")
|
||||
):
|
||||
t.label(
|
||||
"Name",
|
||||
_for="listname",
|
||||
_class=cls("font-bold", "w-1/2", "p-2", "text-center"),
|
||||
)
|
||||
with t.div(_class=cls("w-1/2")):
|
||||
t._input(
|
||||
type="text",
|
||||
id="listname",
|
||||
name="name",
|
||||
**{"value": name} if name is not None else {},
|
||||
data_hx_target="#new-pkglist",
|
||||
data_hx_post="/list/name/validate",
|
||||
data_hx_swap="outerHTML",
|
||||
_class=cls(
|
||||
"block",
|
||||
"w-full",
|
||||
"p-2",
|
||||
"bg-gray-50",
|
||||
"appearance-none" if error else None,
|
||||
"border-2",
|
||||
"border-red-400" if error else "border-gray-300",
|
||||
"rounded",
|
||||
"focus:outline-none",
|
||||
"focus:bg-white",
|
||||
"focus:border-purple-500" if not error else None,
|
||||
),
|
||||
**{
|
||||
"x-on:change": "submit_enabled = $event.srcElement.value.length !== 0;"
|
||||
},
|
||||
)
|
||||
t.p(
|
||||
errormsg, _class=cls("mt-1", "text-red-400", "text-sm")
|
||||
) if error else None
|
||||
with t.div(
|
||||
_class=cls("flex", "flex-row", "justify-center", "items-center", "pb-8")
|
||||
):
|
||||
t.label(
|
||||
"Description",
|
||||
_for="listdesc",
|
||||
_class=cls("font-bold", "w-1/2", "text-center"),
|
||||
)
|
||||
t._input(
|
||||
type="text",
|
||||
id="listdesc",
|
||||
name="description",
|
||||
**{"value": description} if description is not None else {},
|
||||
_class=cls(
|
||||
"block",
|
||||
"w-1/2",
|
||||
"p-2",
|
||||
"bg-gray-50",
|
||||
"appearance-none",
|
||||
"border-2",
|
||||
"border-gray-300",
|
||||
"rounded",
|
||||
"focus:outline-none",
|
||||
"focus:bg-white",
|
||||
"focus:border-purple-500",
|
||||
),
|
||||
)
|
||||
t.p(**{"x-text": "submit_enabled"})
|
||||
t._input(
|
||||
type="submit",
|
||||
value="Add",
|
||||
_class=cls(
|
||||
"py-2",
|
||||
"cursor-not-allowed" if error else None,
|
||||
"opacity-50" if error else None,
|
||||
"border-2",
|
||||
"rounded",
|
||||
"border-gray-300",
|
||||
"mx-auto",
|
||||
"w-full",
|
||||
"hover:border-purple-500" if not error else None,
|
||||
"hover:bg-purple-200" if not error else None,
|
||||
),
|
||||
)
|
||||
return doc
|
||||
|
||||
|
||||
def pkglist_manager(error=False, errormsg=None):
|
||||
assert not (error and not errormsg)
|
||||
with t.div(
|
||||
id="pkglist-manager",
|
||||
_class=cls("p-8", "max-w-xl"),
|
||||
**{
|
||||
"x-data": '{ submit_enabled: document.getElementById("listname").textLength !== 0 }'
|
||||
},
|
||||
) as doc:
|
||||
t.script(raw(open("app.js").read()))
|
||||
pkglist_table()
|
||||
new_pkglist_form(error=error, errormsg=errormsg)
|
||||
return doc
|
||||
|
||||
|
||||
@app.route("/")
|
||||
@@ -118,115 +257,265 @@ def root():
|
||||
with doc.head:
|
||||
t.script(src="https://unpkg.com/htmx.org@1.7.0")
|
||||
t.script(src="https://cdn.tailwindcss.com")
|
||||
t.script(src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js", defer=True)
|
||||
t.link(
|
||||
rel="stylesheet",
|
||||
href="https://cdn.jsdelivr.net/npm/@mdi/font@6.9.96/css/materialdesignicons.min.css",
|
||||
)
|
||||
with doc:
|
||||
with t.div(_class=style("p-8", "max-w-xl")):
|
||||
t.script(raw(open("app.js").read()))
|
||||
pkglist_table()
|
||||
|
||||
with t.form(
|
||||
name="new_pkglist",
|
||||
data_hx_post="/list/",
|
||||
data_hx_target="#packagelist-table",
|
||||
data_hx_swap="outerHTML",
|
||||
_class=style("mt-8", "p-5", "border-2", "border-gray-200"),
|
||||
):
|
||||
with t.div(_class=style("mb-5", "flex", "flex-row", "items-center")):
|
||||
t.span(_class=style("mdi", "mdi-playlist-plus", "text-2xl", "mr-4"))
|
||||
t.p("Add new package list", _class=style("inline", "text-xl"))
|
||||
with t.div(_class=style("w-11/12", "mx-auto")):
|
||||
with t.div(
|
||||
_class=style(
|
||||
"flex", "flex-row", "justify-center", "items-center", "pb-8"
|
||||
)
|
||||
):
|
||||
t.label(
|
||||
"Name",
|
||||
_for="listname",
|
||||
_class=style("font-bold", "w-1/2", "text-center"),
|
||||
)
|
||||
t._input(
|
||||
type="text",
|
||||
id="listname",
|
||||
name="name",
|
||||
value="",
|
||||
_class=style(
|
||||
"block",
|
||||
"w-1/2",
|
||||
"p-2",
|
||||
"bg-gray-100",
|
||||
"appearance-none",
|
||||
"border-2",
|
||||
"border-gray-300",
|
||||
"rounded",
|
||||
"focus:outline-none",
|
||||
"focus:bg-white",
|
||||
"focus:border-purple-500",
|
||||
),
|
||||
)
|
||||
with t.div(
|
||||
_class=style(
|
||||
"flex", "flex-row", "justify-center", "items-center", "pb-8"
|
||||
)
|
||||
):
|
||||
t.label(
|
||||
"Description",
|
||||
_for="listdesc",
|
||||
_class=style("font-bold", "w-1/2", "text-center"),
|
||||
)
|
||||
t._input(
|
||||
type="text",
|
||||
id="listdesc",
|
||||
name="description",
|
||||
value="",
|
||||
_class=style(
|
||||
"block",
|
||||
"w-1/2",
|
||||
"p-2",
|
||||
"bg-gray-100",
|
||||
"appearance-none",
|
||||
"border-2",
|
||||
"border-gray-300",
|
||||
"rounded",
|
||||
"focus:outline-none",
|
||||
"focus:bg-white",
|
||||
"focus:border-purple-500",
|
||||
),
|
||||
)
|
||||
t._input(
|
||||
type="submit",
|
||||
value="Add",
|
||||
_class=style(
|
||||
"py-2",
|
||||
"border-2",
|
||||
"rounded",
|
||||
"border-gray-300",
|
||||
"mx-auto",
|
||||
"w-full",
|
||||
"hover:border-purple-500",
|
||||
"hover:bg-purple-200",
|
||||
),
|
||||
)
|
||||
pkglist_manager()
|
||||
|
||||
return doc.render()
|
||||
|
||||
|
||||
@app.route("/list/", methods=["POST"])
|
||||
def add_new_list():
|
||||
print(request.form)
|
||||
name = request.form["name"]
|
||||
description = request.form["description"]
|
||||
if add_packagelist(name=name, description=description) is False:
|
||||
return make_response(f'A package list with name "{name}" already exists', 400)
|
||||
error, errormsg = False, None
|
||||
if len(name) == 0:
|
||||
error = True
|
||||
errormsg = f"Name cannot be empty"
|
||||
elif add_packagelist(name=name, description=description) is False:
|
||||
error = True
|
||||
errormsg = f'A package list with name "{name}" already exists'
|
||||
|
||||
return pkglist_table().render()
|
||||
return pkglist_manager(error=error, errormsg=errormsg).render()
|
||||
|
||||
|
||||
@app.route("/list/name/validate", methods=["POST"])
|
||||
def validate_list_name():
|
||||
name = request.form["name"]
|
||||
|
||||
error, errormsg = False, None
|
||||
|
||||
if PackageList.query.filter_by(name=name).first() is not None:
|
||||
error = True
|
||||
errormsg = f'Name "{name}" already exists'
|
||||
if len(name) == 0:
|
||||
error = True
|
||||
errormsg = f"Name cannot be empty"
|
||||
|
||||
doc = new_pkglist_form(name, error, errormsg)
|
||||
|
||||
return make_response(doc.render(), 200)
|
||||
|
||||
|
||||
@app.route("/list/<uuid:id>/edit/cancel", methods=["POST"])
|
||||
def edit_list_cancel(id):
|
||||
pkglist = PackageList.query.filter_by(id=str(id)).first()
|
||||
|
||||
with t.tr(_class=cls("h-10", "even:bg-gray-100", "hover:bg-purple-200")) as doc:
|
||||
t.td(pkglist.name, _class=cls("border", "px-2")),
|
||||
t.td(str(pkglist.description), _class=cls("border", "px-2")),
|
||||
t.td(
|
||||
t.span(_class=cls("mdi", "mdi-delete", "text-xl")),
|
||||
id="delete-packagelist",
|
||||
data_hx_delete=f"/list/{pkglist.id}",
|
||||
_class=cls(
|
||||
"border",
|
||||
"bg-red-200",
|
||||
"hover:bg-red-400",
|
||||
"cursor-pointer",
|
||||
"w-8",
|
||||
"text-center",
|
||||
),
|
||||
),
|
||||
t.td(
|
||||
t.span(_class=cls("mdi", "mdi-pencil", "text-xl")),
|
||||
id="edit-packagelist",
|
||||
data_hx_post=f"/list/{pkglist.id}/edit",
|
||||
_class=cls(
|
||||
"border",
|
||||
"bg-blue-200",
|
||||
"hover:bg-blue-400",
|
||||
"cursor-pointer",
|
||||
"w-8",
|
||||
"text-center",
|
||||
),
|
||||
),
|
||||
return make_response(doc.render(), 200)
|
||||
|
||||
|
||||
@app.route("/list/<uuid:id>/edit/submit", methods=["POST"])
|
||||
def edit_list_submit(id):
|
||||
name = request.form["name"]
|
||||
description = request.form["description"]
|
||||
if len(name) == 0:
|
||||
with t.tr(id="pkglist-edit-row") as doc:
|
||||
with t.td(colspan=2, _class=cls("border-none", "bg-purple-100", "h-10")):
|
||||
t.p("Name cannot be empty", _class=cls("text-red-400", "text-sm"))
|
||||
with t.div(_class=cls("flex", "flex-row", "h-full")):
|
||||
with t.div(
|
||||
_class=cls(
|
||||
"box-border" "border",
|
||||
"border-2",
|
||||
"border-red-500",
|
||||
"bg-purple-100",
|
||||
"mr-1",
|
||||
)
|
||||
):
|
||||
with t.div(_class=cls("h-full")):
|
||||
t._input(
|
||||
_class=cls("bg-purple-100", "w-full", "h-full", "px-2"),
|
||||
type="text",
|
||||
name="name",
|
||||
value=name,
|
||||
)
|
||||
with t.div(
|
||||
_class=cls(
|
||||
"border", "border-1", "border-purple-500", "bg-purple-100"
|
||||
)
|
||||
):
|
||||
t._input(
|
||||
_class=cls("bg-purple-100", "w-full", "h-full", "px-2"),
|
||||
type="text",
|
||||
name="description",
|
||||
value=description,
|
||||
)
|
||||
t.td(
|
||||
t.span(_class=cls("mdi", "mdi-cancel", "text-xl")),
|
||||
id="edit-packagelist-abort",
|
||||
data_hx_post=f"/list/{id}/edit/cancel",
|
||||
data_hx_target="#pkglist-edit-row",
|
||||
data_hx_swap="outerHTML",
|
||||
_class=cls(
|
||||
"border",
|
||||
"bg-red-200",
|
||||
"hover:bg-red-400",
|
||||
"cursor-pointer",
|
||||
"w-8",
|
||||
"text-center",
|
||||
),
|
||||
),
|
||||
t.td(
|
||||
t.span(_class=cls("mdi", "mdi-content-save", "text-xl")),
|
||||
id="edit-packagelist-save",
|
||||
data_hx_post=f"/list/{id}/edit/submit",
|
||||
data_hx_target="#pkglist-edit-row",
|
||||
data_hx_swap="outerHTML",
|
||||
data_hx_include="closest tr",
|
||||
_class=cls(
|
||||
"border",
|
||||
"bg-green-200",
|
||||
"hover:bg-green-400",
|
||||
"cursor-pointer",
|
||||
"w-8",
|
||||
"text-center",
|
||||
),
|
||||
),
|
||||
return make_response(doc.render(), 200)
|
||||
|
||||
try:
|
||||
pkglist = PackageList.query.filter_by(id=str(id)).first()
|
||||
if pkglist is None:
|
||||
return make_response("", 404)
|
||||
pkglist.name = name
|
||||
pkglist.description = description
|
||||
db.session.commit()
|
||||
except:
|
||||
raise
|
||||
|
||||
with t.tr(_class=cls("h-10", "even:bg-gray-100", "hover:bg-purple-200")) as doc:
|
||||
t.td(pkglist.name, _class=cls("border", "px-2")),
|
||||
t.td(str(pkglist.description), _class=cls("border", "px-2")),
|
||||
t.td(
|
||||
t.span(_class=cls("mdi", "mdi-delete", "text-xl")),
|
||||
id="delete-packagelist",
|
||||
data_hx_delete=f"/list/{pkglist.id}",
|
||||
_class=cls(
|
||||
"border",
|
||||
"bg-red-200",
|
||||
"hover:bg-red-400",
|
||||
"cursor-pointer",
|
||||
"w-8",
|
||||
"text-center",
|
||||
),
|
||||
),
|
||||
t.td(
|
||||
t.span(_class=cls("mdi", "mdi-pencil", "text-xl")),
|
||||
id="edit-packagelist",
|
||||
data_hx_post=f"/list/{pkglist.id}/edit",
|
||||
_class=cls(
|
||||
"border",
|
||||
"bg-blue-200",
|
||||
"hover:bg-blue-400",
|
||||
"cursor-pointer",
|
||||
"w-8",
|
||||
"text-center",
|
||||
),
|
||||
),
|
||||
return make_response(doc.render(), 200)
|
||||
|
||||
|
||||
@app.route("/list/<uuid:id>/edit", methods=["POST"])
|
||||
def edit_list(id):
|
||||
pkglist = get_packagelist_by_id(id)
|
||||
with t.tr(_class="h-10", id="pkglist-edit-row") as doc:
|
||||
with t.td(colspan=2, _class=cls("border-none", "bg-purple-100", "h-full")):
|
||||
with t.div(_class=cls("flex", "flex-row", "h-full")):
|
||||
with t.div(
|
||||
_class=cls(
|
||||
"border",
|
||||
"border-1",
|
||||
"border-purple-500",
|
||||
"bg-purple-100",
|
||||
"mr-1",
|
||||
)
|
||||
):
|
||||
t._input(
|
||||
_class=cls("bg-purple-100", "w-full", "h-full", "px-2"),
|
||||
type="text",
|
||||
name="name",
|
||||
value=pkglist.name,
|
||||
)
|
||||
with t.div(
|
||||
_class=cls(
|
||||
"border", "border-1", "border-purple-500", "bg-purple-100"
|
||||
)
|
||||
):
|
||||
t._input(
|
||||
_class=cls("bg-purple-100", "w-full", "h-full", "px-2"),
|
||||
type="text",
|
||||
name="description",
|
||||
value=pkglist.description,
|
||||
)
|
||||
t.td(
|
||||
t.span(_class=cls("mdi", "mdi-cancel", "text-xl")),
|
||||
id="edit-packagelist-abort",
|
||||
data_hx_post=f"/list/{pkglist.id}/edit/cancel",
|
||||
data_hx_target="#pkglist-edit-row",
|
||||
data_hx_swap="outerHTML",
|
||||
_class=cls(
|
||||
"border",
|
||||
"bg-red-200",
|
||||
"hover:bg-red-400",
|
||||
"cursor-pointer",
|
||||
"w-8",
|
||||
"text-center",
|
||||
),
|
||||
),
|
||||
t.td(
|
||||
t.span(_class=cls("mdi", "mdi-content-save", "text-xl")),
|
||||
id="edit-packagelist-save",
|
||||
data_hx_post=f"/list/{pkglist.id}/edit/submit",
|
||||
data_hx_target="#pkglist-edit-row",
|
||||
data_hx_swap="outerHTML",
|
||||
data_hx_include="closest tr",
|
||||
_class=cls(
|
||||
"border",
|
||||
"bg-green-200",
|
||||
"hover:bg-green-400",
|
||||
"cursor-pointer",
|
||||
"w-8",
|
||||
"text-center",
|
||||
),
|
||||
),
|
||||
return make_response(doc.render(), 200)
|
||||
|
||||
|
||||
@app.route("/list/<uuid:id>", methods=["DELETE"])
|
||||
def delete_list(id):
|
||||
if not delete_packagelist(id=id):
|
||||
return make_response("", 404)
|
||||
return pkglist_table().render()
|
||||
return make_response("", 200)
|
||||
|
||||
0
python_flask/run.sh
Normal file → Executable file
0
python_flask/run.sh
Normal file → Executable file
Reference in New Issue
Block a user