3
\$\begingroup\$

I am trying to retrieve the series, seasons and episodes from the movie database, however I am pretty new to Ruby on rails and I feel like what I am doing could be improved a lot.

I use an ActiveJob to retrieve all the data, and call the job in the after_initialize initializer.

Here is the code for the job:

# app/jobs/get_series_job.rb
# frozen_string_literal: true

require "httparty"

class GetSeriesJob < ApplicationJob
  queue_as :default

  API_URL = "https://api.themoviedb.org/3/".freeze
  LANGS   = { "fr" => "&language=fr-FR", "en" => "&language=en-EN" }.freeze

  def perform
    save_genres_series_seasons_episodes
  end

  private

    def get_genres
      genres_array = []
      LANGS.each do |lang|
        json = HTTParty.get(
          API_URL +
          "genre/tv/list?api_key=" +
          ENV["API_KEY"] +
          lang[1]
        )
        genres = JSON.parse(json.body)["genres"]
        genres.each do |genre|
          genre["lang"] = lang[0]
        end
        genres_array += genres
      end
      genres_array
    end

    def save_genre(genre)
      return unless !Genre.
      where(external_id: genre["id"], lang: genre["lang"])
      .exists?
      Genre
      .new(
        name: genre["name"],
        external_id: genre["id"],
        lang: genre["lang"]
      ).save
    end

    def get_series
      series = []
      LANGS.each do |lang|
        json = HTTParty.get(
          API_URL +
          "tv/popular?api_key=" +
          ENV["API_KEY"] +
          lang[1]
        )
        tmp = JSON.parse(json.body)["results"]
        tmp.each do |t|
          if (serie = series.detect { |s| s["id"] == t["id"] })
            serie["name_" + lang[0]] = t["name"]
            serie["overview_" + lang[0]] = t["overview"]
            serie.delete("name")
            serie.delete("overview")
          else
            t["name_" + lang[0]] =  t["name"]
            t["overview_" + lang[0]] = t["overview"]
          end
        end
        series = tmp if series.count.zero?
      end
      series
    end

    def get_serie(id)
      serie_info = HTTParty.get(
        API_URL +
        "tv/" +
        id.to_s +
        "?api_key=" +
        ENV["API_KEY"]
      )
      JSON.parse(serie_info.body)
    end

    def save_serie_genre(serie, genre_ids)
      genre_ids.each do |genre_id|
        SeriesGenreFr.new(
          serie: serie,
          genre: Genre.where(external_id: genre_id, lang: "fr").first
        ).save
        SeriesGenreEn.new(
          serie: serie,
          genre: Genre.where(external_id: genre_id, lang: "en").first
        ).save
      end
    end

    def save_serie(serie, serie_info)
      return unless !Serie.exists?(external_id: serie["id"])
      new_serie = Serie.new(
        original_name:     serie["original_name"],
        popularity:        serie["popularity"],
        origin_country:    serie["origin_country"],
        vote_count:        serie["vote_count"],
        first_air_date:    serie["first_air_date"],
        backdrop_path:     serie["backdrop_path"],
        original_language: serie["original_language"],
        external_id:       serie["id"],
        vote_average:      serie["vote_average"],
        overview_en:       serie["overview_en"],
        overview_fr:       serie["overview_fr"],
        poster_path:       serie["poster_path"],
        name_en:           serie["name_en"],
        name_fr:           serie["name_fr"],
        episode_run_time:  serie_info["episode_run_time"],
        number_of_seasons: serie_info["number_of_seasons"]
      )
      new_serie.save
      save_serie_genre(new_serie, serie["genre_ids"])
      new_serie.id
    end

    def get_season(serie_id, season_number)
      season = nil
      LANGS.each do |lang|
        json = HTTParty.get(
          API_URL +
          "tv/" +
          serie_id.to_s +
          "/season/" +
          season_number.to_s +
          "?api_key=" +
          ENV["API_KEY"] +
          lang[1]
        )
        tmp = JSON.parse(json.body)
        if !tmp["episodes"].nil?
          tmp["episodes"].each do |episode|
            episode["name_" + lang[0]] = episode["name"]
            episode["overview_" + lang[0]] = episode["overview"]
          end
        end
        if season.nil?
          season = tmp
          season["name_" + lang[0]] = tmp["name"]
          season["overview_" + lang[0]] = tmp["overview"]
        else
          season["name_" + lang[0]] = tmp["name"]
          season["overview_" + lang[0]] = tmp["overview"]
          if !tmp["episodes"].nil?
            tmp["episodes"].each do |e|
              if (episode = season["episodes"].detect { |ep| ep["id"] == e["id"] })
                episode["name_" + lang[0]] = e["name"]
                episode["overview_" + lang[0]] = e["overview"]
              end
            end
          end
        end
      end
      season.delete("name")
      season.delete("overview")
      season
    end

    def save_season(season, serie_id)
      return unless !Season.exists?(external_id: season["_id"])
      new_season = Season.new(
        air_date:      season["air_date"],
        name_en:       season["name_en"],
        name_fr:       season["name_fr"],
        episode_count: season["episodes"] ? season["episodes"].count : 0,
        overview_en:   season["overview_en"],
        overview_fr:   season["overview_fr"],
        external_id:   season["_id"],
        poster_path:   season["poster_path"],
        season_number: season["season_number"],
        serie_id:      serie_id
      )
      new_season.save
      new_season.id
    end

    def save_genres_series_seasons_episodes
      genres = get_genres
      genres.each do |genre|
        save_genre(genre)
      end
      episodes = []
      episode_columns = [
        :air_date,
        :name_en,
        :name_fr,
        :overview_en,
        :overview_fr,
        :external_id,
        :still_path,
        :vote_average,
        :episode_number,
        :season_id,
        :vote_count
      ]
      series = get_series
      series.each do |serie|
        # Get serie
        serie_info = get_serie(serie["id"].to_s)
        serie_id = save_serie(serie, serie_info)
        nb_season = serie_info["number_of_seasons"]
        until nb_season.zero?
          # Get season
          season = get_season(serie["id"], nb_season)
          season_id = save_season(season, serie_id)
          # Get episode
          if season["episodes"]
            season["episodes"].each do |episode|
              episode = Episode.new(
                air_date:       episode["air_date"],
                name_en:        episode["name_en"],
                name_fr:        episode["name_fr"],
                overview_en:    episode["overview_en"],
                overview_fr:    episode["overview_fr"],
                external_id:    episode["id"],
                still_path:     episode["still_path"],
                vote_average:   episode["vote_average"],
                vote_count:     episode["vote_count"],
                episode_number: episode["episode_number"],
                season_id:      season_id
              )
              episodes.push episode
            end
          end
          nb_season -= 1
        end
      end
      Episode.import episode_columns, episodes, validate: true
    end
end

Here are the migrations:

# db/migrate/20180612124443_create_genres.rb
class CreateGenres < ActiveRecord::Migration[5.2]
  def change
    create_table :genres do |t|
      t.integer :external_id
      t.string :name
      t.string :lang

      t.timestamps
    end
  end
end

# db/migrate/20180612125317_create_series.rb
class CreateSeries < ActiveRecord::Migration[5.2]
  def change
    create_table :series do |t|
      t.string :original_name
      t.string :name_en
      t.string :name_fr
      t.string :popularity
      t.string :origin_country
      t.integer :vote_count
      t.date :first_air_date
      t.string :backdrop_path
      t.string :original_language
      t.float :vote_average
      t.string :overview_en
      t.string :overview_fr
      t.string :poster_path
      t.string :episode_run_time
      t.integer :number_of_seasons
      t.integer :external_id

      t.timestamps
    end
  end
end


# db/migrate/20180612142042_create_seasons.rb
class CreateSeasons < ActiveRecord::Migration[5.2]
  def change
    create_table :seasons do |t|
      t.date :air_date
      t.integer :episode_count
      t.string :name_en
      t.string :name_fr
      t.string :overview_en
      t.string :overview_fr
      t.integer :season_number
      t.string :poster_path
      t.references :serie, foreign_key: true
      t.string :external_id

      t.timestamps
    end
  end
end


# db/migrate/20180612143220_create_episodes.rb
class CreateEpisodes < ActiveRecord::Migration[5.2]
  def change
    create_table :episodes do |t|
      t.date :air_date
      t.string :name_en
      t.string :name_fr
      t.string :overview_en
      t.string :overview_fr
      t.string :external_id
      t.string :still_path
      t.float :vote_average
      t.integer :episode_number
      t.references :season, foreign_key: true
      t.integer :vote_count

      t.timestamps
    end
  end
end

# db/migrate/20180629104316_create_series_genre_frs.rb
class CreateSeriesGenreFrs < ActiveRecord::Migration[5.2]
  def change
    create_table :series_genre_frs do |t|
      t.references :genre, foreign_key: true
      t.references :serie, foreign_key: true

      t.timestamps
    end
  end
end

# db/migrate/20180629104823_create_series_genre_ens.rb
class CreateSeriesGenreEns < ActiveRecord::Migration[5.2]
  def change
    create_table :series_genre_ens do |t|
      t.references :genre, foreign_key: true
      t.references :serie, foreign_key: true

      t.timestamps
    end
  end
end

Here is the code for the initializer:

# config/initializers/after_initialize.rb
# frozen_string_literal: true

GetSeriesJob.perform_later

The initializer is only for development use. In production I would use a crontab to run the job every day (with the whenever gem).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.