Compare commits
13 Commits
main
..
35cd759205
| Author | SHA1 | Date | |
|---|---|---|---|
| 35cd759205 | |||
| 1260930b76 | |||
| bafbc3578c | |||
| 2925fb174c | |||
| 76a56879b0 | |||
| 1ecf16bd0a | |||
| 990c9a66bd | |||
| 4d42e6cae8 | |||
| 3d381b1a4e | |||
| f0daafc5a8 | |||
| ab910e266f | |||
| d997666bc6 | |||
| ad76ffcff4 |
@@ -1,39 +0,0 @@
|
||||
# xbit
|
||||
`xbit` (xkcd backward in time) is a program for outputting historical events
|
||||
while completing a task, inspired by [xkcd](https://xkcd.com/1017). The program
|
||||
reads a SQLite database that stores event descriptions and timestamps, allowing
|
||||
you to easily add your own events of interest.
|
||||
|
||||
The provided SQL file will pre-populate the database with thousands of events
|
||||
scraped from
|
||||
[Wikipedia](https://en.wikipedia.org/wiki/Detailed_logarithmic_timeline). Of
|
||||
these events, over 300 were manually given timestamps.
|
||||
|
||||
## How to use
|
||||
First create the database:
|
||||
```
|
||||
sqlite3 timeline.db < timeline.sql
|
||||
```
|
||||
|
||||
Then set the `XBIT_DB` environment variable to the database file.
|
||||
```
|
||||
export XBIT_DB=./timeline.db
|
||||
```
|
||||
|
||||
Call the program with `xbit -p <percentage>`, like `xbit -p 0.25`. This will
|
||||
calculate a timestamp using the formula from the xkcd comic, then output
|
||||
information about the event in the database closest to that timestamp. See
|
||||
`xbit -h` for more args.
|
||||
|
||||
## Database details
|
||||
* Due to the time-consuming nature of manually assigning timestamps, there are
|
||||
many events that were scraped from Wikipedia but not given timestamps. You
|
||||
may wish to skim through these and add timestamps to events you personally find
|
||||
interesting.
|
||||
* For an event to be output by `xbit`, it must have an entry in the database
|
||||
with a unix timestamp of when the event occured. You can optionally set bools
|
||||
in the "known" columns to indicate how precise your timestamp is. These may be
|
||||
used to modify how the event is output.
|
||||
* There are a few other columns that mainly exist as artifacts from the
|
||||
scraping process. These are not used by `xbit` and do not need to be
|
||||
populated.
|
||||
@@ -1,4 +1,4 @@
|
||||
module scm.dairydemon.net/filifa/xbit
|
||||
module dairydemon.net/xbit
|
||||
|
||||
go 1.19
|
||||
|
||||
|
||||
@@ -2,13 +2,11 @@ package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"log"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
@@ -35,22 +33,28 @@ func formula(p float64) int64 {
|
||||
return int64(yearsAgo * 31556926)
|
||||
}
|
||||
|
||||
func (db *TimelineDB) closestEvents(t int64, n uint, llimit int64, ulimit int64) ([]EventsRow, error) {
|
||||
func (db *TimelineDB) closestEvents(t int64, n int) ([]EventsRow, error) {
|
||||
query := `
|
||||
select description, timestamp, yearknown, monthknown, dayknown, hourknown, minuteknown, secondknown
|
||||
from events
|
||||
where timestamp between ? and ?
|
||||
where timestamp is not null
|
||||
order by abs(? - timestamp) asc
|
||||
limit ?
|
||||
`
|
||||
|
||||
rows, err := db.Query(query, llimit, ulimit, t, n)
|
||||
events := make([]EventsRow, n)
|
||||
stmt, err := db.Prepare(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return events, err
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
rows, err := stmt.Query(t, n)
|
||||
if err != nil {
|
||||
return events, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
events := make([]EventsRow, n)
|
||||
i := 0
|
||||
for ; rows.Next(); i++ {
|
||||
var event EventsRow
|
||||
@@ -66,33 +70,36 @@ func (db *TimelineDB) closestEvents(t int64, n uint, llimit int64, ulimit int64)
|
||||
}
|
||||
|
||||
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")
|
||||
timestamp, err := event.timestamp.Value()
|
||||
if err != nil {
|
||||
return 0, "", "", err
|
||||
}
|
||||
|
||||
timestamp := event.timestamp.Int64
|
||||
desc := event.description.String
|
||||
yearknown := event.yearknown.Valid && event.yearknown.Bool
|
||||
desc, err := event.description.Value()
|
||||
if err != nil {
|
||||
return 0, "", "", err
|
||||
}
|
||||
|
||||
yearknown, err := event.yearknown.Value()
|
||||
if err != nil {
|
||||
return 0, "", "", err
|
||||
}
|
||||
|
||||
var ago string
|
||||
date := time.Unix(timestamp, 0)
|
||||
if !yearknown {
|
||||
date := time.Unix(timestamp.(int64), 0)
|
||||
if yearknown == nil || !yearknown.(bool) {
|
||||
yeardiff := time.Now().Year() - date.Year()
|
||||
ago = strconv.Itoa(yeardiff) + " years ago"
|
||||
} else {
|
||||
ago = date.String()
|
||||
}
|
||||
|
||||
return timestamp, ago, desc, nil
|
||||
return timestamp.(int64), ago, desc.(string), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
percent := flag.Float64("p", -1, "percentage")
|
||||
nevents := flag.Uint("n", 1, "number of events")
|
||||
after := flag.Bool("a", false, "only return events occurring after computed timestamp")
|
||||
before := flag.Bool("b", false, "only return events occurring before computed timestamp")
|
||||
nevents := flag.Int("n", 1, "number of events")
|
||||
flag.Parse()
|
||||
|
||||
if *percent < 0 || *percent > 1 {
|
||||
@@ -101,30 +108,15 @@ func main() {
|
||||
|
||||
t := time.Now().Unix() - formula(*percent)
|
||||
|
||||
llimit := int64(math.MinInt64)
|
||||
ulimit := int64(math.MaxInt64)
|
||||
if *after && *before {
|
||||
log.Fatal("cannot pass both -a and -b")
|
||||
} else if *after {
|
||||
llimit = t
|
||||
} else if *before {
|
||||
ulimit = t
|
||||
}
|
||||
|
||||
dbpath := os.Getenv("XBIT_DB")
|
||||
if dbpath == "" {
|
||||
log.Fatal("XBIT_DB not set")
|
||||
}
|
||||
|
||||
var db TimelineDB
|
||||
var err error
|
||||
db.DB, err = sql.Open("sqlite3", dbpath)
|
||||
db.DB, err = sql.Open("sqlite3", "./timeline.db")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
events, err := db.closestEvents(t, *nevents, llimit, ulimit)
|
||||
events, err := db.closestEvents(t, *nevents)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user