xbit/main.go

147 lines
3.2 KiB
Go
Raw Normal View History

2024-03-20 01:31:32 +00:00
package main
import (
"database/sql"
"errors"
2024-03-20 01:31:32 +00:00
"flag"
"fmt"
_ "github.com/mattn/go-sqlite3"
"log"
"math"
"strconv"
2024-03-20 01:31:32 +00:00
"time"
)
2024-03-20 01:31:32 +00:00
type EventsRow struct {
description sql.NullString
timestamp sql.NullInt64
interval sql.NullString
period sql.NullString
yearknown sql.NullBool
monthknown sql.NullBool
dayknown sql.NullBool
hourknown sql.NullBool
minuteknown sql.NullBool
secondknown sql.NullBool
}
type TimelineDB struct {
*sql.DB
2024-03-20 01:31:32 +00:00
}
func formula(p float64) int64 {
yearsAgo := math.Exp(20.3444*math.Pow(p, 3)+3) - math.Exp(3)
return int64(yearsAgo * 31556926)
}
2024-03-20 01:31:32 +00:00
func (db *TimelineDB) closestEvents(t int64, n int, after bool, before bool) ([]EventsRow, error) {
ulimit := int64(math.MaxInt64)
llimit := int64(math.MinInt64)
if after && before {
return nil, errors.New("after and before can't both be true")
} else if after {
llimit = t
} else if before {
ulimit = t
}
2024-03-20 01:31:32 +00:00
query := `
2024-03-20 01:31:32 +00:00
select description, timestamp, yearknown, monthknown, dayknown, hourknown, minuteknown, secondknown
2024-03-20 01:31:32 +00:00
from events
2024-03-20 01:31:32 +00:00
where timestamp between ? and ?
2024-03-20 01:31:32 +00:00
order by abs(? - timestamp) asc
limit ?
2024-03-20 01:31:32 +00:00
`
stmt, err := db.Prepare(query)
if err != nil {
2024-03-20 01:31:32 +00:00
return nil, err
2024-03-20 01:31:32 +00:00
}
defer stmt.Close()
2024-03-20 01:31:32 +00:00
rows, err := stmt.Query(llimit, ulimit, t, n)
if err != nil {
2024-03-20 01:31:32 +00:00
return nil, err
}
defer rows.Close()
2024-03-20 01:31:32 +00:00
events := make([]EventsRow, n)
i := 0
for ; rows.Next(); i++ {
var event EventsRow
err = rows.Scan(&event.description, &event.timestamp, &event.yearknown, &event.monthknown, &event.dayknown, &event.hourknown, &event.minuteknown, &event.secondknown)
if err != nil {
return events, err
}
events[i] = event
}
return events[:i], err
2024-03-20 01:31:32 +00:00
}
2024-03-20 01:31:32 +00:00
func (event *EventsRow) Output() (int64, string, string, error) {
if !event.timestamp.Valid {
return 0, "", "", errors.New("null timestamp")
} else if !event.description.Valid {
return 0, "", "", errors.New("null description")
2024-03-20 01:31:32 +00:00
}
timestamp := event.timestamp.Int64
desc := event.description.String
yearknown := event.yearknown.Valid && event.yearknown.Bool
var ago string
date := time.Unix(timestamp, 0)
if !yearknown {
yeardiff := time.Now().Year() - date.Year()
ago = strconv.Itoa(yeardiff) + " years ago"
} else {
ago = date.String()
}
2024-03-20 01:31:32 +00:00
return timestamp, ago, desc, nil
2024-03-20 01:31:32 +00:00
}
2024-03-20 01:31:32 +00:00
func main() {
percent := flag.Float64("p", -1, "percentage")
nevents := flag.Int("n", 1, "number of events")
2024-03-20 01:31:32 +00:00
after := flag.Bool("a", false, "only return events occurring after computed timestamp")
before := flag.Bool("b", false, "only return events occurring before computed timestamp")
2024-03-20 01:31:32 +00:00
flag.Parse()
2024-03-20 01:31:32 +00:00
if *after && *before {
log.Fatal("cannot pass both -a and -b")
}
2024-03-20 01:31:32 +00:00
if *percent < 0 || *percent > 1 {
log.Fatal("invalid percentage")
2024-03-20 01:31:32 +00:00
} else if *nevents < 0 {
log.Fatal("number of events must be non-negative")
2024-03-20 01:31:32 +00:00
}
t := time.Now().Unix() - formula(*percent)
2024-03-20 01:31:32 +00:00
var db TimelineDB
var err error
db.DB, err = sql.Open("sqlite3", "./timeline.db")
2024-03-20 01:31:32 +00:00
if err != nil {
log.Fatal(err)
}
defer db.Close()
2024-03-20 01:31:32 +00:00
events, err := db.closestEvents(t, *nevents, *after, *before)
2024-03-20 01:31:32 +00:00
if err != nil {
log.Fatal(err)
}
for _, event := range events {
timestamp, date, desc, err := event.Output()
if err != nil {
log.Fatal(err)
}
2024-03-20 01:31:32 +00:00
fmt.Printf("%v\t%v\t%v\n", timestamp, date, desc)
}
2024-03-20 01:31:32 +00:00
}