[fix] store times as rfc3339 format

This commit is contained in:
Evan Reichard 2023-10-05 21:04:57 -04:00
parent eb7d711022
commit 70c7f4b991
6 changed files with 136 additions and 125 deletions

View File

@ -260,7 +260,7 @@ func (api *API) addActivities(c *gin.Context) {
UserID: rUser.(string), UserID: rUser.(string),
DocumentID: item.DocumentID, DocumentID: item.DocumentID,
DeviceID: rActivity.DeviceID, DeviceID: rActivity.DeviceID,
StartTime: time.Unix(int64(item.StartTime), 0).UTC(), StartTime: time.Unix(int64(item.StartTime), 0).UTC().Format(time.RFC3339),
Duration: int64(item.Duration), Duration: int64(item.Duration),
Page: int64(item.Page), Page: int64(item.Page),
Pages: int64(item.Pages), Pages: int64(item.Pages),
@ -306,7 +306,7 @@ func (api *API) checkActivitySync(c *gin.Context) {
ID: rCheckActivity.DeviceID, ID: rCheckActivity.DeviceID,
UserID: rUser.(string), UserID: rUser.(string),
DeviceName: rCheckActivity.Device, DeviceName: rCheckActivity.Device,
LastSynced: time.Now().UTC(), LastSynced: time.Now().UTC().Format(time.RFC3339),
}); err != nil { }); err != nil {
log.Error("[checkActivitySync] UpsertDevice DB Error", err) log.Error("[checkActivitySync] UpsertDevice DB Error", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Device"}) c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Invalid Device"})
@ -319,15 +319,23 @@ func (api *API) checkActivitySync(c *gin.Context) {
DeviceID: rCheckActivity.DeviceID, DeviceID: rCheckActivity.DeviceID,
}) })
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
lastActivity = time.UnixMilli(0) lastActivity = time.UnixMilli(0).Format(time.RFC3339)
} else if err != nil { } else if err != nil {
log.Error("[checkActivitySync] GetLastActivity DB Error:", err) log.Error("[checkActivitySync] GetLastActivity DB Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"}) c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"})
return return
} }
// Parse Time
parsedTime, err := time.Parse(time.RFC3339, lastActivity)
if err != nil {
log.Error("[checkActivitySync] Time Parse Error:", err)
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "Unknown Error"})
return
}
c.JSON(http.StatusOK, gin.H{ c.JSON(http.StatusOK, gin.H{
"last_sync": lastActivity.Unix(), "last_sync": parsedTime.Unix(),
}) })
} }
@ -406,7 +414,7 @@ func (api *API) checkDocumentsSync(c *gin.Context) {
ID: rCheckDocs.DeviceID, ID: rCheckDocs.DeviceID,
UserID: rUser.(string), UserID: rUser.(string),
DeviceName: rCheckDocs.Device, DeviceName: rCheckDocs.Device,
LastSynced: time.Now().UTC(), LastSynced: time.Now().UTC().Format(time.RFC3339),
}) })
if err != nil { if err != nil {
log.Error("[checkDocumentsSync] UpsertDevice DB Error", err) log.Error("[checkDocumentsSync] UpsertDevice DB Error", err)

View File

@ -6,15 +6,14 @@ package database
import ( import (
"database/sql" "database/sql"
"time"
) )
type Activity struct { type Activity struct {
UserID string `json:"user_id"` UserID string `json:"user_id"`
DocumentID string `json:"document_id"` DocumentID string `json:"document_id"`
DeviceID string `json:"device_id"` DeviceID string `json:"device_id"`
CreatedAt time.Time `json:"created_at"` CreatedAt string `json:"created_at"`
StartTime time.Time `json:"start_time"` StartTime string `json:"start_time"`
Page int64 `json:"page"` Page int64 `json:"page"`
Pages int64 `json:"pages"` Pages int64 `json:"pages"`
Duration int64 `json:"duration"` Duration int64 `json:"duration"`
@ -24,7 +23,7 @@ type Device struct {
ID string `json:"id"` ID string `json:"id"`
UserID string `json:"user_id"` UserID string `json:"user_id"`
DeviceName string `json:"device_name"` DeviceName string `json:"device_name"`
LastSynced time.Time `json:"last_synced"` LastSynced string `json:"last_synced"`
CreatedAt string `json:"created_at"` CreatedAt string `json:"created_at"`
Sync bool `json:"sync"` Sync bool `json:"sync"`
} }
@ -47,8 +46,8 @@ type Document struct {
Isbn13 *string `json:"isbn13"` Isbn13 *string `json:"isbn13"`
Synced bool `json:"-"` Synced bool `json:"-"`
Deleted bool `json:"-"` Deleted bool `json:"-"`
UpdatedAt time.Time `json:"updated_at"` UpdatedAt string `json:"updated_at"`
CreatedAt time.Time `json:"created_at"` CreatedAt string `json:"created_at"`
} }
type DocumentProgress struct { type DocumentProgress struct {
@ -57,7 +56,7 @@ type DocumentProgress struct {
DeviceID string `json:"device_id"` DeviceID string `json:"device_id"`
Percentage float64 `json:"percentage"` Percentage float64 `json:"percentage"`
Progress string `json:"progress"` Progress string `json:"progress"`
CreatedAt time.Time `json:"created_at"` CreatedAt string `json:"created_at"`
} }
type DocumentUserStatistic struct { type DocumentUserStatistic struct {
@ -83,7 +82,7 @@ type Metadatum struct {
Olid *string `json:"olid"` Olid *string `json:"olid"`
Isbn10 *string `json:"isbn10"` Isbn10 *string `json:"isbn10"`
Isbn13 *string `json:"isbn13"` Isbn13 *string `json:"isbn13"`
CreatedAt time.Time `json:"created_at"` CreatedAt string `json:"created_at"`
} }
type RawActivity struct { type RawActivity struct {
@ -91,11 +90,11 @@ type RawActivity struct {
UserID string `json:"user_id"` UserID string `json:"user_id"`
DocumentID string `json:"document_id"` DocumentID string `json:"document_id"`
DeviceID string `json:"device_id"` DeviceID string `json:"device_id"`
StartTime time.Time `json:"start_time"` StartTime string `json:"start_time"`
Page int64 `json:"page"` Page int64 `json:"page"`
Pages int64 `json:"pages"` Pages int64 `json:"pages"`
Duration int64 `json:"duration"` Duration int64 `json:"duration"`
CreatedAt time.Time `json:"created_at"` CreatedAt string `json:"created_at"`
} }
type User struct { type User struct {
@ -103,7 +102,7 @@ type User struct {
Pass *string `json:"-"` Pass *string `json:"-"`
Admin bool `json:"-"` Admin bool `json:"-"`
TimeOffset *string `json:"time_offset"` TimeOffset *string `json:"time_offset"`
CreatedAt time.Time `json:"created_at"` CreatedAt string `json:"created_at"`
} }
type UserStreak struct { type UserStreak struct {
@ -120,7 +119,7 @@ type UserStreak struct {
type ViewDocumentUserStatistic struct { type ViewDocumentUserStatistic struct {
DocumentID string `json:"document_id"` DocumentID string `json:"document_id"`
UserID string `json:"user_id"` UserID string `json:"user_id"`
LastRead time.Time `json:"last_read"` LastRead string `json:"last_read"`
Page int64 `json:"page"` Page int64 `json:"page"`
Pages int64 `json:"pages"` Pages int64 `json:"pages"`
TotalTimeSeconds sql.NullFloat64 `json:"total_time_seconds"` TotalTimeSeconds sql.NullFloat64 `json:"total_time_seconds"`
@ -134,8 +133,8 @@ type ViewRescaledActivity struct {
UserID string `json:"user_id"` UserID string `json:"user_id"`
DocumentID string `json:"document_id"` DocumentID string `json:"document_id"`
DeviceID string `json:"device_id"` DeviceID string `json:"device_id"`
CreatedAt time.Time `json:"created_at"` CreatedAt string `json:"created_at"`
StartTime time.Time `json:"start_time"` StartTime string `json:"start_time"`
Page int64 `json:"page"` Page int64 `json:"page"`
Pages int64 `json:"pages"` Pages int64 `json:"pages"`
Duration int64 `json:"duration"` Duration int64 `json:"duration"`

View File

@ -61,7 +61,7 @@ WITH filtered_activity AS (
SELECT SELECT
document_id, document_id,
CAST(DATETIME(activity.start_time, users.time_offset) AS TEXT) AS start_time, CAST(STRFTIME('%Y-%m-%d %H:%M:%S', activity.start_time, users.time_offset) AS TEXT) AS start_time,
title, title,
author, author,
duration, duration,
@ -131,8 +131,8 @@ WHERE id = $device_id LIMIT 1;
-- name: GetDevices :many -- name: GetDevices :many
SELECT SELECT
devices.device_name, devices.device_name,
CAST(DATETIME(devices.created_at, users.time_offset) AS TEXT) AS created_at, CAST(STRFTIME('%Y-%m-%d %H:%M:%S', devices.created_at, users.time_offset) AS TEXT) AS created_at,
CAST(DATETIME(devices.last_synced, users.time_offset) AS TEXT) AS last_synced CAST(STRFTIME('%Y-%m-%d %H:%M:%S', devices.last_synced, users.time_offset) AS TEXT) AS last_synced
FROM devices FROM devices
JOIN users ON users.id = devices.user_id JOIN users ON users.id = devices.user_id
WHERE users.id = $user_id; WHERE users.id = $user_id;
@ -192,7 +192,7 @@ SELECT
COALESCE(dus.pages, 0) AS pages, COALESCE(dus.pages, 0) AS pages,
COALESCE(dus.read_pages, 0) AS read_pages, COALESCE(dus.read_pages, 0) AS read_pages,
COALESCE(dus.total_time_seconds, 0) AS total_time_seconds, COALESCE(dus.total_time_seconds, 0) AS total_time_seconds,
DATETIME(COALESCE(dus.last_read, "1970-01-01"), users.time_offset) STRFTIME('%Y-%m-%d %H:%M:%S', COALESCE(dus.last_read, "1970-01-01"), users.time_offset)
AS last_read, AS last_read,
CASE CASE
WHEN dus.percentage > 97.0 THEN 100.0 WHEN dus.percentage > 97.0 THEN 100.0
@ -236,7 +236,7 @@ SELECT
COALESCE(dus.pages, 0) AS pages, COALESCE(dus.pages, 0) AS pages,
COALESCE(dus.read_pages, 0) AS read_pages, COALESCE(dus.read_pages, 0) AS read_pages,
COALESCE(dus.total_time_seconds, 0) AS total_time_seconds, COALESCE(dus.total_time_seconds, 0) AS total_time_seconds,
DATETIME(COALESCE(dus.last_read, "1970-01-01"), users.time_offset) STRFTIME('%Y-%m-%d %H:%M:%S', COALESCE(dus.last_read, "1970-01-01"), users.time_offset)
AS last_read, AS last_read,
CASE CASE
WHEN dus.percentage > 97.0 THEN 100.0 WHEN dus.percentage > 97.0 THEN 100.0

View File

@ -9,7 +9,6 @@ import (
"context" "context"
"database/sql" "database/sql"
"strings" "strings"
"time"
) )
const addActivity = `-- name: AddActivity :one const addActivity = `-- name: AddActivity :one
@ -30,7 +29,7 @@ type AddActivityParams struct {
UserID string `json:"user_id"` UserID string `json:"user_id"`
DocumentID string `json:"document_id"` DocumentID string `json:"document_id"`
DeviceID string `json:"device_id"` DeviceID string `json:"device_id"`
StartTime time.Time `json:"start_time"` StartTime string `json:"start_time"`
Duration int64 `json:"duration"` Duration int64 `json:"duration"`
Page int64 `json:"page"` Page int64 `json:"page"`
Pages int64 `json:"pages"` Pages int64 `json:"pages"`
@ -173,7 +172,7 @@ WITH filtered_activity AS (
SELECT SELECT
document_id, document_id,
CAST(DATETIME(activity.start_time, users.time_offset) AS TEXT) AS start_time, CAST(STRFTIME('%Y-%m-%d %H:%M:%S', activity.start_time, users.time_offset) AS TEXT) AS start_time,
title, title,
author, author,
duration, duration,
@ -397,8 +396,8 @@ func (q *Queries) GetDevice(ctx context.Context, deviceID string) (Device, error
const getDevices = `-- name: GetDevices :many const getDevices = `-- name: GetDevices :many
SELECT SELECT
devices.device_name, devices.device_name,
CAST(DATETIME(devices.created_at, users.time_offset) AS TEXT) AS created_at, CAST(STRFTIME('%Y-%m-%d %H:%M:%S', devices.created_at, users.time_offset) AS TEXT) AS created_at,
CAST(DATETIME(devices.last_synced, users.time_offset) AS TEXT) AS last_synced CAST(STRFTIME('%Y-%m-%d %H:%M:%S', devices.last_synced, users.time_offset) AS TEXT) AS last_synced
FROM devices FROM devices
JOIN users ON users.id = devices.user_id JOIN users ON users.id = devices.user_id
WHERE users.id = ?1 WHERE users.id = ?1
@ -503,7 +502,7 @@ AND start_time >= ?3
type GetDocumentReadStatsParams struct { type GetDocumentReadStatsParams struct {
DocumentID string `json:"document_id"` DocumentID string `json:"document_id"`
UserID string `json:"user_id"` UserID string `json:"user_id"`
StartTime time.Time `json:"start_time"` StartTime string `json:"start_time"`
} }
type GetDocumentReadStatsRow struct { type GetDocumentReadStatsRow struct {
@ -537,7 +536,7 @@ type GetDocumentReadStatsCappedParams struct {
PageDurationCap int64 `json:"page_duration_cap"` PageDurationCap int64 `json:"page_duration_cap"`
DocumentID string `json:"document_id"` DocumentID string `json:"document_id"`
UserID string `json:"user_id"` UserID string `json:"user_id"`
StartTime time.Time `json:"start_time"` StartTime string `json:"start_time"`
} }
type GetDocumentReadStatsCappedRow struct { type GetDocumentReadStatsCappedRow struct {
@ -573,7 +572,7 @@ SELECT
COALESCE(dus.pages, 0) AS pages, COALESCE(dus.pages, 0) AS pages,
COALESCE(dus.read_pages, 0) AS read_pages, COALESCE(dus.read_pages, 0) AS read_pages,
COALESCE(dus.total_time_seconds, 0) AS total_time_seconds, COALESCE(dus.total_time_seconds, 0) AS total_time_seconds,
DATETIME(COALESCE(dus.last_read, "1970-01-01"), users.time_offset) STRFTIME('%Y-%m-%d %H:%M:%S', COALESCE(dus.last_read, "1970-01-01"), users.time_offset)
AS last_read, AS last_read,
CASE CASE
WHEN dus.percentage > 97.0 THEN 100.0 WHEN dus.percentage > 97.0 THEN 100.0
@ -715,7 +714,7 @@ SELECT
COALESCE(dus.pages, 0) AS pages, COALESCE(dus.pages, 0) AS pages,
COALESCE(dus.read_pages, 0) AS read_pages, COALESCE(dus.read_pages, 0) AS read_pages,
COALESCE(dus.total_time_seconds, 0) AS total_time_seconds, COALESCE(dus.total_time_seconds, 0) AS total_time_seconds,
DATETIME(COALESCE(dus.last_read, "1970-01-01"), users.time_offset) STRFTIME('%Y-%m-%d %H:%M:%S', COALESCE(dus.last_read, "1970-01-01"), users.time_offset)
AS last_read, AS last_read,
CASE CASE
WHEN dus.percentage > 97.0 THEN 100.0 WHEN dus.percentage > 97.0 THEN 100.0
@ -819,9 +818,9 @@ type GetLastActivityParams struct {
UserID string `json:"user_id"` UserID string `json:"user_id"`
} }
func (q *Queries) GetLastActivity(ctx context.Context, arg GetLastActivityParams) (time.Time, error) { func (q *Queries) GetLastActivity(ctx context.Context, arg GetLastActivityParams) (string, error) {
row := q.db.QueryRowContext(ctx, getLastActivity, arg.DeviceID, arg.UserID) row := q.db.QueryRowContext(ctx, getLastActivity, arg.DeviceID, arg.UserID)
var start_time time.Time var start_time string
err := row.Scan(&start_time) err := row.Scan(&start_time)
return start_time, err return start_time, err
} }
@ -913,7 +912,7 @@ type GetProgressRow struct {
DeviceID string `json:"device_id"` DeviceID string `json:"device_id"`
Percentage float64 `json:"percentage"` Percentage float64 `json:"percentage"`
Progress string `json:"progress"` Progress string `json:"progress"`
CreatedAt time.Time `json:"created_at"` CreatedAt string `json:"created_at"`
DeviceName string `json:"device_name"` DeviceName string `json:"device_name"`
} }
@ -1295,7 +1294,7 @@ RETURNING id, user_id, device_name, last_synced, created_at, sync
type UpsertDeviceParams struct { type UpsertDeviceParams struct {
ID string `json:"id"` ID string `json:"id"`
UserID string `json:"user_id"` UserID string `json:"user_id"`
LastSynced time.Time `json:"last_synced"` LastSynced string `json:"last_synced"`
DeviceName string `json:"device_name"` DeviceName string `json:"device_name"`
} }

View File

@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS users (
admin BOOLEAN NOT NULL DEFAULT 0 CHECK (admin IN (0, 1)), admin BOOLEAN NOT NULL DEFAULT 0 CHECK (admin IN (0, 1)),
time_offset TEXT NOT NULL DEFAULT '0 hours', time_offset TEXT NOT NULL DEFAULT '0 hours',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now'))
); );
-- Books / Documents -- Books / Documents
@ -39,8 +39,8 @@ CREATE TABLE IF NOT EXISTS documents (
synced BOOLEAN NOT NULL DEFAULT 0 CHECK (synced IN (0, 1)), synced BOOLEAN NOT NULL DEFAULT 0 CHECK (synced IN (0, 1)),
deleted BOOLEAN NOT NULL DEFAULT 0 CHECK (deleted IN (0, 1)), deleted BOOLEAN NOT NULL DEFAULT 0 CHECK (deleted IN (0, 1)),
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')),
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now'))
); );
-- Metadata -- Metadata
@ -57,7 +57,7 @@ CREATE TABLE IF NOT EXISTS metadata (
isbn10 TEXT, isbn10 TEXT,
isbn13 TEXT, isbn13 TEXT,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')),
FOREIGN KEY (document_id) REFERENCES documents (id) FOREIGN KEY (document_id) REFERENCES documents (id)
); );
@ -68,8 +68,8 @@ CREATE TABLE IF NOT EXISTS devices (
user_id TEXT NOT NULL, user_id TEXT NOT NULL,
device_name TEXT NOT NULL, device_name TEXT NOT NULL,
last_synced DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, last_synced DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')),
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')),
sync BOOLEAN NOT NULL DEFAULT 1 CHECK (sync IN (0, 1)), sync BOOLEAN NOT NULL DEFAULT 1 CHECK (sync IN (0, 1)),
FOREIGN KEY (user_id) REFERENCES users (id) FOREIGN KEY (user_id) REFERENCES users (id)
@ -83,7 +83,7 @@ CREATE TABLE IF NOT EXISTS document_progress (
percentage REAL NOT NULL, percentage REAL NOT NULL,
progress TEXT NOT NULL, progress TEXT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')),
FOREIGN KEY (user_id) REFERENCES users (id), FOREIGN KEY (user_id) REFERENCES users (id),
FOREIGN KEY (document_id) REFERENCES documents (id), FOREIGN KEY (document_id) REFERENCES documents (id),
@ -102,7 +102,7 @@ CREATE TABLE IF NOT EXISTS raw_activity (
page INTEGER NOT NULL, page INTEGER NOT NULL,
pages INTEGER NOT NULL, pages INTEGER NOT NULL,
duration INTEGER NOT NULL, duration INTEGER NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, created_at DATETIME NOT NULL DEFAULT (STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')),
FOREIGN KEY (user_id) REFERENCES users (id), FOREIGN KEY (user_id) REFERENCES users (id),
FOREIGN KEY (document_id) REFERENCES documents (id), FOREIGN KEY (document_id) REFERENCES documents (id),
@ -430,6 +430,6 @@ INSERT INTO document_user_statistics SELECT * FROM view_document_user_statistics
CREATE TRIGGER IF NOT EXISTS update_documents_updated_at CREATE TRIGGER IF NOT EXISTS update_documents_updated_at
BEFORE UPDATE ON documents BEGIN BEFORE UPDATE ON documents BEGIN
UPDATE documents UPDATE documents
SET updated_at = CURRENT_TIMESTAMP SET updated_at = STRFTIME('%Y-%m-%dT%H:%M:%SZ', 'now')
WHERE id = old.id; WHERE id = old.id;
END; END;

View File

@ -124,6 +124,11 @@ sql:
type: "string" type: "string"
pointer: true pointer: true
# Override Time
- db_type: "DATETIME"
go_type:
type: "string"
# Do not generate JSON # Do not generate JSON
- column: "documents.synced" - column: "documents.synced"
go_struct_tag: 'json:"-"' go_struct_tag: 'json:"-"'