Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changement du type pour le Mandat #815

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 8 additions & 5 deletions app/controllers/ApplicationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import java.time.{LocalDate, ZonedDateTime}
import java.util.UUID

import actions._
import cats.implicits.catsSyntaxOptionId
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import cats.implicits.catsSyntaxOptionId

import cats.syntax.all._
import constants.Constants
import forms.FormsPlusMap
Expand Down Expand Up @@ -228,9 +229,9 @@ case class ApplicationController @Inject() (

val capitalizedUserName = user.name.split(' ').map(_.capitalize).mkString(" ")
if (contexts.isEmpty)
s"${capitalizedUserName} ( ${user.qualite} )"
s"$capitalizedUserName ( ${user.qualite} )"
else
s"${capitalizedUserName} ${contexts.mkString(",")}"
s"$capitalizedUserName ${contexts.mkString(",")}"
}

def createPost: Action[AnyContent] =
Expand Down Expand Up @@ -314,9 +315,11 @@ case class ApplicationController @Inject() (
applicationData.selectedSubject.contains[String](applicationData.subject),
category = applicationData.category,
files = newAttachments ++ pendingAttachments,
mandatType = DataModel.Application.MandatType
.dataModelDeserialization(applicationData.mandatType),
mandatDate = Some(applicationData.mandatDate)
mandat = (
DataModel.Application.MandatType
.dataModelDeserialization(applicationData.mandatType),
applicationData.mandatDate.some
).mapN(Application.Mandat.apply)
)
if (applicationService.createApplication(application)) {
notificationsService.newApplication(application)
Expand Down
35 changes: 35 additions & 0 deletions app/models/Answer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ package models
import java.time.ZonedDateTime
import java.util.UUID

import anorm.Column.nonNull
import anorm.{MetaDataItem, TypeDoesNotMatch}
import cats.implicits.catsSyntaxEitherId
import helper.Time
import play.api.libs.json.{Json, Reads, Writes}
import serializers.Anorm.className

import serializers.JsonFormats._

case class Answer(
id: UUID,
applicationId: UUID,
Expand All @@ -24,3 +33,29 @@ case class Answer(
}

}

object Answer {

implicit val Reads: Reads[Answer] = Json
.reads[Answer]
.map(answer =>
answer.copy(creationDate = answer.creationDate.withZoneSameInstant(Time.timeZoneParis))
)

implicit val Writes: Writes[Answer] = Json.writes[Answer]

implicit val answerListParser: anorm.Column[List[Answer]] =
nonNull { (value, meta) =>
val MetaDataItem(qualified, _, _) = meta
value match {
case json: org.postgresql.util.PGobject =>
Json.parse(json.getValue).as[List[Answer]].asRight[Nothing]
case json: String => Json.parse(json).as[List[Answer]].asRight[Nothing]
case _ =>
TypeDoesNotMatch(
s"Cannot convert $value: ${className(value)} to List[Answer] for column $qualified"
).asLeft[List[Answer]]
}
}
Comment on lines +39 to +59
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En fait avoir la logique de serialization dans le modèle ça n'a pas de sens. Ce n'est pas clair, ça mixe de la logique métier avec de la logique de serialization. Aussi juste la dépendance "modèle dépends des serializers anorm/json" ça sens pas bon.


}
Comment on lines +36 to +61
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Du coup ces modifs ne sont plus nécessaires ?

94 changes: 81 additions & 13 deletions app/models/Application.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ import java.time.ZonedDateTime
import java.time.temporal.ChronoUnit.MINUTES
import java.util.UUID

import anorm.SqlParser.{bool, get, int, str}
import anorm.{~, RowParser}
import cats.Eq
import cats.syntax.all._
import helper.BooleanHelper.not
import helper.Time
import models.Application.Mandat
import models.Application.Mandat.MandatType
import models.Authorization.{isExpert, isHelper, isInstructor, UserRights}
import serializers.Anorm.{fieldsMapLongParser, fieldsMapStringParser, fieldsMapUUIDParser}
import serializers.DataModel
import serializers.JsonFormats._

case class Application(
id: UUID,
Expand All @@ -21,18 +29,17 @@ case class Application(
invitedUsers: Map[UUID, String],
area: UUID,
irrelevant: Boolean,
answers: List[Answer] = List(),
answers: List[Answer] = List.empty[Answer],
internalId: Int = -1,
closed: Boolean = false,
seenByUserIds: List[UUID] = List(),
seenByUserIds: List[UUID] = List.empty[UUID],
usefulness: Option[String] = None,
closedDate: Option[ZonedDateTime] = None,
closedDate: Option[ZonedDateTime] = Option.empty[ZonedDateTime],
expertInvited: Boolean = false,
hasSelectedSubject: Boolean = false,
category: Option[String] = None,
files: Map[String, Long] = Map(),
mandatType: Option[Application.MandatType],
mandatDate: Option[String]
category: Option[String] = Option.empty[String],
files: Map[String, Long] = Map.empty[String, Long],
mandat: Option[Mandat]
) extends AgeModel {

lazy val filesAvailabilityLeftInDays: Option[Int] = if (ageInDays > 8) {
Expand Down Expand Up @@ -186,14 +193,75 @@ case class Application(

object Application {

sealed trait MandatType
implicit val Parser: RowParser[Application] =
(get[UUID]("id") ~
get[ZonedDateTime]("creation_date").map(_.withZoneSameInstant(Time.timeZoneParis)) ~
str(columnName = "creator_user_name") ~
get[UUID]("creator_user_id") ~
str(columnName = "subject") ~
str(columnName = "description") ~
get[Map[String, String]]("user_infos") ~
get[Map[UUID, String]]("invited_users") ~
get[UUID]("area") ~
bool(columnName = "irrelevant") ~
get[List[Answer]]("answers") ~
int("internal_id") ~
bool("closed") ~
get[List[UUID]]("seen_by_user_ids") ~
str("usefulness").? ~
get[ZonedDateTime]("closed_date").? ~
bool("expert_invited") ~
bool("has_selected_subject") ~
str("category").? ~
get[Map[String, Long]]("files") ~
Application.Mandat.Parser)
.map {
case id ~ creation ~ creatorName ~ creatorId ~ subject ~ description ~ userInfos ~ invitedUsers
~ area ~ irrelevant ~ answers ~ internalId ~ closed ~ seen ~ usefulness ~ closedDate ~ experts ~ hasSelectedSubject ~ category ~ files ~ mandat =>
Application(
id,
creation,
creatorName,
creatorId,
subject,
description,
userInfos,
invitedUsers,
area,
irrelevant,
answers,
internalId,
closed,
seen,
usefulness,
closedDate,
experts,
hasSelectedSubject,
category,
files,
mandat
)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non mais simplement définis une ApplicationRow qui matche la ligne PG, et tu fais des méthodes de conversion Application <=> ApplicationRow, c'est tellement plus simple. Et les erreurs de consistence peuvent être loggués dans la transformation avec un log.warn puisqu'en théorie si ApplicationRow => Application fail, c'est un bug de notre part, ce n'est pas de la faute de l'utilisateur.


final case class Mandat(_type: MandatType, date: String)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

J'aime pas trop le _ avant le nom, car ça signifie souvent "privé" ou "non utilisé, mais quand même utile" dans certains autres langages. Que penses-tu de type_ ? ou simplement `type` ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type_ c'est ok pour moi. A la base j'avais mis type, mais le parsing Twirl n'a pas l'air d'aimer :(


object Mandat {

implicit val Parser: RowParser[Option[Mandat]] =
(str("mandat_type").?.map(
_.flatMap(DataModel.Application.MandatType.dataModelDeserialization)
) ~ str("mandat_date").?).map { case _type ~ date => (_type, date).mapN(Mandat.apply) }

object MandatType {
case object Sms extends MandatType
case object Phone extends MandatType
case object Paper extends MandatType
sealed trait MandatType

implicit val Eq: Eq[MandatType] = (x: MandatType, y: MandatType) => x == y
object MandatType {
case object Sms extends MandatType
case object Phone extends MandatType
case object Paper extends MandatType

implicit val Eq: Eq[MandatType] = (x: MandatType, y: MandatType) => x == y

}

}

Expand Down
37 changes: 18 additions & 19 deletions app/serializers/DataModel.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package serializers

import models.Application.MandatType
import cats.implicits.catsSyntaxOptionId
import models.Application.Mandat.MandatType
import play.api.libs.json._

object DataModel {

object Application {

object MandatType {
import models.Application.MandatType._
import models.Application.Mandat.MandatType._

def dataModelSerialization(entity: MandatType): String =
entity match {
Expand All @@ -19,9 +20,9 @@ object DataModel {

def dataModelDeserialization(raw: String): Option[MandatType] =
raw match {
case "sms" => Some(Sms)
case "phone" => Some(Phone)
case "paper" => Some(Paper)
case "sms" => Sms.some
case "phone" => Phone.some
case "paper" => Paper.some
case _ => None
}

Expand Down Expand Up @@ -53,20 +54,18 @@ object DataModel {
}

implicit val smsApiWrites: Writes[Sms] =
Writes(
_ match {
case sms: Sms.Outgoing =>
smsOutgoingFormat.writes(sms) match {
case obj: JsObject => obj + ("tag" -> JsString("outgoing"))
case other => other
}
case sms: Sms.Incoming =>
smsIncomingFormat.writes(sms) match {
case obj: JsObject => obj + ("tag" -> JsString("incoming"))
case other => other
}
}
)
Writes {
case sms: Sms.Outgoing =>
smsOutgoingFormat.writes(sms) match {
case obj: JsObject => obj + ("tag" -> JsString("outgoing"))
case other => other
}
case sms: Sms.Incoming =>
smsIncomingFormat.writes(sms) match {
case obj: JsObject => obj + ("tag" -> JsString("incoming"))
case other => other
}
}

}

Expand Down
35 changes: 15 additions & 20 deletions app/serializers/JsonFormats.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,38 @@ package serializers
import constants.Constants
import helper.{StringHelper, UUIDHelper}
import java.util.UUID

import models.mandat.{Mandat, SmsMandatInitiation}
import play.api.libs.json.Json.JsValueWrapper
import play.api.libs.json._
import play.api.libs.functional.syntax._
import play.api.libs.json.JsonConfiguration.Aux
import play.api.mvc.Results.InternalServerError

object JsonFormats {
implicit val jsonConfiguration = JsonConfiguration(naming = JsonNaming.SnakeCase)

implicit val mapUUIDReads = new Reads[Map[UUID, String]] {

def reads(jv: JsValue): JsResult[Map[UUID, String]] =
JsSuccess(jv.as[Map[String, String]].map { case (k, v) =>
UUIDHelper.fromString(k).get -> v.asInstanceOf[String]
})

}

implicit val mapUUIDWrites = new Writes[Map[UUID, String]] {
implicit val jsonConfiguration: Aux[Json.MacroOptions] =
JsonConfiguration(naming = JsonNaming.SnakeCase)

def writes(map: Map[UUID, String]): JsValue =
Json.obj(map.map { case (s, o) =>
val ret: (String, JsValueWrapper) = s.toString -> JsString(o)
ret
}.toSeq: _*)
implicit val mapUUIDReads: Reads[Map[UUID, String]] = (jv: JsValue) =>
JsSuccess(jv.as[Map[String, String]].map { case (k, v) =>
UUIDHelper.fromString(k).get -> v.asInstanceOf[String]
})

}
implicit val mapUUIDWrites: Writes[Map[UUID, String]] = (map: Map[UUID, String]) =>
Json.obj(map.map { case (s, o) =>
val ret: (String, JsValueWrapper) = s.toString -> JsString(o)
ret
}.toSeq: _*)

implicit val mapUUIDFormat = Format(mapUUIDReads, mapUUIDWrites)
implicit val mapUUIDFormat: Format[Map[UUID, String]] = Format(mapUUIDReads, mapUUIDWrites)

//
// Mandat
//
import serializers.DataModel.SmsFormats._

implicit val mandatIdReads: Reads[Mandat.Id] =
implicitly[Reads[UUID]].map(Mandat.Id.apply)
implicit val mandatIdReads: Reads[Mandat.Id] = implicitly[Reads[UUID]].map(Mandat.Id.apply)

implicit val mandatIdWrites: Writes[Mandat.Id] =
implicitly[Writes[UUID]].contramap((id: Mandat.Id) => id.underlying)
Expand Down