canape/shows/shows.go

235 lines
5.6 KiB
Go

package shows
import (
"fmt"
"gitlab.quimbo.fr/odwrtw/canape-sql/sqly"
"gitlab.quimbo.fr/odwrtw/canape-sql/users"
"github.com/Sirupsen/logrus"
"github.com/jmoiron/sqlx"
"github.com/odwrtw/polochon/lib"
)
const (
addShowQuery = `INSERT INTO shows (imdbid, title) VALUES (:imdbid, :title) RETURNING id;`
getShowQueryByImdbID = `SELECT * FROM shows WHERE imdbid=$1;`
getShowQueryByID = `SELECT * FROM shows WHERE id=$1;`
deleteShowQuery = `DELETE FROM shows WHERE id=$1;`
addEpisodeQuery = `INSERT INTO episodes (shows_id, title, season, episode) VALUES ($1,$2,$3,$4);`
getEpisodesQuery = `SELECT title, season, episode FROM episodes WHERE shows_id=$1;`
getShowWithUserQueryByImdbID = `
SELECT
shows.id,
shows.title,
shows.imdbid,
COALESCE(shows_tracked.season,0) AS trackedseason,
COALESCE(shows_tracked.episode,0) AS trackedepisode
FROM shows LEFT JOIN shows_tracked ON shows.id=shows_tracked.shows_id AND shows_tracked.users_id=$2
WHERE shows.imdbid=$1;`
getShowWithUserQueryByID = `
SELECT
shows.id,
shows.title,
shows.imdbid,
COALESCE(shows_tracked.season,0) AS trackedseason,
COALESCE(shows_tracked.episode,0) AS trackedepisode
FROM shows LEFT JOIN shows_tracked ON shows.id=shows_tracked.shows_id AND shows_tracked.users_id=$2
WHERE shows.id=$1;`
)
var (
// NotFound error returned when show not found in database
ErrNotFound = fmt.Errorf("Not found")
)
type Show struct {
sqly.BaseTable
polochon.Show
Episodes []*Episode
TrackedSeason int
TrackedEpisode int
}
// New returns a new Show with a polochon ShowConfig
func New(conf polochon.ShowConfig) *Show {
return &Show{Show: polochon.Show{ShowConfig: conf}}
}
// Get returns show details in database from id or imdbid or an error
func (s *Show) Get(db *sqlx.DB) error {
var err error
if s.ID != "" {
err = db.QueryRowx(getShowQueryByID, s.ID).StructScan(s)
} else if s.ImdbID != "" {
err = db.QueryRowx(getShowQueryByImdbID, s.ImdbID).StructScan(s)
} else {
err = fmt.Errorf("Can't get show details, you have to specify an ID or ImdbID")
}
if err != nil {
if err.Error() == "sql: no rows in result set" {
return ErrNotFound
}
return err
}
return nil
}
// GetAsUser returns a show with user info like tracked
func (s *Show) GetAsUser(db *sqlx.DB, user *users.User) error {
var err error
if s.ID != "" {
err = db.QueryRowx(getShowWithUserQueryByID, s.ID, user.ID).StructScan(s)
} else if s.ImdbID != "" {
err = db.QueryRowx(getShowWithUserQueryByImdbID, s.ImdbID, user.ID).StructScan(s)
} else {
err = fmt.Errorf("Can't get show details, you have to specify an ID or ImdbID")
}
if err != nil {
if err.Error() == "sql: no rows in result set" {
return ErrNotFound
}
return err
}
return nil
}
// GetDetails retrieves details for the show, first try to
// get info from db, if not exists, use polochon.Detailer
// and save informations in the database for future use
func (s *Show) GetDetails(db *sqlx.DB, log *logrus.Entry) error {
var err error
err = s.Get(db)
if err == nil {
// found ok
return nil
}
if err != ErrNotFound {
// Unexpected error
return err
}
err = s.Show.GetDetails(log)
if err != nil {
return err
}
s.Episodes = []*Episode{}
for _, pe := range s.Show.Episodes {
s.Episodes = append(s.Episodes, NewEpisodeFromPolochon(pe))
}
s.Add(db)
return nil
}
// GetDetailsAsUser like GetDetails but with User context
func (s *Show) GetDetailsAsUser(db *sqlx.DB, user *users.User, log *logrus.Entry) error {
var err error
err = s.GetAsUser(db, user)
if err == nil {
// found ok
return nil
}
if err != ErrNotFound {
// Unexpected error
return err
}
err = s.Show.GetDetails(log)
if err != nil {
return err
}
s.Add(db)
s.Episodes = []*Episode{}
for _, pe := range s.Show.Episodes {
s.Episodes = append(s.Episodes, NewEpisodeFromPolochon(pe))
}
return nil
}
// IsTracked returns true if the show is tracked use this function
// after retrieve the show with GetAsUser or other *AsUser functions
func (s *Show) IsTracked() bool {
if s.TrackedSeason != 0 && s.TrackedEpisode != 0 {
return true
}
return false
}
func (s *Show) Add(db *sqlx.DB) error {
var id string
r, err := db.NamedQuery(addShowQuery, s)
if err != nil {
return err
}
for r.Next() {
r.Scan(&id)
}
s.ID = id
// When add a show to database use polochon episode details
// so s.Show.Episodes
for _, pEp := range s.Show.Episodes {
err = s.addEpisode(db, pEp)
if err != nil {
return err
}
}
return nil
}
func (s *Show) addEpisode(db *sqlx.DB, pEpisode *polochon.ShowEpisode) error {
_, err := db.Exec(addEpisodeQuery, s.ID, pEpisode.Title, pEpisode.Season, pEpisode.Episode)
if err != nil {
return err
}
return nil
}
func (s *Show) Delete(db *sqlx.DB) error {
r, err := db.Exec(deleteShowQuery, s.ID)
if err != nil {
return err
}
count, _ := r.RowsAffected()
if count != 1 {
return fmt.Errorf("Unexpected number of row deleted: %d", count)
}
return nil
}
func (s *Show) GetEpisodes(db *sqlx.DB) error {
// When retrive episode's info from database populate the s.Episodes member
// and not s.Show.Episodes
err := db.Select(&s.Episodes, getEpisodesQuery, s.ID)
if err != nil {
return err
}
return nil
}
type Episode struct {
sqly.BaseTable
polochon.ShowEpisode
}
// NewEpisodeFromPolochon returns a new episode from a polochon.ShowEpisode
func NewEpisodeFromPolochon(pe *polochon.ShowEpisode) *Episode {
e := &Episode{}
e.Title = pe.Title
e.ShowTitle = pe.ShowTitle
e.Season = pe.Season
e.Episode = pe.Episode
e.TvdbID = pe.TvdbID
e.Aired = pe.Aired
e.Plot = pe.Plot
e.Runtime = pe.Runtime
e.Rating = pe.Rating
e.ShowImdbID = pe.ShowImdbID
e.ShowTvdbID = pe.ShowTvdbID
e.EpisodeImdbID = pe.EpisodeImdbID
return e
}