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 } // so we got ErrNotFound so GetDetails from a detailer 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 }