Skip to content

Commit

Permalink
Added fetching comprehensive object of movie favorites and marking th…
Browse files Browse the repository at this point in the history
…em as notified instead of deletion
  • Loading branch information
diareuse committed Nov 18, 2023
1 parent c06f1ec commit 0dd4d2f
Show file tree
Hide file tree
Showing 30 changed files with 956 additions and 51 deletions.
781 changes: 781 additions & 0 deletions feature-core-database/schemas/movie.core.db.MovieDatabase/16.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import movie.core.db.model.MovieStored
import movie.core.db.model.ShowingStored

@Database(
version = 15,
version = 16,
entities = [
BookingStored::class,
BookingSeatsStored::class,
Expand Down Expand Up @@ -152,4 +152,22 @@ internal abstract class MovieDatabase : RoomDatabase() {
}
}

class Migration15to16 : Migration(15, 16) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("CREATE TABLE IF NOT EXISTS `movie_favorites_copy` (`movie` TEXT NOT NULL, `notified` INTEGER NOT NULL, `created_at` INTEGER NOT NULL, PRIMARY KEY(`movie`), FOREIGN KEY(`movie`) REFERENCES `movies`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )")
val now = System.currentTimeMillis()
db.execSQL("insert into `movie_favorites_copy` (`movie`, `notified`, `created_at`) select mf.`movie`, (m.`screening_from` < $now) as `notified`, mf.`created_at` from `movie_favorites` as mf join `movie_details` as m on m.`movie`=mf.`movie`")
db.execSQL("drop table `movie_favorites`")
db.execSQL("alter table `movie_favorites_copy` rename to `movie_favorites`")
// ---
db.execSQL("CREATE TABLE IF NOT EXISTS `movies_copy` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `url` TEXT NOT NULL, `released_at` INTEGER NOT NULL, `screening_from` INTEGER NOT NULL, `duration` INTEGER NOT NULL, PRIMARY KEY(`id`))")
db.execSQL("insert into `movies_copy` (`id`,`name`,`url`,`released_at`,`screening_from`,`duration`) select `id`, `name`, `url`, `released_at`, COALESCE((select `screening_from` from `movie_details` as md where md.`movie`= m.`id`),(select `screening_from` from `movie_previews` as mp where mp.`movie`=m.`id`),0), `duration` from `movies` as m")
db.execSQL("drop table `movies`")
db.execSQL("alter table `movies_copy` rename to `movies`")
// ---
db.execSQL("drop view `movie_reference_views`")
db.execSQL("CREATE VIEW `movie_reference_views` AS select m.id,m.name,m.url,m.released_at,m.screening_from,m.duration,mr.poster,mr.video from movies as m, movie_references as mr where m.id=mr.movie")
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import movie.core.db.model.MovieStored
@Dao
interface MovieDao : DaoBase<MovieStored> {

@Query("select duration from movies where id=:id")
suspend fun getDuration(id: String): Long?
@Query("select * from movies where id=:id")
suspend fun select(id: String): MovieStored?

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ class MovieDaoLowercase(
return origin.update(model.lowercase())
}

override suspend fun getDuration(id: String): Long? {
return origin.getDuration(id.lowercase())
override suspend fun select(id: String): MovieStored? {
return origin.select(id.lowercase())
}

// ---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class MovieDaoPerformance(
origin.update(model)
}

override suspend fun getDuration(id: String) = tracer.trace("$Tag.getDuration") {
origin.getDuration(id)
override suspend fun select(id: String) = tracer.trace("$Tag.select") {
origin.select(id)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ class MovieDaoThreading(

override suspend fun update(model: MovieStored) = origin.io { update(model) }

override suspend fun getDuration(id: String) = origin.io { getDuration(id) }
override suspend fun select(id: String) = origin.io { select(id) }

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@ package movie.core.db.dao
import androidx.room.Dao
import androidx.room.Query
import movie.core.db.model.MovieFavoriteStored
import movie.core.db.model.MoviePreviewView

@Dao
interface MovieFavoriteDao : DaoBase<MovieFavoriteStored> {

@Query("select * from movie_preview_views where exists(select 1 from movie_favorites where movie_favorites.movie=movie_preview_views.id) order by (select created_at from movie_favorites where movie_favorites.movie=movie_preview_views.id) desc")
suspend fun selectAll(): List<MoviePreviewView>
@Query("select * from movie_favorites")
suspend fun selectAll(): List<MovieFavoriteStored>

@Query("select * from movie_favorites")
suspend fun selectPending(): List<MovieFavoriteStored>

@Query("select * from movie_favorites as mf where mf.movie=:id")
suspend fun select(id: String): MovieFavoriteStored?

@Query("select exists(select 1 from movie_favorites where movie=:id)")
suspend fun isFavorite(id: String): Boolean

@Query("update movie_favorites set notified=1 where movie=:id")
suspend fun setFavorite(id: String)

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ class MovieFavoriteDaoPerformance(
origin.isFavorite(id)
}

override suspend fun selectPending() = tracer.trace("$Tag.selectPending") {
origin.selectPending()
}

override suspend fun select(id: String) = tracer.trace("$Tag.select") {
origin.select(id)
}

override suspend fun setFavorite(id: String) = tracer.trace("$Tag.setFavorite") {
origin.setFavorite(id)
}

companion object {
private const val Tag = "movie-favorite"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package movie.core.db.dao

import movie.core.db.io
import movie.core.db.model.MovieFavoriteStored
import movie.core.db.model.MoviePreviewView

class MovieFavoriteDaoThreading(
private val origin: MovieFavoriteDao
Expand All @@ -14,7 +13,13 @@ class MovieFavoriteDaoThreading(

override suspend fun update(model: MovieFavoriteStored) = origin.io { update(model) }

override suspend fun selectAll(): List<MoviePreviewView> = origin.io { selectAll() }
override suspend fun selectAll() = origin.io { selectAll() }

override suspend fun selectPending() = origin.io { selectPending() }

override suspend fun select(id: String) = origin.io { select(id) }

override suspend fun setFavorite(id: String) = origin.io { setFavorite(id) }

override suspend fun isFavorite(id: String) = origin.io { isFavorite(id) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class DatabaseModule {
.addMigrations(MovieDatabase.Migration10to11())
.addMigrations(MovieDatabase.Migration12to13())
.addMigrations(MovieDatabase.Migration14to15())
.addMigrations(MovieDatabase.Migration15to16())
.build()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ data class MovieFavoriteStored(
@PrimaryKey
@ColumnInfo(name = "movie")
val movie: String,
@ColumnInfo(name = "notified")
val notified: Boolean,
@ColumnInfo(name = "created_at")
val createdAt: Date = Date()
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import java.util.Date
import kotlin.time.Duration.Companion.milliseconds

@DatabaseView(
value = "select movies.id,movies.name,movies.url,movies.released_at,movies.duration,movie_references.poster,movie_references.video from movies, movie_references where movies.id=movie_references.movie",
value = "select m.id,m.name,m.url,m.released_at,m.screening_from,m.duration,mr.poster,mr.video from movies as m, movie_references as mr where m.id=mr.movie",
viewName = "movie_reference_views"
)
data class MovieReferenceView(
Expand All @@ -18,6 +18,8 @@ data class MovieReferenceView(
val url: String,
@ColumnInfo("released_at")
val releasedAt: Date,
@ColumnInfo("screening_from")
val screeningFrom: Date,
@ColumnInfo("duration")
val durationMillis: Long,
@ColumnInfo("poster")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ data class MovieStored(
val url: String,
@ColumnInfo("released_at")
val releasedAt: Date,
@ColumnInfo("screening_from")
val screeningFrom: Date,
@ColumnInfo("duration")
val durationMillis: Long
) {
Expand Down
10 changes: 7 additions & 3 deletions feature-core/src/main/java/movie/core/FavoriteFeature.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package movie.core

import movie.core.model.Movie
import movie.core.model.MoviePreview
import movie.core.model.MovieFavorite

interface FavoriteFeature {

suspend fun isFavorite(movie: Movie): Result<Boolean>
suspend fun toggle(movie: MoviePreview): Result<Boolean>
suspend fun getAll(): Result<List<MoviePreview>>
suspend fun toggle(movie: Movie): Result<Boolean>
suspend fun setNotified(movie: Movie): Result<Unit>

suspend fun getPending(): Result<List<MovieFavorite>>
suspend fun getAll(): Result<List<MovieFavorite>>
suspend fun get(movie: Movie): Result<MovieFavorite>

}

Original file line number Diff line number Diff line change
@@ -1,35 +1,57 @@
package movie.core

import movie.core.adapter.MoviePreviewFromDatabase
import movie.core.adapter.MovieFavoriteFromDatabase
import movie.core.adapter.MovieFromDatabase
import movie.core.db.dao.MovieDao
import movie.core.db.dao.MovieFavoriteDao
import movie.core.db.dao.MovieMediaDao
import movie.core.db.model.MovieFavoriteStored
import movie.core.model.Movie
import movie.core.model.MoviePreview
import movie.core.model.MovieFavorite
import java.util.Date

class FavoriteFeatureFromDatabase(
private val favoriteDao: MovieFavoriteDao,
private val mediaDao: MovieMediaDao
private val movieDao: MovieDao
) : FavoriteFeature {

override suspend fun isFavorite(movie: Movie): Result<Boolean> =
favoriteDao.runCatching {
isFavorite(movie.id)
override suspend fun isFavorite(movie: Movie): Result<Boolean> = favoriteDao.runCatching {
isFavorite(movie.id)
}

override suspend fun setNotified(movie: Movie): Result<Unit> {
return favoriteDao.runCatching { setFavorite(movie.id) }
}

override suspend fun toggle(movie: Movie): Result<Boolean> = favoriteDao.runCatching {
val favorite = MovieFavoriteStored(movie = movie.id, movie.releasedAt.before(Date()))
val isFavorite = isFavorite(movie).getOrThrow()
when (isFavorite) {
true -> delete(favorite)
else -> insertOrUpdate(favorite)
}
!isFavorite
}

override suspend fun toggle(movie: MoviePreview): Result<Boolean> =
favoriteDao.runCatching {
val favorite = MovieFavoriteStored(movie = movie.id)
val isFavorite = isFavorite(movie).getOrThrow()
when (isFavorite) {
true -> delete(favorite)
else -> insertOrUpdate(favorite)
}
!isFavorite
override suspend fun getPending(): Result<List<MovieFavorite>> = favoriteDao.runCatching {
selectPending().mapNotNull {
val movie = movieDao.select(it.movie)?.let(::MovieFromDatabase)
?: return@mapNotNull null
MovieFavoriteFromDatabase(it, movie)
}
}

override suspend fun getAll() = runCatching {
favoriteDao.selectAll().map { MoviePreviewFromDatabase(it, mediaDao.select(it.id)) }
favoriteDao.selectAll().mapNotNull {
val movie = movieDao.select(it.movie)?.let(::MovieFromDatabase)
?: return@mapNotNull null
MovieFavoriteFromDatabase(it, movie)
}
}

override suspend fun get(movie: Movie): Result<MovieFavorite> {
return favoriteDao.runCatching { select(movie.id).let(::requireNotNull) }.mapCatching {
val movie = movieDao.select(it.movie)?.let(::MovieFromDatabase).let(::requireNotNull)
MovieFavoriteFromDatabase(it, movie)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package movie.core

import movie.core.model.MoviePreview
import movie.core.model.Movie
import movie.core.pulse.ExactPulseNotificationMovie
import movie.pulse.ExactPulseRequest
import movie.pulse.ExactPulseScheduler
Expand All @@ -11,13 +11,13 @@ class FavoriteFeatureScheduleNotification(
) : FavoriteFeature by origin {

override suspend fun toggle(
movie: MoviePreview
movie: Movie
) = origin.toggle(movie).onSuccess { isFavorite ->
val request = ExactPulseRequest.Builder<ExactPulseNotificationMovie>()
.setDate(movie.screeningFrom)
.setData(ExactPulseNotificationMovie.getData(movie))
.build()
when (isFavorite) {
when (isFavorite && get(movie).map { !it.isNotified }.getOrDefault(false)) {
true -> scheduler.schedule(request)
else -> scheduler.cancel(request)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import movie.calendar.EventMetadata
import movie.core.db.dao.MovieDao
import movie.core.model.Booking
import movie.core.preference.EventPreference
import kotlin.time.Duration.Companion.milliseconds

class UserBookingFeatureCalendar(
private val origin: UserBookingFeature,
Expand All @@ -27,7 +26,7 @@ class UserBookingFeatureCalendar(
val calendar = preference.calendarId ?: return
val writer = writer.create(calendar)
for (booking in bookings) {
val duration = dao.getDuration(booking.movieId)?.milliseconds ?: continue
val duration = dao.select(booking.movieId)?.duration ?: continue
val metadata = EventMetadata(
name = booking.name,
start = booking.startsAt,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package movie.core.adapter

import movie.core.db.model.MovieFavoriteStored
import movie.core.model.Movie
import movie.core.model.MovieFavorite
import java.util.Date

data class MovieFavoriteFromDatabase(
private val stored: MovieFavoriteStored,
override val movie: Movie
) : MovieFavorite {
override val createdAt: Date
get() = stored.createdAt
override val isNotified: Boolean
get() = stored.notified
}
23 changes: 23 additions & 0 deletions feature-core/src/main/java/movie/core/adapter/MovieFromDatabase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package movie.core.adapter

import movie.core.db.model.MovieStored
import movie.core.model.Movie
import java.util.Date
import kotlin.time.Duration

data class MovieFromDatabase(
private val stored: MovieStored
) : Movie {
override val id: String
get() = stored.id
override val name: String
get() = stored.name
override val url: String
get() = stored.url
override val releasedAt: Date
get() = stored.releasedAt
override val screeningFrom: Date
get() = stored.screeningFrom
override val duration: Duration
get() = stored.duration
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ data class MovieFromId(
override val name: String = ""
override val url: String = ""
override val releasedAt: Date = Date(0)
override val screeningFrom: Date = Date(0)
override val duration: Duration = 0.seconds

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ data class MovieReferenceFromDatabase(
get() = movie.url
override val releasedAt: Date
get() = movie.releasedAt
override val screeningFrom: Date
get() = movie.releasedAt
override val duration: Duration
get() = movie.duration
override val posterUrl: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ internal data class MovieReferenceFromResponse(
get() = movie.url
override val releasedAt: Date
get() = movie.releasedAt
override val screeningFrom: Date
get() = movie.releasedAt
override val duration: Duration
get() = movie.duration
override val posterUrl: String
Expand Down

0 comments on commit 0dd4d2f

Please sign in to comment.