Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
Daily.scala 5.91 KiB
package models

import repositories.DailyRepository

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.{Future, Await}
import scala.concurrent.duration._

import org.bson.types.ObjectId
import java.util.Date
import java.time.Instant
import java.text.SimpleDateFormat

import play.api.libs.json.{Json, JsValue, JsString, JsObject, JsArray}

import org.bson.{BsonWriter, BsonReader, BsonType}
import org.bson.codecs.{Codec, EncoderContext, DecoderContext}
import org.bson.conversions.Bson
import org.mongodb.scala.model.Updates

case class Daily(
    id: Option[ObjectId],
    userId: ObjectId,
    questionId: ObjectId,
    content: String,
    usersLiked: Seq[ObjectId],
    createdAt: Date,
    updatedAt: Date
)

object Daily {
    val dailyRepo = new DailyRepository()

    def createDailyAsync(
        userId: ObjectId,
        questionId: ObjectId,
        content: String,
        timeout: Int = 4
    ): Daily = {
        val now: Date = Date.from(Instant.now())
        val daily: Daily = Daily(None, userId, questionId, content, Seq.empty[ObjectId], now, now)
        val future: Future[Daily] = dailyRepo.insertDaily(daily)
        Await.result[Daily](future, timeout.seconds)
    }

    def getAllDailiesAsync(timeout: Int = 4): Seq[Daily] = {
        val future: Future[Seq[Daily]] = dailyRepo.getAllDailies()
        Await.result[Seq[Daily]](future, timeout.seconds)
    }

    def getUserDailiesAsync(userId: ObjectId, timeout: Int = 4): Seq[Daily] = {
        val future: Future[Seq[Daily]] = dailyRepo.getUserDailies(userId)
        Await.result[Seq[Daily]](future, timeout.seconds)
    }

    def getUserFeedAsync(userId: ObjectId, timeout: Int = 4): Seq[Daily] = {
        // Sequentially waits for Future objects to complete before calling next method
        val result: Future[Seq[Daily]] = for {
            friends: Seq[ObjectId] <- User.getUserFriends(userId)
            feed: Seq[Daily] <- dailyRepo.getUserDailies(friends)
        } yield feed

        Await.result[Seq[Daily]](result, timeout.seconds)
    }

    def likeAsync(dailyId: ObjectId, userId: ObjectId, timeout: Int = 4): Unit = {
        val result: Future[Unit] = for {
            daily: Daily <- dailyRepo.getDaily(dailyId)
            unit: Unit <- dailyRepo.like(daily, userId)
        } yield unit

        Await.result[Unit](result, timeout.seconds)
    }

    // Convert from Daily object to JSON (serializing to JSON)
    def toJson(daily: Daily): JsValue = {
        val usersLikedAsJsStrings: Seq[JsString] = daily.usersLiked.map[JsString](id => JsString(id.toString()))

        val dateFormat: SimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
        val formattedCreatedAt: String = dateFormat.format(daily.createdAt)
        val formattedUpdatedAt: String = dateFormat.format(daily.updatedAt)

        val dailyJson = Seq(
            "id" -> JsString(daily.id.getOrElse("").toString()),
            "userId" -> JsString(daily.userId.toString()),
            "questionId" -> JsString(daily.questionId.toString()),
            "content" -> JsString(daily.content),
            "usersLiked" -> JsArray(usersLikedAsJsStrings),
            "createdAt" -> JsString(formattedCreatedAt),
            "updatedAt" -> JsString(formattedUpdatedAt)
        )
        
        Json.toJson[JsObject](JsObject(dailyJson))
    }

    // Convert from Daily set to JSON (serializing to JSON)
    def toJson(dailies: Seq[Daily]): JsValue = {
        val dailiesJson: Seq[JsValue] = dailies.map(daily => Daily.toJson(daily))

        Json.toJson[JsArray](JsArray(dailiesJson))
    }

    def toString(daily: Daily): String =
        return s"Daily(${daily.id.toString()}, ${daily.userId.toString()}, ${daily.questionId.toString()}, ${daily.content}, ${daily.usersLiked.toString()})"

    // Codec instance for serialising/deserialising type User to or from BSON.
    // Implicit keyword lets Scala compiler automatically insert this into the code where it's needed.
    implicit val codec: Codec[Daily] = new Codec[Daily] {
        override def encode(writer: BsonWriter, value: Daily, encoderContext: EncoderContext): Unit = {
            writer.writeStartDocument()
            writer.writeObjectId("user_id", value.userId)
            writer.writeObjectId("question_id", value.questionId)
            writer.writeString("content", value.content)
            writer.writeStartArray("usersLiked")
            value.usersLiked.foreach(writer.writeObjectId)
            writer.writeEndArray()
            writer.writeDateTime("createdAt", value.createdAt.getTime())
            writer.writeDateTime("updatedAt", value.updatedAt.getTime())
            writer.writeEndDocument()
        }

        override def decode(reader: BsonReader, decoderContext: DecoderContext): Daily = {
            reader.readStartDocument()
            println("hi")
            val id = reader.readObjectId("_id")
            println(id)
            val userId = reader.readObjectId("user_id")
            val questionId = reader.readObjectId("question_id")
            val content = reader.readString("content")
            val usersLiked = {
                reader.readName("usersLiked")
                reader.readStartArray()
                val buffer = collection.mutable.Buffer.empty[ObjectId]
                while (reader.readBsonType() != BsonType.END_OF_DOCUMENT) {
                    buffer += reader.readObjectId()
                }
                reader.readEndArray()
                buffer.toSeq
            }
            val createdAt = reader.readDateTime("createdAt")
            val updatedAt = reader.readDateTime("updatedAt")
            reader.readEndDocument()

            val createdAtDate: Date = Date.from(Instant.ofEpochMilli(createdAt))
            val updatedAtDate: Date = Date.from(Instant.ofEpochMilli(updatedAt))

            Daily(Some(id), userId, questionId, content, usersLiked, createdAtDate, updatedAtDate)
        }

        override def getEncoderClass: Class[Daily] = classOf[Daily]
    }
}