Add feed command

This commit is contained in:
Tim Van Baak 2025-01-17 07:05:57 -08:00
parent 6bd9449baf
commit c040f97680
4 changed files with 150 additions and 22 deletions

101
cmd/feed.go Normal file
View File

@ -0,0 +1,101 @@
package cmd
import (
"database/sql"
"encoding/json"
"fmt"
"log"
"github.com/Jaculabilis/intake/core"
"github.com/spf13/cobra"
)
var feedCmd = &cobra.Command{
Use: "feed",
Short: "Print item feeds",
Long: `Display the intake item feed in various formats.
The default format is "headlines".
Available formats:
headlines Only item titles
json Full item JSON
short Item source and id
`,
Run: func(cmd *cobra.Command, args []string) {
feed()
},
}
var feedFormat string
var feedSource string
var feedChannel string
func init() {
rootCmd.AddCommand(feedCmd)
feedCmd.Flags().StringVarP(&feedFormat, "format", "f", "headlines", "Feed format")
feedCmd.Flags().StringVarP(&feedSource, "source", "s", "", "Limit to items from source")
feedCmd.Flags().StringVarP(&feedChannel, "channel", "c", "", "Limit to items from channel")
feedCmd.MarkFlagsMutuallyExclusive("source", "channel")
}
func feed() {
var formatter func(core.Item)
switch feedFormat {
case "headlines":
formatter = formatHeadline
case "json":
formatter = formatJson
case "short":
formatter = formatShort
default:
log.Fatalf("error: invalid format %s", feedFormat)
}
db, err := sql.Open("sqlite3", getDbPath())
if err != nil {
log.Fatalf("error: failed to open %s", dbPath)
}
core.InitDatabase(db)
core.MigrateDatabase(db)
var items []core.Item
if feedSource != "" {
items, err = core.GetActiveItemsForSource(db, feedSource)
if err != nil {
log.Fatalf("error: failed to fetch items from %s", feedSource)
}
} else if feedChannel != "" {
log.Fatal("error: unimplemented")
} else {
items, err = core.GetAllActiveItems(db)
if err != nil {
log.Fatal("error: failed to fetch items")
}
}
for _, item := range items {
formatter(item)
}
}
func formatHeadline(item core.Item) {
title := item.Title
if title == "" {
title = item.Id
}
fmt.Println(title)
}
func formatJson(item core.Item) {
data, err := json.Marshal(item)
if err != nil {
log.Fatalf("error: failed to serialize %s/%s: %v", item.Source, item.Id, err)
}
fmt.Println(string(data))
}
func formatShort(item core.Item) {
fmt.Printf("%s/%s\n", item.Source, item.Id)
}

View File

@ -27,7 +27,7 @@ func init() {
rootCmd.SetHelpCommand(&cobra.Command{Hidden: true})
// All commands need to operate on a database
rootCmd.PersistentFlags().StringVarP(&dbPath, "db", "d", "", "Path to the intake sqlite database")
rootCmd.PersistentFlags().StringVarP(&dbPath, "db", "d", "", "Path to the intake sqlite database (default: INTAKE_DB)")
}
func getDbPath() string {

View File

@ -11,15 +11,15 @@ import (
)
type Item struct {
source string
id string
created int
active bool
title string
author string
body string
link string
time int
Source string `json:"source"`
Id string `json:"id"`
Created int `json:"created"`
Active bool `json:"active"`
Title string `json:"title"`
Author string `json:"author"`
Body string `json:"body"`
Link string `json:"link"`
Time int `json:"time"`
}
//go:embed sql/*.sql
@ -169,7 +169,34 @@ func DeactivateItem(db *sql.DB, source string, id string) (bool, error) {
return active, nil
}
func GetActiveItems(db *sql.DB, source string) ([]Item, error) {
func GetAllActiveItems(db *sql.DB) ([]Item, error) {
rows, err := db.Query(`
select
source,
id,
created,
active,
title,
author,
body,
link,
time
from items
where active <> 0
`)
if err != nil {
return nil, err
}
var items []Item
for rows.Next() {
var item Item
rows.Scan(&item.Source, &item.Id, &item.Created, &item.Active, &item.Title, &item.Author, &item.Body, &item.Link, &item.Time)
items = append(items, item)
}
return items, nil
}
func GetActiveItemsForSource(db *sql.DB, source string) ([]Item, error) {
rows, err := db.Query(`
select
source,
@ -192,7 +219,7 @@ func GetActiveItems(db *sql.DB, source string) ([]Item, error) {
var items []Item
for rows.Next() {
var item Item
rows.Scan(&item.source, &item.id, &item.created, &item.active, &item.title, &item.author, &item.body, &item.link, &item.time)
rows.Scan(&item.Source, &item.Id, &item.Created, &item.Active, &item.Title, &item.Author, &item.Body, &item.Link, &item.Time)
items = append(items, item)
}
return items, nil

View File

@ -98,14 +98,14 @@ func TestCreateSource(t *testing.T) {
func AssertItemIs(t *testing.T, item Item, expected string) {
actual := fmt.Sprintf(
"%s/%s/%t/%s/%s/%s/%s/%d",
item.source,
item.id,
item.active,
item.title,
item.author,
item.body,
item.link,
item.time,
item.Source,
item.Id,
item.Active,
item.Title,
item.Author,
item.Body,
item.Link,
item.Time,
)
if actual != expected {
t.Fatalf("expected %s, got %s", expected, actual)
@ -125,7 +125,7 @@ func TestAddItem(t *testing.T) {
if err := AddItem(db, "test", "two", "title", "author", "body", "link", 123456); err != nil {
t.Fatal(err)
}
items, err := GetActiveItems(db, "test")
items, err := GetActiveItemsForSource(db, "test")
if err != nil {
t.Fatal(err)
}
@ -138,7 +138,7 @@ func TestAddItem(t *testing.T) {
if _, err = DeactivateItem(db, "test", "one"); err != nil {
t.Fatal(err)
}
items, err = GetActiveItems(db, "test")
items, err = GetActiveItemsForSource(db, "test")
if err != nil {
t.Fatal(err)
}