#!/usr/bin/env python3
# File: library_info_cli.py
# Author: bgstack15
# Startdate: 2024-07-06-7 15:19
# Title: CLI for library_info
# Project: library_info
# Purpose: cli client for library_info_lib
# History:
# Usage: --help
# Reference:
# Improve:
# Dependencies:
# dep-almalinux8: python3-requests, python3-dateutil
import argparse, sys, json, datetime
import library_info_lib
parser = argparse.ArgumentParser(description="Shows currently checked out items from the configured libraries.")
parser.add_argument("-d","--debug", nargs='?', default=0, type=int, choices=range(0,11), help="Set debug level. >=5 adds verbose=True to get_checkouts().")
# add output option: html, raw, json
# add mutual: all or single.
group1 = parser.add_mutually_exclusive_group()
group1.add_argument("-s","--single", help="Show this single account.")
group1.add_argument("-a","--all", action='store_true', default=True, help="Check all accounts")
parser.add_argument("-o","--output", choices=["html","json","raw"], default="raw", help="Output format.")
try:
# This was only added in python 3.9
parser.add_argument("-f","--full", action=argparse.BooleanOptionalAction, default=True, help="Use full image objects or not. They are huge and during debugging it is useful to turn off.")
except AttributeError:
group2 = parser.add_mutually_exclusive_group()
group2.add_argument("-f","--full", action="store_true", default=True, help="Use full image objects or not. They are huge and during debugging it is useful to turn off.")
group2.add_argument("--no-f","--no-full", action="store_true", default=False)
args = parser.parse_args()
debuglevel = 0
if args.debug is None:
# -d was used but no value provided
debuglevel = 10
elif args.debug:
debuglevel = args.debug
full_images = args.full
single = args.single
output = args.output
def prn(*args,**kwargs):
kwargs["end"] = ""
print(*args,**kwargs)
def eprint(*args,**kwargs):
kwargs["file"] = sys.stderr
print(*args,**kwargs)
def serialize(item):
eprint(f"DEBUG: trying to serialize {item}, type {type(item)}")
if type(item) == datetime.datetime:
# We know library due dates are just a day, and not a time.
return item.strftime("%F")
else:
eprint(f"WARNING: unknown type {type(item)} for json-serializing object {item}")
return item
def html(checkouts, reservations):
# Uses https://datatables.net/download/builder?dt/jq-3.7.0/dt-2.0.8/cr-2.0.3/fh-4.0.1
# with css corrected by having the utf-8 line at the top of the .min.css file.
""" Make an html of the items """
prn("\n")
prn("
\n")
prn('\n')
prn('\n')
prn('\n')
prn('')
# inline css, if any
prn('\n')
prn("Library checkouts\n")
prn("\n")
prn("\n")
seen_accounts = []
for i in checkouts:
if i["patron"] not in seen_accounts:
seen_accounts.append(i["patron"])
if seen_accounts:
prn("
Accounts: ")
prn(", ".join(seen_accounts))
prn("
\n")
try:
# fails on python36 on almalinux8
now = datetime.datetime.now(datetime.UTC)
except AttributeError:
now = datetime.datetime.utcnow()
prn("Last modified: " + now.strftime("%FT%TZ") + "\n")
prn("
Checkouts
\n")
if len(checkouts):
# for some reason, DataTables will let a table resize with browser window resize only if the table object itself has the style. A css entry for "table" does not affect DataTables.
prn("
\n")
prn(f"
Patron
barcode
due
format
cover
Title
\n")
prn("\n")
for i in checkouts:
prn(f"
")
prn(f"
{i['patron']}
")
prn(f"
{i['barcode']}
")
due = i["due"].strftime("%F")
prn(f"
{due}
")
if "img" in i:
img_src = i["img"]
else:
img_src = ""
#prn(f"
{i['format']}
")
prn(f"
{i['format']}
")
prn(f"
")
prn(f"
{i['title']}
")
prn(f"
\n")
prn(f"\n")
prn(f"
\n")
else:
prn(f"No checkouts.\n")
prn("
Reservations
\n")
if len(reservations):
prn(f"
\n")
prn(f"
Patron
date placed
location
position
status
cover
title
\n")
prn("\n")
for i in reservations:
prn(f"
")
prn(f"
{i['patron']}
")
prn(f"
{i['date_placed']}
")
prn(f"
{i['location']}
")
prn(f"
{i['position']}
")
prn(f"
{i['status']}
")
if "img" in i:
img_src = i["img"]
else:
img_src = ""
prn(f"
")
prn(f"
{i['title']}
")
prn(f"
\n")
prn(f"\n")
prn(f"
\n")
else:
prn("No reservations.\n")
prn(f"\n")
prn(f"\n")
prn('')
# disable paging, because I dislike it.
# Use column 2 (due date) ascending as initial sort.
prn("""
""")
prn(f"")
if "__main__" == __name__:
if debuglevel >= 1:
eprint(args)
if single:
checkouts, reservations = library_info_lib.get_single_configitem(alias = single, full_images = full_images, verbose = debuglevel >= 5)
else:
checkouts, reservations = library_info_lib.get_all_configitems(full_images = full_images, verbose = debuglevel >= 5)
if "raw" == output:
print(checkouts)
print(reservations)
elif "json" == output:
print(json.dumps(checkouts,default=serialize))
elif "html" == output:
html(checkouts, reservations)
else:
print(f"Error! Invalid choice for output format {output}.")