Day 1: Worked out more robust schema, wrote some go code for interacting with it.

This commit is contained in:
John Oss 2022-10-06 12:41:31 -04:00
parent 5688a6546a
commit d832463ded
10 changed files with 201 additions and 1 deletions

View File

@ -1,3 +1,16 @@
# goread
Maybe rewrite the library
Maybe rewrite the library, at the very least migrate to a better schema.
This is a proof of concept, mainly to get myself a bit more knowledgable about how our current library system works.
- file.out contains a dump of all tables that have a NULL ISBN.
TODO:
- Tighten up the columns w.r.t. null and bad values
- Add more checks to ensure data is actually valid / not bad
- Ensure we have the books we have in the catalogue
- Add UTF-8 support for the UI/UX/storage
- Perform normalization on data entered by users (e.g. ISBN sanitization / verification).
- Double check formats of everything else

BIN
catalogue.db Normal file

Binary file not shown.

39
db/book.go Normal file
View File

@ -0,0 +1,39 @@
package db
import (
"database/sql"
"time"
)
type CheckedOut struct {
ID int64
UWID string
DateOut sql.NullTime
}
type Returned struct {
ID int64
UWID string
DateOut sql.NullTime
DateIn sql.NullTime
}
// TODO: NARROW THIS DATA SOMEHOW
type Book struct {
ID int64
ISBN sql.NullString
LCCN sql.NullString
Title string
Subtitle sql.NullString
Authors sql.NullString
Edition sql.NullString
Publisher sql.NullString
PublishYear sql.NullString `db:"publish_year"`
PublishMonth sql.NullString `db:"publish_month"`
PublishLocation sql.NullString `db:"publish_location"`
Pages sql.NullString
Pagination sql.NullString
Weight sql.NullString
LastUpdated time.Time `db:"last_updated"`
Deleted bool
}

31
db/database.go Normal file
View File

@ -0,0 +1,31 @@
package db
import (
"context"
"fmt"
"github.com/jmoiron/sqlx"
)
const (
getAllBooks = `SELECT * from books where deleted = 0`
)
type Catalogue struct {
db *sqlx.DB
}
func OpenCatalogue(dsn string) (*Catalogue, error) {
db, err := sqlx.Open("sqlite3", dsn)
if err != nil {
return nil, fmt.Errorf("failed to open sqlite3 database with dsn: %s, given error: %w", dsn, err)
}
return &Catalogue{db: db}, nil
}
func (c *Catalogue) GetAllBooks(ctx context.Context) ([]Book, error) {
var books []Book
err := c.db.SelectContext(ctx, &books, getAllBooks)
return books, err
}

8
go.mod Normal file
View File

@ -0,0 +1,8 @@
module git.csclub.uwaterloo.ca/joss/goread
go 1.19
require (
github.com/jmoiron/sqlx v1.3.5 // indirect
github.com/mattn/go-sqlite3 v1.14.15 // indirect
)

7
go.sum Normal file
View File

@ -0,0 +1,7 @@
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=

BIN
goread Executable file

Binary file not shown.

37
main.go Normal file
View File

@ -0,0 +1,37 @@
package main
import (
"context"
"fmt"
"git.csclub.uwaterloo.ca/joss/goread/db"
_ "github.com/mattn/go-sqlite3"
)
const (
CatalogueDBFile = "/users/libcom/catalogue.db"
BookTableName = "books"
BookCategoryTableName = "book_categories"
CategoryTableName = "categories"
CheckoutDBFile = "/users/libcom/checkout/checkout.db"
CheckoutTableName = "checked_out"
ReturnedTableName = "returned"
)
func main() {
catalogue, err := db.OpenCatalogue("catalogue.db")
if err != nil {
panic(err)
}
books, err := catalogue.GetAllBooks(context.Background())
if err != nil {
panic(err)
}
for _, b := range books {
fmt.Printf("%#v\n", b)
}
}

39
permissions.go Normal file
View File

@ -0,0 +1,39 @@
package main
import (
"fmt"
"os/user"
)
// map groupname -> pretty name
var privilegedGroups = map[string]string{
"office": "Office worker",
"libcom": "Library Committee",
}
// IsPrivilegedUser checks to see if the current user is privileged.
func IsPrivilegedUser() (bool, error) {
person, err := user.Current()
if err != nil {
return false, fmt.Errorf("failed to get current user: %w", err)
}
groups, err := person.GroupIds()
if err != nil {
return false, fmt.Errorf("failed to get group ids: %w", err)
}
for _, gid := range groups {
group, err := user.LookupGroupId(gid)
if err != nil {
// Log error
continue
}
if _, ok := privilegedGroups[group.Name]; ok {
return true, nil
}
}
return false, nil
}

26
schema.sql Normal file
View File

@ -0,0 +1,26 @@
CREATE TABLE IF NOT EXISTS books (
id INTEGER PRIMARY KEY ASC AUTOINCREMENT,
isbn INTEGER NOT NULL,
lccn INTEGER DEFAULT NULL,
title TEXT NOT NULL,
subtitle TEXT DEFAULT NULL,
authours TEXT NOT NULL,
variant TEXT DEFAULT NULL,
publisher TEXT NOT NULL,
publish_year INTEGER NOT NULL,
publish_month INTEGER NOT NULL,
publish_location TEXT DEFAULT NULL,
pages INTEGER NOT NULL,
pagination TEXT DEFAULT NULL,
bookweight TEXT DEFAULT NULL,
last_updated TEXT NOT NULL DEFAULT (datetime()),
deleted INTEGER NOT NULL DEFAULT 0,
donated_by TEXT NOT NULL,
CHECK ((log10(isbn) >= 9 AND log10(isbn) <= 10) OR (log10(isbn) >= 12 AND log10(isbn) <= 13)) -- This is a check to see if the isbn is a canonical format (either isbn)
CHECK (length(title) > 0)
CHECK (publsh_year <= strftime('%Y') AND publish_year >= 0)
CHECK (publish_month >= 1 AND publish_month <= 12)
CHECK (pages > 0)
CHECK (bookweight is NULL OR bookweight > 0)
);