AnthoLume/database/query.sql.go
Evan Reichard 3d61d0f5ef
All checks were successful
continuous-integration/drone/push Build is passing
perf(db): incremental document stats cache
2024-12-01 12:48:25 -05:00

1442 lines
36 KiB
Go

// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.27.0
// source: query.sql
package database
import (
"context"
"strings"
)
const addActivity = `-- name: AddActivity :one
INSERT INTO activity (
user_id,
document_id,
device_id,
start_time,
duration,
start_percentage,
end_percentage
)
VALUES (?, ?, ?, ?, ?, ?, ?)
RETURNING id, user_id, document_id, device_id, start_time, start_percentage, end_percentage, duration, created_at
`
type AddActivityParams struct {
UserID string `json:"user_id"`
DocumentID string `json:"document_id"`
DeviceID string `json:"device_id"`
StartTime string `json:"start_time"`
Duration int64 `json:"duration"`
StartPercentage float64 `json:"start_percentage"`
EndPercentage float64 `json:"end_percentage"`
}
func (q *Queries) AddActivity(ctx context.Context, arg AddActivityParams) (Activity, error) {
row := q.db.QueryRowContext(ctx, addActivity,
arg.UserID,
arg.DocumentID,
arg.DeviceID,
arg.StartTime,
arg.Duration,
arg.StartPercentage,
arg.EndPercentage,
)
var i Activity
err := row.Scan(
&i.ID,
&i.UserID,
&i.DocumentID,
&i.DeviceID,
&i.StartTime,
&i.StartPercentage,
&i.EndPercentage,
&i.Duration,
&i.CreatedAt,
)
return i, err
}
const addMetadata = `-- name: AddMetadata :one
INSERT INTO metadata (
document_id,
title,
author,
description,
gbid,
olid,
isbn10,
isbn13
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
RETURNING id, document_id, title, author, description, gbid, olid, isbn10, isbn13, created_at
`
type AddMetadataParams struct {
DocumentID string `json:"document_id"`
Title *string `json:"title"`
Author *string `json:"author"`
Description *string `json:"description"`
Gbid *string `json:"gbid"`
Olid *string `json:"olid"`
Isbn10 *string `json:"isbn10"`
Isbn13 *string `json:"isbn13"`
}
func (q *Queries) AddMetadata(ctx context.Context, arg AddMetadataParams) (Metadatum, error) {
row := q.db.QueryRowContext(ctx, addMetadata,
arg.DocumentID,
arg.Title,
arg.Author,
arg.Description,
arg.Gbid,
arg.Olid,
arg.Isbn10,
arg.Isbn13,
)
var i Metadatum
err := row.Scan(
&i.ID,
&i.DocumentID,
&i.Title,
&i.Author,
&i.Description,
&i.Gbid,
&i.Olid,
&i.Isbn10,
&i.Isbn13,
&i.CreatedAt,
)
return i, err
}
const createUser = `-- name: CreateUser :execrows
INSERT INTO users (id, pass, auth_hash, admin)
VALUES (?, ?, ?, ?)
ON CONFLICT DO NOTHING
`
type CreateUserParams struct {
ID string `json:"id"`
Pass *string `json:"-"`
AuthHash *string `json:"auth_hash"`
Admin bool `json:"-"`
}
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (int64, error) {
result, err := q.db.ExecContext(ctx, createUser,
arg.ID,
arg.Pass,
arg.AuthHash,
arg.Admin,
)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
const deleteDocument = `-- name: DeleteDocument :execrows
UPDATE documents
SET
deleted = 1
WHERE id = ?1
`
func (q *Queries) DeleteDocument(ctx context.Context, id string) (int64, error) {
result, err := q.db.ExecContext(ctx, deleteDocument, id)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
const deleteUser = `-- name: DeleteUser :execrows
DELETE FROM users WHERE id = ?1
`
func (q *Queries) DeleteUser(ctx context.Context, id string) (int64, error) {
result, err := q.db.ExecContext(ctx, deleteUser, id)
if err != nil {
return 0, err
}
return result.RowsAffected()
}
const getActivity = `-- name: GetActivity :many
WITH filtered_activity AS (
SELECT
document_id,
device_id,
user_id,
start_time,
duration,
ROUND(CAST(start_percentage AS REAL) * 100, 2) AS start_percentage,
ROUND(CAST(end_percentage AS REAL) * 100, 2) AS end_percentage,
ROUND(CAST(end_percentage - start_percentage AS REAL) * 100, 2) AS read_percentage
FROM activity
WHERE
activity.user_id = ?1
AND (
(
CAST(?2 AS BOOLEAN) = TRUE
AND document_id = ?3
) OR ?2 = FALSE
)
ORDER BY start_time DESC
LIMIT ?5
OFFSET ?4
)
SELECT
document_id,
device_id,
CAST(STRFTIME('%Y-%m-%d %H:%M:%S', LOCAL_TIME(activity.start_time, users.timezone)) AS TEXT) AS start_time,
title,
author,
duration,
start_percentage,
end_percentage,
read_percentage
FROM filtered_activity AS activity
LEFT JOIN documents ON documents.id = activity.document_id
LEFT JOIN users ON users.id = activity.user_id
`
type GetActivityParams struct {
UserID string `json:"user_id"`
DocFilter bool `json:"doc_filter"`
DocumentID string `json:"document_id"`
Offset int64 `json:"offset"`
Limit int64 `json:"limit"`
}
type GetActivityRow struct {
DocumentID string `json:"document_id"`
DeviceID string `json:"device_id"`
StartTime string `json:"start_time"`
Title *string `json:"title"`
Author *string `json:"author"`
Duration int64 `json:"duration"`
StartPercentage float64 `json:"start_percentage"`
EndPercentage float64 `json:"end_percentage"`
ReadPercentage float64 `json:"read_percentage"`
}
func (q *Queries) GetActivity(ctx context.Context, arg GetActivityParams) ([]GetActivityRow, error) {
rows, err := q.db.QueryContext(ctx, getActivity,
arg.UserID,
arg.DocFilter,
arg.DocumentID,
arg.Offset,
arg.Limit,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetActivityRow
for rows.Next() {
var i GetActivityRow
if err := rows.Scan(
&i.DocumentID,
&i.DeviceID,
&i.StartTime,
&i.Title,
&i.Author,
&i.Duration,
&i.StartPercentage,
&i.EndPercentage,
&i.ReadPercentage,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getDailyReadStats = `-- name: GetDailyReadStats :many
WITH RECURSIVE last_30_days AS (
SELECT DATE(LOCAL_TIME(STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now'), timezone)) AS date
FROM users WHERE users.id = ?1
UNION ALL
SELECT DATE(date, '-1 days')
FROM last_30_days
LIMIT 30
),
filtered_activity AS (
SELECT
user_id,
start_time,
duration
FROM activity
WHERE start_time > DATE('now', '-31 days')
AND activity.user_id = ?1
),
activity_days AS (
SELECT
SUM(duration) AS seconds_read,
DATE(LOCAL_TIME(start_time, timezone)) AS day
FROM filtered_activity AS activity
LEFT JOIN users ON users.id = activity.user_id
GROUP BY day
)
SELECT
CAST(date AS TEXT),
CAST(CASE
WHEN seconds_read IS NULL THEN 0
ELSE seconds_read / 60
END AS INTEGER) AS minutes_read
FROM last_30_days
LEFT JOIN activity_days ON activity_days.day == last_30_days.date
ORDER BY date DESC
LIMIT 30
`
type GetDailyReadStatsRow struct {
Date string `json:"date"`
MinutesRead int64 `json:"minutes_read"`
}
func (q *Queries) GetDailyReadStats(ctx context.Context, userID string) ([]GetDailyReadStatsRow, error) {
rows, err := q.db.QueryContext(ctx, getDailyReadStats, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetDailyReadStatsRow
for rows.Next() {
var i GetDailyReadStatsRow
if err := rows.Scan(&i.Date, &i.MinutesRead); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getDatabaseInfo = `-- name: GetDatabaseInfo :one
SELECT
(SELECT COUNT(rowid) FROM activity WHERE activity.user_id = ?1) AS activity_size,
(SELECT COUNT(rowid) FROM documents) AS documents_size,
(SELECT COUNT(rowid) FROM document_progress WHERE document_progress.user_id = ?1) AS progress_size,
(SELECT COUNT(rowid) FROM devices WHERE devices.user_id = ?1) AS devices_size
LIMIT 1
`
type GetDatabaseInfoRow struct {
ActivitySize int64 `json:"activity_size"`
DocumentsSize int64 `json:"documents_size"`
ProgressSize int64 `json:"progress_size"`
DevicesSize int64 `json:"devices_size"`
}
func (q *Queries) GetDatabaseInfo(ctx context.Context, userID string) (GetDatabaseInfoRow, error) {
row := q.db.QueryRowContext(ctx, getDatabaseInfo, userID)
var i GetDatabaseInfoRow
err := row.Scan(
&i.ActivitySize,
&i.DocumentsSize,
&i.ProgressSize,
&i.DevicesSize,
)
return i, err
}
const getDeletedDocuments = `-- name: GetDeletedDocuments :many
SELECT documents.id
FROM documents
WHERE
documents.deleted = true
AND documents.id IN (/*SLICE:document_ids*/?)
`
func (q *Queries) GetDeletedDocuments(ctx context.Context, documentIds []string) ([]string, error) {
query := getDeletedDocuments
var queryParams []interface{}
if len(documentIds) > 0 {
for _, v := range documentIds {
queryParams = append(queryParams, v)
}
query = strings.Replace(query, "/*SLICE:document_ids*/?", strings.Repeat(",?", len(documentIds))[1:], 1)
} else {
query = strings.Replace(query, "/*SLICE:document_ids*/?", "NULL", 1)
}
rows, err := q.db.QueryContext(ctx, query, queryParams...)
if err != nil {
return nil, err
}
defer rows.Close()
var items []string
for rows.Next() {
var id string
if err := rows.Scan(&id); err != nil {
return nil, err
}
items = append(items, id)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getDevice = `-- name: GetDevice :one
SELECT id, user_id, device_name, last_synced, created_at, sync FROM devices
WHERE id = ?1 LIMIT 1
`
func (q *Queries) GetDevice(ctx context.Context, deviceID string) (Device, error) {
row := q.db.QueryRowContext(ctx, getDevice, deviceID)
var i Device
err := row.Scan(
&i.ID,
&i.UserID,
&i.DeviceName,
&i.LastSynced,
&i.CreatedAt,
&i.Sync,
)
return i, err
}
const getDevices = `-- name: GetDevices :many
SELECT
devices.id,
devices.device_name,
CAST(STRFTIME('%Y-%m-%d %H:%M:%S', LOCAL_TIME(devices.created_at, users.timezone)) AS TEXT) AS created_at,
CAST(STRFTIME('%Y-%m-%d %H:%M:%S', LOCAL_TIME(devices.last_synced, users.timezone)) AS TEXT) AS last_synced
FROM devices
JOIN users ON users.id = devices.user_id
WHERE users.id = ?1
ORDER BY devices.last_synced DESC
`
type GetDevicesRow struct {
ID string `json:"id"`
DeviceName string `json:"device_name"`
CreatedAt string `json:"created_at"`
LastSynced string `json:"last_synced"`
}
func (q *Queries) GetDevices(ctx context.Context, userID string) ([]GetDevicesRow, error) {
rows, err := q.db.QueryContext(ctx, getDevices, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetDevicesRow
for rows.Next() {
var i GetDevicesRow
if err := rows.Scan(
&i.ID,
&i.DeviceName,
&i.CreatedAt,
&i.LastSynced,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getDocument = `-- name: GetDocument :one
SELECT id, md5, basepath, filepath, coverfile, title, author, series, series_index, lang, description, words, gbid, olid, isbn10, isbn13, synced, deleted, updated_at, created_at FROM documents
WHERE id = ?1 LIMIT 1
`
func (q *Queries) GetDocument(ctx context.Context, documentID string) (Document, error) {
row := q.db.QueryRowContext(ctx, getDocument, documentID)
var i Document
err := row.Scan(
&i.ID,
&i.Md5,
&i.Basepath,
&i.Filepath,
&i.Coverfile,
&i.Title,
&i.Author,
&i.Series,
&i.SeriesIndex,
&i.Lang,
&i.Description,
&i.Words,
&i.Gbid,
&i.Olid,
&i.Isbn10,
&i.Isbn13,
&i.Synced,
&i.Deleted,
&i.UpdatedAt,
&i.CreatedAt,
)
return i, err
}
const getDocumentProgress = `-- name: GetDocumentProgress :one
SELECT
document_progress.user_id, document_progress.document_id, document_progress.device_id, document_progress.percentage, document_progress.progress, document_progress.created_at,
devices.device_name
FROM document_progress
JOIN devices ON document_progress.device_id = devices.id
WHERE
document_progress.user_id = ?1
AND document_progress.document_id = ?2
ORDER BY
document_progress.created_at
DESC
LIMIT 1
`
type GetDocumentProgressParams struct {
UserID string `json:"user_id"`
DocumentID string `json:"document_id"`
}
type GetDocumentProgressRow struct {
UserID string `json:"user_id"`
DocumentID string `json:"document_id"`
DeviceID string `json:"device_id"`
Percentage float64 `json:"percentage"`
Progress string `json:"progress"`
CreatedAt string `json:"created_at"`
DeviceName string `json:"device_name"`
}
func (q *Queries) GetDocumentProgress(ctx context.Context, arg GetDocumentProgressParams) (GetDocumentProgressRow, error) {
row := q.db.QueryRowContext(ctx, getDocumentProgress, arg.UserID, arg.DocumentID)
var i GetDocumentProgressRow
err := row.Scan(
&i.UserID,
&i.DocumentID,
&i.DeviceID,
&i.Percentage,
&i.Progress,
&i.CreatedAt,
&i.DeviceName,
)
return i, err
}
const getDocumentWithStats = `-- name: GetDocumentWithStats :one
SELECT
docs.id,
docs.title,
docs.author,
docs.description,
docs.isbn10,
docs.isbn13,
docs.filepath,
docs.words,
CAST(COALESCE(dus.total_wpm, 0.0) AS INTEGER) AS wpm,
COALESCE(dus.read_percentage, 0) AS read_percentage,
COALESCE(dus.total_time_seconds, 0) AS total_time_seconds,
STRFTIME('%Y-%m-%d %H:%M:%S', LOCAL_TIME(COALESCE(dus.last_read, STRFTIME('%Y-%m-%dT%H:%M:%SZ', 0, 'unixepoch')), users.timezone))
AS last_read,
ROUND(CAST(CASE
WHEN dus.percentage IS NULL THEN 0.0
WHEN (dus.percentage * 100.0) > 97.0 THEN 100.0
ELSE dus.percentage * 100.0
END AS REAL), 2) AS percentage,
CAST(CASE
WHEN dus.total_time_seconds IS NULL THEN 0.0
ELSE
CAST(dus.total_time_seconds AS REAL)
/ (dus.read_percentage * 100.0)
END AS INTEGER) AS seconds_per_percent
FROM documents AS docs
LEFT JOIN users ON users.id = ?1
LEFT JOIN
document_user_statistics AS dus
ON dus.document_id = docs.id AND dus.user_id = ?1
WHERE users.id = ?1
AND docs.id = ?2
LIMIT 1
`
type GetDocumentWithStatsParams struct {
UserID string `json:"user_id"`
DocumentID string `json:"document_id"`
}
type GetDocumentWithStatsRow struct {
ID string `json:"id"`
Title *string `json:"title"`
Author *string `json:"author"`
Description *string `json:"description"`
Isbn10 *string `json:"isbn10"`
Isbn13 *string `json:"isbn13"`
Filepath *string `json:"filepath"`
Words *int64 `json:"words"`
Wpm int64 `json:"wpm"`
ReadPercentage float64 `json:"read_percentage"`
TotalTimeSeconds int64 `json:"total_time_seconds"`
LastRead interface{} `json:"last_read"`
Percentage float64 `json:"percentage"`
SecondsPerPercent int64 `json:"seconds_per_percent"`
}
func (q *Queries) GetDocumentWithStats(ctx context.Context, arg GetDocumentWithStatsParams) (GetDocumentWithStatsRow, error) {
row := q.db.QueryRowContext(ctx, getDocumentWithStats, arg.UserID, arg.DocumentID)
var i GetDocumentWithStatsRow
err := row.Scan(
&i.ID,
&i.Title,
&i.Author,
&i.Description,
&i.Isbn10,
&i.Isbn13,
&i.Filepath,
&i.Words,
&i.Wpm,
&i.ReadPercentage,
&i.TotalTimeSeconds,
&i.LastRead,
&i.Percentage,
&i.SecondsPerPercent,
)
return i, err
}
const getDocuments = `-- name: GetDocuments :many
SELECT id, md5, basepath, filepath, coverfile, title, author, series, series_index, lang, description, words, gbid, olid, isbn10, isbn13, synced, deleted, updated_at, created_at FROM documents
ORDER BY created_at DESC
LIMIT ?2
OFFSET ?1
`
type GetDocumentsParams struct {
Offset int64 `json:"offset"`
Limit int64 `json:"limit"`
}
func (q *Queries) GetDocuments(ctx context.Context, arg GetDocumentsParams) ([]Document, error) {
rows, err := q.db.QueryContext(ctx, getDocuments, arg.Offset, arg.Limit)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Document
for rows.Next() {
var i Document
if err := rows.Scan(
&i.ID,
&i.Md5,
&i.Basepath,
&i.Filepath,
&i.Coverfile,
&i.Title,
&i.Author,
&i.Series,
&i.SeriesIndex,
&i.Lang,
&i.Description,
&i.Words,
&i.Gbid,
&i.Olid,
&i.Isbn10,
&i.Isbn13,
&i.Synced,
&i.Deleted,
&i.UpdatedAt,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getDocumentsSize = `-- name: GetDocumentsSize :one
SELECT
COUNT(rowid) AS length
FROM documents AS docs
WHERE ?1 IS NULL OR (
docs.title LIKE ?1 OR
docs.author LIKE ?1
)
LIMIT 1
`
func (q *Queries) GetDocumentsSize(ctx context.Context, query interface{}) (int64, error) {
row := q.db.QueryRowContext(ctx, getDocumentsSize, query)
var length int64
err := row.Scan(&length)
return length, err
}
const getDocumentsWithStats = `-- name: GetDocumentsWithStats :many
SELECT
docs.id,
docs.title,
docs.author,
docs.description,
docs.isbn10,
docs.isbn13,
docs.filepath,
docs.words,
CAST(COALESCE(dus.total_wpm, 0.0) AS INTEGER) AS wpm,
COALESCE(dus.read_percentage, 0) AS read_percentage,
COALESCE(dus.total_time_seconds, 0) AS total_time_seconds,
STRFTIME('%Y-%m-%d %H:%M:%S', LOCAL_TIME(COALESCE(dus.last_read, STRFTIME('%Y-%m-%dT%H:%M:%SZ', 0, 'unixepoch')), users.timezone))
AS last_read,
ROUND(CAST(CASE
WHEN dus.percentage IS NULL THEN 0.0
WHEN (dus.percentage * 100.0) > 97.0 THEN 100.0
ELSE dus.percentage * 100.0
END AS REAL), 2) AS percentage,
CASE
WHEN dus.total_time_seconds IS NULL THEN 0.0
ELSE
ROUND(
CAST(dus.total_time_seconds AS REAL)
/ (dus.read_percentage * 100.0)
)
END AS seconds_per_percent
FROM documents AS docs
LEFT JOIN users ON users.id = ?1
LEFT JOIN
document_user_statistics AS dus
ON dus.document_id = docs.id AND dus.user_id = ?1
WHERE
docs.deleted = false AND (
?2 IS NULL OR (
docs.title LIKE ?2 OR
docs.author LIKE ?2
)
)
ORDER BY dus.last_read DESC, docs.created_at DESC
LIMIT ?4
OFFSET ?3
`
type GetDocumentsWithStatsParams struct {
UserID string `json:"user_id"`
Query interface{} `json:"query"`
Offset int64 `json:"offset"`
Limit int64 `json:"limit"`
}
type GetDocumentsWithStatsRow struct {
ID string `json:"id"`
Title *string `json:"title"`
Author *string `json:"author"`
Description *string `json:"description"`
Isbn10 *string `json:"isbn10"`
Isbn13 *string `json:"isbn13"`
Filepath *string `json:"filepath"`
Words *int64 `json:"words"`
Wpm int64 `json:"wpm"`
ReadPercentage float64 `json:"read_percentage"`
TotalTimeSeconds int64 `json:"total_time_seconds"`
LastRead interface{} `json:"last_read"`
Percentage float64 `json:"percentage"`
SecondsPerPercent interface{} `json:"seconds_per_percent"`
}
func (q *Queries) GetDocumentsWithStats(ctx context.Context, arg GetDocumentsWithStatsParams) ([]GetDocumentsWithStatsRow, error) {
rows, err := q.db.QueryContext(ctx, getDocumentsWithStats,
arg.UserID,
arg.Query,
arg.Offset,
arg.Limit,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetDocumentsWithStatsRow
for rows.Next() {
var i GetDocumentsWithStatsRow
if err := rows.Scan(
&i.ID,
&i.Title,
&i.Author,
&i.Description,
&i.Isbn10,
&i.Isbn13,
&i.Filepath,
&i.Words,
&i.Wpm,
&i.ReadPercentage,
&i.TotalTimeSeconds,
&i.LastRead,
&i.Percentage,
&i.SecondsPerPercent,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getLastActivity = `-- name: GetLastActivity :one
SELECT start_time
FROM activity
WHERE device_id = ?1
AND user_id = ?2
ORDER BY start_time DESC LIMIT 1
`
type GetLastActivityParams struct {
DeviceID string `json:"device_id"`
UserID string `json:"user_id"`
}
func (q *Queries) GetLastActivity(ctx context.Context, arg GetLastActivityParams) (string, error) {
row := q.db.QueryRowContext(ctx, getLastActivity, arg.DeviceID, arg.UserID)
var start_time string
err := row.Scan(&start_time)
return start_time, err
}
const getMissingDocuments = `-- name: GetMissingDocuments :many
SELECT documents.id, documents.md5, documents.basepath, documents.filepath, documents.coverfile, documents.title, documents.author, documents.series, documents.series_index, documents.lang, documents.description, documents.words, documents.gbid, documents.olid, documents.isbn10, documents.isbn13, documents.synced, documents.deleted, documents.updated_at, documents.created_at FROM documents
WHERE
documents.filepath IS NOT NULL
AND documents.deleted = false
AND documents.id NOT IN (/*SLICE:document_ids*/?)
`
func (q *Queries) GetMissingDocuments(ctx context.Context, documentIds []string) ([]Document, error) {
query := getMissingDocuments
var queryParams []interface{}
if len(documentIds) > 0 {
for _, v := range documentIds {
queryParams = append(queryParams, v)
}
query = strings.Replace(query, "/*SLICE:document_ids*/?", strings.Repeat(",?", len(documentIds))[1:], 1)
} else {
query = strings.Replace(query, "/*SLICE:document_ids*/?", "NULL", 1)
}
rows, err := q.db.QueryContext(ctx, query, queryParams...)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Document
for rows.Next() {
var i Document
if err := rows.Scan(
&i.ID,
&i.Md5,
&i.Basepath,
&i.Filepath,
&i.Coverfile,
&i.Title,
&i.Author,
&i.Series,
&i.SeriesIndex,
&i.Lang,
&i.Description,
&i.Words,
&i.Gbid,
&i.Olid,
&i.Isbn10,
&i.Isbn13,
&i.Synced,
&i.Deleted,
&i.UpdatedAt,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getProgress = `-- name: GetProgress :many
SELECT
documents.title,
documents.author,
devices.device_name,
ROUND(CAST(progress.percentage AS REAL) * 100, 2) AS percentage,
progress.document_id,
progress.user_id,
CAST(STRFTIME('%Y-%m-%d %H:%M:%S', LOCAL_TIME(progress.created_at, users.timezone)) AS TEXT) AS created_at
FROM document_progress AS progress
LEFT JOIN users ON progress.user_id = users.id
LEFT JOIN devices ON progress.device_id = devices.id
LEFT JOIN documents ON progress.document_id = documents.id
WHERE
progress.user_id = ?1
AND (
(
CAST(?2 AS BOOLEAN) = TRUE
AND document_id = ?3
) OR ?2 = FALSE
)
ORDER BY created_at DESC
LIMIT ?5
OFFSET ?4
`
type GetProgressParams struct {
UserID string `json:"user_id"`
DocFilter bool `json:"doc_filter"`
DocumentID string `json:"document_id"`
Offset int64 `json:"offset"`
Limit int64 `json:"limit"`
}
type GetProgressRow struct {
Title *string `json:"title"`
Author *string `json:"author"`
DeviceName string `json:"device_name"`
Percentage float64 `json:"percentage"`
DocumentID string `json:"document_id"`
UserID string `json:"user_id"`
CreatedAt string `json:"created_at"`
}
func (q *Queries) GetProgress(ctx context.Context, arg GetProgressParams) ([]GetProgressRow, error) {
rows, err := q.db.QueryContext(ctx, getProgress,
arg.UserID,
arg.DocFilter,
arg.DocumentID,
arg.Offset,
arg.Limit,
)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetProgressRow
for rows.Next() {
var i GetProgressRow
if err := rows.Scan(
&i.Title,
&i.Author,
&i.DeviceName,
&i.Percentage,
&i.DocumentID,
&i.UserID,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getUser = `-- name: GetUser :one
SELECT id, pass, auth_hash, admin, timezone, created_at FROM users
WHERE id = ?1 LIMIT 1
`
func (q *Queries) GetUser(ctx context.Context, userID string) (User, error) {
row := q.db.QueryRowContext(ctx, getUser, userID)
var i User
err := row.Scan(
&i.ID,
&i.Pass,
&i.AuthHash,
&i.Admin,
&i.Timezone,
&i.CreatedAt,
)
return i, err
}
const getUserStatistics = `-- name: GetUserStatistics :many
SELECT
user_id,
CAST(SUM(total_words_read) AS INTEGER) AS total_words_read,
CAST(SUM(total_time_seconds) AS INTEGER) AS total_seconds,
ROUND(COALESCE(CAST(SUM(total_words_read) AS REAL) / (SUM(total_time_seconds) / 60.0), 0.0), 2)
AS total_wpm,
CAST(SUM(yearly_words_read) AS INTEGER) AS yearly_words_read,
CAST(SUM(yearly_time_seconds) AS INTEGER) AS yearly_seconds,
ROUND(COALESCE(CAST(SUM(yearly_words_read) AS REAL) / (SUM(yearly_time_seconds) / 60.0), 0.0), 2)
AS yearly_wpm,
CAST(SUM(monthly_words_read) AS INTEGER) AS monthly_words_read,
CAST(SUM(monthly_time_seconds) AS INTEGER) AS monthly_seconds,
ROUND(COALESCE(CAST(SUM(monthly_words_read) AS REAL) / (SUM(monthly_time_seconds) / 60.0), 0.0), 2)
AS monthly_wpm,
CAST(SUM(weekly_words_read) AS INTEGER) AS weekly_words_read,
CAST(SUM(weekly_time_seconds) AS INTEGER) AS weekly_seconds,
ROUND(COALESCE(CAST(SUM(weekly_words_read) AS REAL) / (SUM(weekly_time_seconds) / 60.0), 0.0), 2)
AS weekly_wpm
FROM document_user_statistics
WHERE total_words_read > 0
GROUP BY user_id
ORDER BY total_wpm DESC
`
type GetUserStatisticsRow struct {
UserID string `json:"user_id"`
TotalWordsRead int64 `json:"total_words_read"`
TotalSeconds int64 `json:"total_seconds"`
TotalWpm float64 `json:"total_wpm"`
YearlyWordsRead int64 `json:"yearly_words_read"`
YearlySeconds int64 `json:"yearly_seconds"`
YearlyWpm float64 `json:"yearly_wpm"`
MonthlyWordsRead int64 `json:"monthly_words_read"`
MonthlySeconds int64 `json:"monthly_seconds"`
MonthlyWpm float64 `json:"monthly_wpm"`
WeeklyWordsRead int64 `json:"weekly_words_read"`
WeeklySeconds int64 `json:"weekly_seconds"`
WeeklyWpm float64 `json:"weekly_wpm"`
}
func (q *Queries) GetUserStatistics(ctx context.Context) ([]GetUserStatisticsRow, error) {
rows, err := q.db.QueryContext(ctx, getUserStatistics)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetUserStatisticsRow
for rows.Next() {
var i GetUserStatisticsRow
if err := rows.Scan(
&i.UserID,
&i.TotalWordsRead,
&i.TotalSeconds,
&i.TotalWpm,
&i.YearlyWordsRead,
&i.YearlySeconds,
&i.YearlyWpm,
&i.MonthlyWordsRead,
&i.MonthlySeconds,
&i.MonthlyWpm,
&i.WeeklyWordsRead,
&i.WeeklySeconds,
&i.WeeklyWpm,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getUserStreaks = `-- name: GetUserStreaks :many
SELECT user_id, "window", max_streak, max_streak_start_date, max_streak_end_date, current_streak, current_streak_start_date, current_streak_end_date FROM user_streaks
WHERE user_id = ?1
`
func (q *Queries) GetUserStreaks(ctx context.Context, userID string) ([]UserStreak, error) {
rows, err := q.db.QueryContext(ctx, getUserStreaks, userID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []UserStreak
for rows.Next() {
var i UserStreak
if err := rows.Scan(
&i.UserID,
&i.Window,
&i.MaxStreak,
&i.MaxStreakStartDate,
&i.MaxStreakEndDate,
&i.CurrentStreak,
&i.CurrentStreakStartDate,
&i.CurrentStreakEndDate,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getUsers = `-- name: GetUsers :many
SELECT id, pass, auth_hash, admin, timezone, created_at FROM users
`
func (q *Queries) GetUsers(ctx context.Context) ([]User, error) {
rows, err := q.db.QueryContext(ctx, getUsers)
if err != nil {
return nil, err
}
defer rows.Close()
var items []User
for rows.Next() {
var i User
if err := rows.Scan(
&i.ID,
&i.Pass,
&i.AuthHash,
&i.Admin,
&i.Timezone,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getWantedDocuments = `-- name: GetWantedDocuments :many
SELECT
CAST(value AS TEXT) AS id,
CAST((documents.filepath IS NULL) AS BOOLEAN) AS want_file,
CAST((documents.id IS NULL) AS BOOLEAN) AS want_metadata
FROM json_each(?1)
LEFT JOIN documents
ON value = documents.id
WHERE (
documents.id IS NOT NULL
AND documents.deleted = false
AND documents.filepath IS NULL
)
OR (documents.id IS NULL)
OR CAST(?1 AS TEXT) != CAST(?1 AS TEXT)
`
type GetWantedDocumentsRow struct {
ID string `json:"id"`
WantFile bool `json:"want_file"`
WantMetadata bool `json:"want_metadata"`
}
func (q *Queries) GetWantedDocuments(ctx context.Context, documentIds string) ([]GetWantedDocumentsRow, error) {
rows, err := q.db.QueryContext(ctx, getWantedDocuments, documentIds)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetWantedDocumentsRow
for rows.Next() {
var i GetWantedDocumentsRow
if err := rows.Scan(&i.ID, &i.WantFile, &i.WantMetadata); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const updateProgress = `-- name: UpdateProgress :one
INSERT OR REPLACE INTO document_progress (
user_id,
document_id,
device_id,
percentage,
progress
)
VALUES (?, ?, ?, ?, ?)
RETURNING user_id, document_id, device_id, percentage, progress, created_at
`
type UpdateProgressParams struct {
UserID string `json:"user_id"`
DocumentID string `json:"document_id"`
DeviceID string `json:"device_id"`
Percentage float64 `json:"percentage"`
Progress string `json:"progress"`
}
func (q *Queries) UpdateProgress(ctx context.Context, arg UpdateProgressParams) (DocumentProgress, error) {
row := q.db.QueryRowContext(ctx, updateProgress,
arg.UserID,
arg.DocumentID,
arg.DeviceID,
arg.Percentage,
arg.Progress,
)
var i DocumentProgress
err := row.Scan(
&i.UserID,
&i.DocumentID,
&i.DeviceID,
&i.Percentage,
&i.Progress,
&i.CreatedAt,
)
return i, err
}
const updateSettings = `-- name: UpdateSettings :one
INSERT INTO settings (name, value)
VALUES (?, ?)
ON CONFLICT DO UPDATE
SET
name = COALESCE(excluded.name, name),
value = COALESCE(excluded.value, value)
RETURNING id, name, value, created_at
`
type UpdateSettingsParams struct {
Name string `json:"name"`
Value string `json:"value"`
}
func (q *Queries) UpdateSettings(ctx context.Context, arg UpdateSettingsParams) (Setting, error) {
row := q.db.QueryRowContext(ctx, updateSettings, arg.Name, arg.Value)
var i Setting
err := row.Scan(
&i.ID,
&i.Name,
&i.Value,
&i.CreatedAt,
)
return i, err
}
const updateUser = `-- name: UpdateUser :one
UPDATE users
SET
pass = COALESCE(?1, pass),
auth_hash = COALESCE(?2, auth_hash),
timezone = COALESCE(?3, timezone),
admin = COALESCE(?4, admin)
WHERE id = ?5
RETURNING id, pass, auth_hash, admin, timezone, created_at
`
type UpdateUserParams struct {
Password *string `json:"-"`
AuthHash *string `json:"auth_hash"`
Timezone *string `json:"timezone"`
Admin bool `json:"-"`
UserID string `json:"user_id"`
}
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) {
row := q.db.QueryRowContext(ctx, updateUser,
arg.Password,
arg.AuthHash,
arg.Timezone,
arg.Admin,
arg.UserID,
)
var i User
err := row.Scan(
&i.ID,
&i.Pass,
&i.AuthHash,
&i.Admin,
&i.Timezone,
&i.CreatedAt,
)
return i, err
}
const upsertDevice = `-- name: UpsertDevice :one
INSERT INTO devices (id, user_id, last_synced, device_name)
VALUES (?, ?, ?, ?)
ON CONFLICT DO UPDATE
SET
device_name = COALESCE(excluded.device_name, device_name),
last_synced = COALESCE(excluded.last_synced, last_synced)
RETURNING id, user_id, device_name, last_synced, created_at, sync
`
type UpsertDeviceParams struct {
ID string `json:"id"`
UserID string `json:"user_id"`
LastSynced string `json:"last_synced"`
DeviceName string `json:"device_name"`
}
func (q *Queries) UpsertDevice(ctx context.Context, arg UpsertDeviceParams) (Device, error) {
row := q.db.QueryRowContext(ctx, upsertDevice,
arg.ID,
arg.UserID,
arg.LastSynced,
arg.DeviceName,
)
var i Device
err := row.Scan(
&i.ID,
&i.UserID,
&i.DeviceName,
&i.LastSynced,
&i.CreatedAt,
&i.Sync,
)
return i, err
}
const upsertDocument = `-- name: UpsertDocument :one
INSERT INTO documents (
id,
md5,
basepath,
filepath,
coverfile,
title,
author,
series,
series_index,
lang,
description,
words,
olid,
gbid,
isbn10,
isbn13
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT DO UPDATE
SET
md5 = COALESCE(excluded.md5, md5),
basepath = COALESCE(excluded.basepath, basepath),
filepath = COALESCE(excluded.filepath, filepath),
coverfile = COALESCE(excluded.coverfile, coverfile),
title = COALESCE(excluded.title, title),
author = COALESCE(excluded.author, author),
series = COALESCE(excluded.series, series),
series_index = COALESCE(excluded.series_index, series_index),
lang = COALESCE(excluded.lang, lang),
description = COALESCE(excluded.description, description),
words = COALESCE(excluded.words, words),
olid = COALESCE(excluded.olid, olid),
gbid = COALESCE(excluded.gbid, gbid),
isbn10 = COALESCE(excluded.isbn10, isbn10),
isbn13 = COALESCE(excluded.isbn13, isbn13)
RETURNING id, md5, basepath, filepath, coverfile, title, author, series, series_index, lang, description, words, gbid, olid, isbn10, isbn13, synced, deleted, updated_at, created_at
`
type UpsertDocumentParams struct {
ID string `json:"id"`
Md5 *string `json:"md5"`
Basepath *string `json:"basepath"`
Filepath *string `json:"filepath"`
Coverfile *string `json:"coverfile"`
Title *string `json:"title"`
Author *string `json:"author"`
Series *string `json:"series"`
SeriesIndex *int64 `json:"series_index"`
Lang *string `json:"lang"`
Description *string `json:"description"`
Words *int64 `json:"words"`
Olid *string `json:"-"`
Gbid *string `json:"gbid"`
Isbn10 *string `json:"isbn10"`
Isbn13 *string `json:"isbn13"`
}
func (q *Queries) UpsertDocument(ctx context.Context, arg UpsertDocumentParams) (Document, error) {
row := q.db.QueryRowContext(ctx, upsertDocument,
arg.ID,
arg.Md5,
arg.Basepath,
arg.Filepath,
arg.Coverfile,
arg.Title,
arg.Author,
arg.Series,
arg.SeriesIndex,
arg.Lang,
arg.Description,
arg.Words,
arg.Olid,
arg.Gbid,
arg.Isbn10,
arg.Isbn13,
)
var i Document
err := row.Scan(
&i.ID,
&i.Md5,
&i.Basepath,
&i.Filepath,
&i.Coverfile,
&i.Title,
&i.Author,
&i.Series,
&i.SeriesIndex,
&i.Lang,
&i.Description,
&i.Words,
&i.Gbid,
&i.Olid,
&i.Isbn10,
&i.Isbn13,
&i.Synced,
&i.Deleted,
&i.UpdatedAt,
&i.CreatedAt,
)
return i, err
}