Skip to content
Snippets Groups Projects
Verified Commit 09643888 authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Implement basic data retrieval and querying

parent 2f5ba167
No related branches found
No related tags found
No related merge requests found
...@@ -277,6 +277,12 @@ version = "0.15.0" ...@@ -277,6 +277,12 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]] [[package]]
name = "env_logger" name = "env_logger"
version = "0.7.1" version = "0.7.1"
...@@ -601,6 +607,15 @@ dependencies = [ ...@@ -601,6 +607,15 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "itertools"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.6" version = "0.4.6"
...@@ -684,6 +699,7 @@ dependencies = [ ...@@ -684,6 +699,7 @@ dependencies = [
"dotenv", "dotenv",
"env_logger", "env_logger",
"futures", "futures",
"itertools",
"rocket", "rocket",
"rocket_contrib", "rocket_contrib",
"serde", "serde",
......
...@@ -9,6 +9,7 @@ anyhow = "1.0.32" ...@@ -9,6 +9,7 @@ anyhow = "1.0.32"
dotenv = "0.15.0" dotenv = "0.15.0"
env_logger = "0.7.1" env_logger = "0.7.1"
futures = "0.3.5" futures = "0.3.5"
itertools = "0.9.0"
rocket = "0.4.5" rocket = "0.4.5"
serde_json = "1.0.57" serde_json = "1.0.57"
......
use serde::{Deserialize, Serialize};
use crate::models::{Genre, Person, Title, TitleCast, TitleDescription, TitleName, TitleImage, TitleMedium, TitleRating, TitleSubtitle};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct TitleDto {
pub ids: TitleIdDto,
pub original_language: Option<String>,
pub runtime: Option<i32>,
pub year_start: Option<i32>,
pub year_end: Option<i32>,
pub titles: Vec<TitleNameDto>, //
pub descriptions: Vec<TitleDescriptionDto>,
pub cast: Vec<TitleCastDto>,
pub genres: Vec<GenreDto>, //
pub ratings: Vec<TitleRatingDto>, //
pub images: Vec<TitleImageDto>,
pub media: Vec<TitleMediaDto>,
pub subtitles: Vec<TitleSubtitleDto>,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
impl TitleDto {
pub fn of(
src: Title,
titles: Vec<TitleNameDto>,
descriptions: Vec<TitleDescriptionDto>,
cast: Vec<TitleCastDto>,
genres: Vec<GenreDto>,
ratings: Vec<TitleRatingDto>,
images: Vec<TitleImageDto>,
media: Vec<TitleMediaDto>,
subtitles: Vec<TitleSubtitleDto>
) -> Self {
TitleDto {
ids: TitleIdDto {
uuid: src.id,
imdb: src.imdb_id,
tmdb: src.tmdb_id,
tvdb: src.tvdb_id,
},
original_language: src.original_language,
runtime: src.runtime,
year_start: src.year_start,
year_end: src.year_end,
titles,
descriptions,
cast,
genres,
ratings,
images,
media,
subtitles,
created_at: src.created_at,
updated_at: src.updated_at,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct TitleIdDto {
pub uuid: uuid::Uuid,
pub imdb: Option<String>,
pub tmdb: Option<i32>,
pub tvdb: Option<i32>,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct TitleNameDto {
pub region: Option<String>,
pub languages: Vec<String>,
pub kind: String,
pub name: String,
}
impl From<TitleName> for TitleNameDto {
fn from(src: TitleName) -> Self {
TitleNameDto {
region: src.region,
languages: src.languages,
kind: src.kind,
name: src.name,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct TitleDescriptionDto {
pub region: Option<String>,
pub languages: Vec<String>,
pub kind: String,
pub overview: String,
pub tagline: Option<String>,
}
impl From<TitleDescription> for TitleDescriptionDto {
fn from(src: TitleDescription) -> Self {
TitleDescriptionDto {
region: src.region,
languages: src.languages,
kind: src.kind,
overview: src.overview,
tagline: src.tagline,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct GenreDto {
pub id: uuid::Uuid,
pub tmdb_id: Option<i32>,
pub name: String,
}
impl From<Genre> for GenreDto {
fn from(src: Genre) -> Self {
GenreDto {
id: src.id,
tmdb_id: src.tmdb_id,
name: src.name,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct GenreWithTitlesDto {
pub genre: GenreDto,
pub titles: Vec<TitleDto>,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct TitleCastDto {
pub category: Option<String>,
pub characters: Vec<String>,
pub credit: Option<String>,
pub person: PersonDto,
}
impl TitleCastDto {
pub fn of(cast: TitleCast, person: Person) -> Self {
TitleCastDto {
category: cast.category,
characters: cast.characters,
credit: cast.credit,
person: PersonDto::from(person),
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct PersonDto {
pub id: uuid::Uuid,
pub imdb_id: Option<String>,
pub name: String,
}
impl From<Person> for PersonDto {
fn from(src: Person) -> Self {
PersonDto {
id: src.id,
imdb_id: src.imdb_id,
name: src.name,
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct TitleEpisodeDto {
pub show_id: uuid::Uuid,
pub episode_id: uuid::Uuid,
pub season_number: Option<String>,
pub episode_number: Option<String>,
pub air_date: Option<chrono::NaiveDate>,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct TitleImageDto {
pub kind: String,
pub mime: String,
pub src: String,
}
impl From<TitleImage> for TitleImageDto {
fn from(src: TitleImage) -> Self {
TitleImageDto {
kind: src.kind,
mime: src.mime,
src: src.src
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct TitleMediaDto {
pub mime: String,
pub codecs: Vec<String>,
pub languages: Vec<String>,
pub src: String,
}
impl From<TitleMedium> for TitleMediaDto {
fn from(src: TitleMedium) -> Self {
TitleMediaDto {
mime: src.mime,
codecs: src.codecs,
languages: src.languages,
src: src.src
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct TitleRatingDto {
pub region: Option<String>,
pub certification: String,
}
impl From<TitleRating> for TitleRatingDto {
fn from(src: TitleRating) -> Self {
TitleRatingDto {
region: src.region,
certification: src.certification
}
}
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct TitleSubtitleDto {
pub format: String,
pub language: Option<String>,
pub region: Option<String>,
pub specifier: Option<String>,
pub src: String,
}
impl From<TitleSubtitle> for TitleSubtitleDto {
fn from(src: TitleSubtitle) -> Self {
TitleSubtitleDto {
format: src.format,
language: src.language,
region: src.region,
specifier: src.specifier,
src: src.src
}
}
}
\ No newline at end of file
...@@ -4,3 +4,4 @@ extern crate dotenv; ...@@ -4,3 +4,4 @@ extern crate dotenv;
pub mod schema; pub mod schema;
pub mod models; pub mod models;
pub mod dto;
\ No newline at end of file
...@@ -14,6 +14,7 @@ use rocket_contrib::databases::diesel; ...@@ -14,6 +14,7 @@ use rocket_contrib::databases::diesel;
use rocket_contrib::json::Json; use rocket_contrib::json::Json;
use uuid::Uuid; use uuid::Uuid;
use media_backend::dto::{GenreDto, GenreWithTitlesDto, TitleCastDto, TitleDescriptionDto, TitleDto, TitleImageDto, TitleMediaDto, TitleNameDto, TitleRatingDto, TitleSubtitleDto};
use media_backend::models::*; use media_backend::models::*;
#[database("mediaflix")] #[database("mediaflix")]
...@@ -38,25 +39,88 @@ impl FromParam<'_> for ParamUuid { ...@@ -38,25 +39,88 @@ impl FromParam<'_> for ParamUuid {
} }
#[get("/api/v1/genres/<genre>")] #[get("/api/v1/genres/<id>")]
fn get_genre(db: MediaflixConnection, genre: ParamUuid) -> QueryResult<Json<Vec<((Title, Vec<TitleName>), Vec<TitleRating>)>>> { fn get_genre(db: MediaflixConnection, id: ParamUuid) -> QueryResult<Json<GenreWithTitlesDto>> {
use media_backend::schema::*; use media_backend::schema::*;
let titles = title_genres::table let genre: Genre = genres::table
.filter(title_genres::genre_id.eq(genre.0)) .find(id.0)
.first::<Genre>(&db.0)?;
let titles: Vec<Title> = title_genres::table
.filter(title_genres::genre_id.eq(genre.id))
.inner_join(titles::table) .inner_join(titles::table)
.select(titles::all_columns) .select(titles::all_columns)
.load::<Title>(&db.0)?; .load::<Title>(&db.0)?;
let title_akas = TitleName::belonging_to(&titles) let title_names: Vec<Vec<TitleName>> = TitleName::belonging_to(&titles)
.load::<TitleName>(&db.0)? .load::<TitleName>(&db.0)?
.grouped_by(&titles); .grouped_by(&titles);
let title_ratings = TitleRating::belonging_to(&titles) let title_descriptions: Vec<Vec<TitleDescription>> = TitleDescription::belonging_to(&titles)
.load::<TitleDescription>(&db.0)?
.grouped_by(&titles);
let title_cast: Vec<Vec<(TitleCast, Person)>> = TitleCast::belonging_to(&titles)
.inner_join(people::table)
.load::<(TitleCast, Person)>(&db.0)?
.grouped_by(&titles);
let title_genres: Vec<Vec<(TitleGenre, Genre)>> = TitleGenre::belonging_to(&titles)
.inner_join(genres::table)
.load::<(TitleGenre, Genre)>(&db.0)?
.grouped_by(&titles);
let title_ratings: Vec<Vec<TitleRating>> = TitleRating::belonging_to(&titles)
.load::<TitleRating>(&db.0)? .load::<TitleRating>(&db.0)?
.grouped_by(&titles); .grouped_by(&titles);
let data = titles.into_iter() let title_images: Vec<Vec<TitleImage>> = TitleImage::belonging_to(&titles)
.zip(title_akas) .load::<TitleImage>(&db.0)?
.grouped_by(&titles);
let title_media: Vec<Vec<TitleMedium>> = TitleMedium::belonging_to(&titles)
.load::<TitleMedium>(&db.0)?
.grouped_by(&titles);
let title_subtitles: Vec<Vec<TitleSubtitle>> = TitleSubtitle::belonging_to(&titles)
.load::<TitleSubtitle>(&db.0)?
.grouped_by(&titles);
Ok(Json(GenreWithTitlesDto {
genre: GenreDto::from(genre),
titles: titles.into_iter()
.zip(title_names)
.zip(title_descriptions)
.zip(title_cast)
.zip(title_genres)
.zip(title_ratings) .zip(title_ratings)
.collect::<Vec<_>>(); .zip(title_images)
Ok(Json(data)) .zip(title_media)
.zip(title_subtitles)
.map(|tuple| {
let ((((((((title, akas), descriptions),
cast), genres), ratings),
images), media), subtitles) = tuple;
TitleDto::of(
title,
akas.into_iter().map(|src| {
TitleNameDto::from(src)
}).collect::<Vec<_>>(),
descriptions.into_iter().map(|src| {
TitleDescriptionDto::from(src)
}).collect::<Vec<_>>(),
cast.into_iter().map(|src| {
let (cast, person) = src;
TitleCastDto::of(cast, person)
}).collect::<Vec<_>>(),
genres.into_iter().map(|(_, src)| {
GenreDto::from(src)
}).collect::<Vec<_>>(),
ratings.into_iter().map(|src| {
TitleRatingDto::from(src)
}).collect::<Vec<_>>(),
images.into_iter().map(|src| {
TitleImageDto::from(src)
}).collect::<Vec<_>>(),
media.into_iter().map(|src| {
TitleMediaDto::from(src)
}).collect::<Vec<_>>(),
subtitles.into_iter().map(|src| {
TitleSubtitleDto::from(src)
}).collect::<Vec<_>>(),
)
}).collect::<Vec<TitleDto>>(),
}))
} }
......
...@@ -56,16 +56,17 @@ pub struct TitleDescription { ...@@ -56,16 +56,17 @@ pub struct TitleDescription {
} }
#[derive(Identifiable, Queryable, Associations, Serialize, Deserialize, PartialEq, Debug)] #[derive(Identifiable, Queryable, Associations, Serialize, Deserialize, PartialEq, Debug)]
#[belongs_to(Title, foreign_key = "parent_id")] #[belongs_to(Title, foreign_key = "show_id")]
#[table_name = "title_episodes"] #[table_name = "title_episodes"]
pub struct TitleEpisode { pub struct TitleEpisode {
pub id: uuid::Uuid, pub id: uuid::Uuid,
pub show_id: uuid::Uuid,
pub episode_id: uuid::Uuid,
pub season_number: Option<String>, pub season_number: Option<String>,
pub episode_number: Option<String>, pub episode_number: Option<String>,
pub air_date: Option<chrono::NaiveDate>, pub air_date: Option<chrono::NaiveDate>,
pub created_at: chrono::DateTime<chrono::Utc>, pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>, pub updated_at: chrono::DateTime<chrono::Utc>,
pub parent_id: uuid::Uuid,
} }
#[derive(Identifiable, Queryable, Associations, Serialize, Deserialize, PartialEq, Debug)] #[derive(Identifiable, Queryable, Associations, Serialize, Deserialize, PartialEq, Debug)]
......
...@@ -48,12 +48,13 @@ table! { ...@@ -48,12 +48,13 @@ table! {
table! { table! {
title_episodes (id) { title_episodes (id) {
id -> Uuid, id -> Uuid,
show_id -> Uuid,
episode_id -> Uuid,
season_number -> Nullable<Text>, season_number -> Nullable<Text>,
episode_number -> Nullable<Text>, episode_number -> Nullable<Text>,
air_date -> Nullable<Date>, air_date -> Nullable<Date>,
created_at -> Timestamptz, created_at -> Timestamptz,
updated_at -> Timestamptz, updated_at -> Timestamptz,
parent_id -> Uuid,
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment