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

Ajoute l’état du déploiement sur la page de stats publique #2129

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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
123 changes: 21 additions & 102 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 @@ -396,112 +402,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
.orElse(Organisation.deductedFromName(group.name))
.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
119 changes: 119 additions & 0 deletions app/services/DataService.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
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
.orElse(Organisation.deductedFromName(group.name))
.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