Skip to content

Commit

Permalink
Ajoute l’état du déploiement sur la page de stats publique (#2129)
Browse files Browse the repository at this point in the history
  • Loading branch information
niladic authored Nov 20, 2024
1 parent f106792 commit c2ed5b8
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 113 deletions.
6 changes: 3 additions & 3 deletions app/actions/LoginAction.scala
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class LoginAction @Inject() (
userService,
) {

def withPublicPage(publicPage: Result): BaseLoginAction =
def withPublicPage(publicPage: IO[Result]): BaseLoginAction =
new BaseLoginAction(
config,
dependencies,
Expand All @@ -89,7 +89,7 @@ class BaseLoginAction(
signupService: SignupService,
tokenService: TokenService,
userService: UserService,
publicPage: Option[Result] = none,
publicPage: Option[IO[Result]] = none,
) extends ActionBuilder[RequestWithUserData, AnyContent]
with ActionRefiner[Request, RequestWithUserData] {

Expand Down Expand Up @@ -202,7 +202,7 @@ class BaseLoginAction(
log.warn(s"Accès à la page ${request.path} non autorisé")
Future(userNotLogged("Vous devez vous identifier pour accéder à cette page."))
case Some(page) =>
Future.successful(page.asLeft)
page.map(_.asLeft).unsafeToFuture()
}
}
}
Expand Down
119 changes: 21 additions & 98 deletions app/controllers/ApiController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,18 @@ import helper.StringHelper
import java.time.ZonedDateTime
import java.util.UUID
import javax.inject.{Inject, Singleton}
import models.{Area, Authorization, Error, EventType, Organisation, User, UserGroup}
import models.{Area, Authorization, Error, EventType, Organisation, UserGroup}
import play.api.libs.json.{JsValue, Json}
import play.api.mvc.{Action, AnyContent, BaseController, ControllerComponents, Result}
import scala.concurrent.{ExecutionContext, Future}
import serializers.ApiModel._
import serializers.Keys
import services.{
AnonymizedDataService,
DataService,
EventService,
OrganisationService,
ServicesDependencies,
UserGroupService,
UserService
}
Expand All @@ -25,16 +27,20 @@ import services.{
case class ApiController @Inject() (
anonymizedDataService: AnonymizedDataService,
val controllerComponents: ControllerComponents,
loginAction: LoginAction,
dataService: DataService,
dependencies: ServicesDependencies,
eventService: EventService,
loginAction: LoginAction,
organisationService: OrganisationService,
userGroupService: UserGroupService,
userService: UserService,
userGroupService: UserGroupService
)(implicit val ec: ExecutionContext)
extends BaseController
with UserOperators {
import OrganisationService.FranceServiceInstance

import dependencies.ioRuntime

def franceServices: Action[AnyContent] =
loginAction.async { implicit request =>
asUserWithAuthorization(Authorization.isAdminOrObserver)(
Expand Down Expand Up @@ -394,108 +400,25 @@ case class ApiController @Inject() (
}
}

private val organisationSetAll: List[Set[Organisation]] = {
val groups: List[Set[Organisation]] = List(
Set("DDFIP", "DRFIP"),
Set("CPAM", "CRAM", "CNAM"),
Set("CARSAT", "CNAV")
).map(_.flatMap(id => Organisation.byId(Organisation.Id(id))))
val groupedSet: Set[Organisation.Id] = groups.flatMap(_.map(_.id)).toSet
val nonGrouped: List[Organisation] =
Organisation.organismesOperateurs.filterNot(org => groupedSet.contains(org.id))
groups ::: nonGrouped.map(Set(_))
}

private val organisationSetFranceService: List[Set[Organisation]] = (
List(
Set("DDFIP", "DRFIP"),
Set("CPAM", "CRAM", "CNAM"),
Set("CARSAT", "CNAV"),
Set("ANTS", "Préf")
) :::
List(
"CAF",
"CDAD",
"La Poste",
"MSA",
"Pôle emploi"
).map(Set(_))
).map(_.flatMap(id => Organisation.byId(Organisation.Id(id))))

private def organisationSetId(organisations: Set[Organisation]): String =
organisations.map(_.id.toString).mkString

def deploymentData: Action[AnyContent] =
loginAction.async { implicit request =>
asUserWithAuthorization(Authorization.isAdminOrObserver)(
EventType.DeploymentDashboardUnauthorized,
"Accès non autorisé au dashboard de déploiement"
) { () =>
val userGroups = userGroupService.allOrThrow
userService.allNoNameNoEmail.map { users =>
def usersIn(area: Area, organisationSet: Set[Organisation]): List[User] =
for {
group <- userGroups.filter(group =>
group.areaIds.contains[UUID](area.id)
&& organisationSet.exists(group.organisation.contains[Organisation])
)
user <- users if user.groupIds.contains[UUID](group.id)
} yield user

val organisationSets: List[Set[Organisation]] =
if (request.getQueryString(Keys.QueryParam.uniquementFs).getOrElse("oui") === "oui") {
organisationSetFranceService
} else {
organisationSetAll
}

val areasData = for {
area <- request.currentUser.areas.flatMap(Area.fromId).filterNot(_.name === "Demo")
} yield {
val numOfInstructors: Map[Set[Organisation], Int] = (
for {
organisations <- organisationSets
users = usersIn(area, organisations)
userSum = users
.filter(user => user.instructor && !user.disabled)
.map(_.id)
.distinct
.size
} yield (organisations, userSum)
).toMap

DeploymentData.AreaData(
areaId = area.id.toString,
areaName = area.toString,
numOfInstructorByOrganisationSet = numOfInstructors.map {
case (organisations, count) => (organisationSetId(organisations), count)
},
numOfOrganisationSetWithOneInstructor = numOfInstructors
.count { case (_, numOfInstructors) => numOfInstructors > 0 }
)
val organisationSets: List[Set[Organisation]] =
if (request.getQueryString(Keys.QueryParam.uniquementFs).getOrElse("oui") === "oui") {
DataService.organisationSetFranceService
} else {
DataService.organisationSetAll
}

val numOfAreasWithOneInstructorByOrganisationSet =
organisationSets.map { organisations =>
val id = organisationSetId(organisations)
val count =
areasData.count(data => data.numOfInstructorByOrganisationSet.getOrElse(id, 0) > 0)
(id, count)
}.toMap

val data = DeploymentData(
organisationSets = organisationSets.map(organisations =>
DeploymentData.OrganisationSet(
id = organisationSetId(organisations),
organisations = organisations
)
),
areasData = areasData,
numOfAreasWithOneInstructorByOrganisationSet =
numOfAreasWithOneInstructorByOrganisationSet
)
Ok(Json.toJson(data))
}
val areas = request.currentUser.areas.flatMap(Area.fromId).filterNot(_.name === "Demo")
dataService
.operateursDeploymentData(organisationSets, areas)
.map { data =>
Ok(Json.toJson(data))
}
.unsafeToFuture()
}
}

Expand Down
8 changes: 7 additions & 1 deletion app/controllers/ApplicationController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import serializers.Keys
import services.{
ApplicationService,
BusinessDaysService,
DataService,
EventService,
FileService,
MandatService,
Expand All @@ -88,6 +89,7 @@ case class ApplicationController @Inject() (
businessDaysService: BusinessDaysService,
config: AppConfig,
val controllerComponents: ControllerComponents,
dataService: DataService,
dependencies: ServicesDependencies,
eventService: EventService,
fileService: FileService,
Expand Down Expand Up @@ -872,7 +874,11 @@ case class ApplicationController @Inject() (
}
}

val statsAction: BaseLoginAction = loginAction.withPublicPage(Ok(views.publicStats.page))
val statsAction: BaseLoginAction = loginAction.withPublicPage(
dataService
.operateursDeploymentData(DataService.organisationSetFranceService, Area.allExcludingDemo)
.map(deploymentData => Ok(views.publicStats.page(deploymentData)))
)

def stats: Action[AnyContent] =
statsAction.async { implicit request =>
Expand Down
8 changes: 5 additions & 3 deletions app/controllers/SignupController.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ case class SignupController @Inject() (
EventType.SignupFormShowed,
s"Visualisation de la page d'inscription ${signupRequest.id}"
)
groupService.all.map(groups =>
Ok(views.signup.page(signupRequest, SignupFormData.form, groups))
)
groupService.all
.map(groups => Ok(views.signup.page(signupRequest, SignupFormData.form, groups)))
.unsafeToFuture()
}

def createSignup: Action[AnyContent] =
Expand All @@ -73,6 +73,7 @@ case class SignupController @Inject() (
)
groupService.all
.map(groups => BadRequest(views.signup.page(signupRequest, formWithError, groups)))
.unsafeToFuture()
},
form => {
// `SignupFormData` is supposed to validate that boolean,
Expand Down Expand Up @@ -132,6 +133,7 @@ case class SignupController @Inject() (
.map(groups =>
InternalServerError(views.signup.page(signupRequest, formWithError, groups))
)
.unsafeToFuture()
},
_ =>
LoginAction.readUserRights(user).flatMap { userRights =>
Expand Down
1 change: 1 addition & 0 deletions app/serializers/ApiModel.scala
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ object ApiModel {
case class AreaData(
areaId: String,
areaName: String,
areaCode: String,
numOfInstructorByOrganisationSet: Map[String, Int],
numOfOrganisationSetWithOneInstructor: Int
)
Expand Down
115 changes: 115 additions & 0 deletions app/services/DataService.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package services

import cats.effect.IO
import java.util.UUID
import javax.inject.{Inject, Singleton}
import models.{Area, Organisation, User}
import serializers.ApiModel.DeploymentData

object DataService {

val organisationSetAll: List[Set[Organisation]] = {
val groups: List[Set[Organisation]] = List(
Set("DDFIP", "DRFIP"),
Set("CPAM", "CRAM", "CNAM"),
Set("CARSAT", "CNAV")
).map(_.flatMap(id => Organisation.byId(Organisation.Id(id))))
val groupedSet: Set[Organisation.Id] = groups.flatMap(_.map(_.id)).toSet
val nonGrouped: List[Organisation] =
Organisation.organismesOperateurs.filterNot(org => groupedSet.contains(org.id))
groups ::: nonGrouped.map(Set(_))
}

val organisationSetFranceService: List[Set[Organisation]] = (
List(
Set("DDFIP", "DRFIP"),
Set("CPAM", "CRAM", "CNAM"),
Set("CARSAT", "CNAV"),
Set("ANTS", "Préf")
) :::
List(
"CAF",
"CDAD",
"La Poste",
"MSA",
"Pôle emploi"
).map(Set(_))
).map(_.flatMap(id => Organisation.byId(Organisation.Id(id))))

}

@Singleton
class DataService @Inject() (
userService: UserService,
userGroupService: UserGroupService,
) {

private def organisationSetId(organisations: Set[Organisation]): String =
organisations.map(_.id.toString).mkString

def operateursDeploymentData(
organisationSets: List[Set[Organisation]],
areas: List[Area]
): IO[DeploymentData] =
userGroupService.all.flatMap { userGroups =>
userService.allNoNameNoEmail.map { users =>
def usersIn(area: Area, organisationSet: Set[Organisation]): List[User] =
for {
group <- userGroups.filter(group =>
group.areaIds.contains[UUID](area.id)
&& organisationSet.exists(group.organisation.contains[Organisation])
)
user <- users if user.groupIds.contains[UUID](group.id)
} yield user

val areasData = for {
area <- areas
} yield {
val numOfInstructors: Map[Set[Organisation], Int] = (
for {
organisations <- organisationSets
users = usersIn(area, organisations)
userSum = users
.filter(user => user.instructor && !user.disabled)
.map(_.id)
.distinct
.size
} yield (organisations, userSum)
).toMap

DeploymentData.AreaData(
areaId = area.id.toString,
areaName = area.toString,
areaCode = area.inseeCode,
numOfInstructorByOrganisationSet = numOfInstructors.map { case (organisations, count) =>
(organisationSetId(organisations), count)
},
numOfOrganisationSetWithOneInstructor = numOfInstructors
.count { case (_, numOfInstructors) => numOfInstructors > 0 }
)
}

val numOfAreasWithOneInstructorByOrganisationSet =
organisationSets.map { organisations =>
val id = organisationSetId(organisations)
val count =
areasData.count(data => data.numOfInstructorByOrganisationSet.getOrElse(id, 0) > 0)
(id, count)
}.toMap

val data = DeploymentData(
organisationSets = organisationSets.map(organisations =>
DeploymentData.OrganisationSet(
id = organisationSetId(organisations),
organisations = organisations
)
),
areasData = areasData,
numOfAreasWithOneInstructorByOrganisationSet =
numOfAreasWithOneInstructorByOrganisationSet
)
data
}
}

}
3 changes: 2 additions & 1 deletion app/services/UserGroupService.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package services

import anorm._
import aplus.macros.Macros
import cats.effect.IO
import cats.syntax.all._
import helper.{Time, UUIDHelper}
import java.sql.ResultSet
Expand Down Expand Up @@ -144,7 +145,7 @@ class UserGroupService @Inject() (
SQL(s"SELECT $fieldsInSelect FROM user_group").as(simpleUserGroup.*)
}

def all: Future[List[UserGroup]] = Future(allOrThrow)
def all: IO[List[UserGroup]] = IO.blocking(allOrThrow)

def byIds(groupIds: List[UUID]): List[UserGroup] =
db.withConnection { implicit connection =>
Expand Down
Loading

0 comments on commit c2ed5b8

Please sign in to comment.