diff --git a/.scalafmt.conf b/.scalafmt.conf index 612bdf9f4..abb508439 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = "3.7.14" +version = "3.7.15" runner.dialect = scala213 maxColumn = 100 spaces.afterKeywordBeforeParen = true diff --git a/app/controllers/ApplicationController.scala b/app/controllers/ApplicationController.scala index 03e7e3fd0..567cd3821 100644 --- a/app/controllers/ApplicationController.scala +++ b/app/controllers/ApplicationController.scala @@ -11,7 +11,7 @@ import helper.PlayFormHelper.formErrorsLog import helper.ScalatagsHelpers.writeableOf_Modifier import helper.StringHelper.{CanonizeString, NonEmptyTrimmedString} import helper.Time.zonedDateTimeOrdering -import helper.{BusinessDaysCalculator, Hash, Time, UUIDHelper} +import helper.{Hash, Time, UUIDHelper} import helper.TwirlImports.toHtml import models.Answer.AnswerType import models.EventType._ @@ -50,6 +50,7 @@ import scala.util.{Failure, Success, Try} @Singleton case class ApplicationController @Inject() ( applicationService: ApplicationService, + businessDaysService: BusinessDaysService, config: AppConfig, eventService: EventService, fileService: FileService, @@ -586,10 +587,10 @@ case class ApplicationController @Inject() ( ) .lastOption match { case None => - BusinessDaysCalculator + businessDaysService .businessHoursBetween(application.creationDate, ZonedDateTime.now()) > (3 * 24) case Some(lastAnswer) => - BusinessDaysCalculator + businessDaysService .businessHoursBetween(lastAnswer.creationDate, ZonedDateTime.now()) > (15 * 24) } } diff --git a/app/helper/BusinessDaysCalculator.scala b/app/helper/BusinessDaysCalculator.scala index fd3cedb3a..148a23faf 100644 --- a/app/helper/BusinessDaysCalculator.scala +++ b/app/helper/BusinessDaysCalculator.scala @@ -1,16 +1,21 @@ package helper -import java.time.{DayOfWeek, ZonedDateTime} +import java.time.{DayOfWeek, LocalDate, ZonedDateTime} import java.time.temporal.ChronoUnit object BusinessDaysCalculator { - def isBusinessDay(date: ZonedDateTime): Boolean = { + def isBusinessDay(date: ZonedDateTime, holidays: Set[LocalDate]): Boolean = { + val localDate = date.toLocalDate val dayOfWeek = date.getDayOfWeek - !Set(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY).contains(dayOfWeek) + !Set(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY).contains(dayOfWeek) && !holidays.contains(localDate) } - def businessHoursBetween(startTime: ZonedDateTime, endTime: ZonedDateTime): Int = { + def businessHoursBetween( + startTime: ZonedDateTime, + endTime: ZonedDateTime, + holidays: Set[LocalDate] = Set.empty + ): Int = { val (start, end) = if (endTime.isAfter(startTime)) (startTime, endTime) else (endTime, startTime) @@ -18,7 +23,7 @@ object BusinessDaysCalculator { var totalHours: Int = 0 if (start.truncatedTo(ChronoUnit.DAYS).isEqual(end.truncatedTo(ChronoUnit.DAYS))) { - if (isBusinessDay(start)) { + if (isBusinessDay(start, holidays)) { totalHours = end.getHour - start.getHour } } else { @@ -26,7 +31,7 @@ object BusinessDaysCalculator { var current = start.truncatedTo(ChronoUnit.DAYS) // scalastyle:ignore while while (!current.isAfter(end)) { - if (isBusinessDay(current)) { + if (isBusinessDay(current, holidays)) { if (current.isEqual(start.truncatedTo(ChronoUnit.DAYS))) { // For the start day, count hours from start time to end of day totalHours += 24 - start.getHour diff --git a/app/models/User.scala b/app/models/User.scala index 82902e0cd..5a1794326 100644 --- a/app/models/User.scala +++ b/app/models/User.scala @@ -107,7 +107,7 @@ case class User( newsletterAcceptationDate.map(Time.adminsFormatter.format).getOrElse("") lazy val firstLoginDateLog: String = - firstLoginDate.map(Time.adminsFormatter.format).getOrElse("") + firstLoginDate.map(Time.formatForAdmins).getOrElse("") lazy val phoneNumberLog: String = phoneNumber.map(withQuotes).getOrElse("") lazy val observableOrganisationIdsLog: String = observableOrganisationIds.map(_.id).mkString(", ") diff --git a/app/services/BusinessDaysService.scala b/app/services/BusinessDaysService.scala new file mode 100644 index 000000000..b75b7ac85 --- /dev/null +++ b/app/services/BusinessDaysService.scala @@ -0,0 +1,22 @@ +package services + +import anorm._ +import anorm.SqlParser.scalar +import helper.BusinessDaysCalculator +import java.time.{LocalDate, ZonedDateTime} +import javax.inject.{Inject, Singleton} +import play.api.db.Database + +@Singleton +class BusinessDaysService @Inject() (db: Database) { + + val publicHolidays: List[LocalDate] = db.withTransaction { implicit connection => + SQL("SELECT holiday_date FROM public_holidays").as(scalar[LocalDate].*) + } + + private val publicHolidaySet: Set[LocalDate] = publicHolidays.toSet + + def businessHoursBetween(startTime: ZonedDateTime, endTime: ZonedDateTime): Int = + BusinessDaysCalculator.businessHoursBetween(startTime, endTime, publicHolidaySet) + +} diff --git a/app/views/myApplications.scala b/app/views/myApplications.scala index 93db4e544..b9e5a4789 100644 --- a/app/views/myApplications.scala +++ b/app/views/myApplications.scala @@ -33,7 +33,7 @@ object myApplications { )(frag())( div( cls := "mdl-cell mdl-cell--12-col mdl-grid--no-spacing", - if (filters.allGroupsOpenCount <= 0) + if (filters.allGroupsOpenCount <= 0 && filters.allGroupsClosedCount <= 0) noApplications(currentUser, currentUserRights) else openApplications(currentUser, currentUserRights, applications, groups, filters), diff --git a/build.sbt b/build.sbt index d4d23e0b5..2b2b7b766 100644 --- a/build.sbt +++ b/build.sbt @@ -92,7 +92,7 @@ libraryDependencies ++= Seq( "org.postgresql" % "postgresql" % "42.6.0", anormDependency, "com.typesafe.play" %% "play-mailer" % "9.0.0", - "com.typesafe.play" %% "play-json" % "2.10.1", + "com.typesafe.play" %% "play-json" % "2.10.3", "com.sun.mail" % "javax.mail" % "1.6.2", "net.jcazevedo" %% "moultingyaml" % "0.4.2", "com.github.tototoshi" %% "scala-csv" % "1.3.10", @@ -112,7 +112,7 @@ libraryDependencies ++= Seq( ) // Crash -libraryDependencies += "io.sentry" % "sentry-logback" % "6.32.0" +libraryDependencies += "io.sentry" % "sentry-logback" % "6.33.1" // Overrides dependencyOverrides += "org.apache.commons" % "commons-text" % "1.10.0" diff --git a/conf/evolutions/default/69.sql b/conf/evolutions/default/69.sql new file mode 100644 index 000000000..923874cfa --- /dev/null +++ b/conf/evolutions/default/69.sql @@ -0,0 +1,146 @@ +-- !Ups + +CREATE TABLE public_holidays ( + holiday_date date PRIMARY KEY +); + +-- https://www.data.gouv.fr/fr/datasets/jours-feries-en-france/ +INSERT INTO public_holidays (holiday_date) VALUES + ('2017-01-01'), + ('2017-04-17'), + ('2017-05-01'), + ('2017-05-08'), + ('2017-05-25'), + ('2017-06-05'), + ('2017-07-14'), + ('2017-08-15'), + ('2017-11-01'), + ('2017-11-11'), + ('2017-12-25'), + ('2018-01-01'), + ('2018-04-02'), + ('2018-05-01'), + ('2018-05-08'), + ('2018-05-10'), + ('2018-05-21'), + ('2018-07-14'), + ('2018-08-15'), + ('2018-11-01'), + ('2018-11-11'), + ('2018-12-25'), + ('2019-01-01'), + ('2019-04-22'), + ('2019-05-01'), + ('2019-05-08'), + ('2019-05-30'), + ('2019-06-10'), + ('2019-07-14'), + ('2019-08-15'), + ('2019-11-01'), + ('2019-11-11'), + ('2019-12-25'), + ('2020-01-01'), + ('2020-04-13'), + ('2020-05-01'), + ('2020-05-08'), + ('2020-05-21'), + ('2020-06-01'), + ('2020-07-14'), + ('2020-08-15'), + ('2020-11-01'), + ('2020-11-11'), + ('2020-12-25'), + ('2021-01-01'), + ('2021-04-05'), + ('2021-05-01'), + ('2021-05-08'), + ('2021-05-13'), + ('2021-05-24'), + ('2021-07-14'), + ('2021-08-15'), + ('2021-11-01'), + ('2021-11-11'), + ('2021-12-25'), + ('2022-01-01'), + ('2022-04-18'), + ('2022-05-01'), + ('2022-05-08'), + ('2022-05-26'), + ('2022-06-06'), + ('2022-07-14'), + ('2022-08-15'), + ('2022-11-01'), + ('2022-11-11'), + ('2022-12-25'), + ('2023-01-01'), + ('2023-04-10'), + ('2023-05-01'), + ('2023-05-08'), + ('2023-05-18'), + ('2023-05-29'), + ('2023-07-14'), + ('2023-08-15'), + ('2023-11-01'), + ('2023-11-11'), + ('2023-12-25'), + ('2024-01-01'), + ('2024-04-01'), + ('2024-05-01'), + ('2024-05-08'), + ('2024-05-09'), + ('2024-05-20'), + ('2024-07-14'), + ('2024-08-15'), + ('2024-11-01'), + ('2024-11-11'), + ('2024-12-25'), + ('2025-01-01'), + ('2025-04-21'), + ('2025-05-01'), + ('2025-05-08'), + ('2025-05-29'), + ('2025-06-09'), + ('2025-07-14'), + ('2025-08-15'), + ('2025-11-01'), + ('2025-11-11'), + ('2025-12-25'), + ('2026-01-01'), + ('2026-04-06'), + ('2026-05-01'), + ('2026-05-08'), + ('2026-05-14'), + ('2026-05-25'), + ('2026-07-14'), + ('2026-08-15'), + ('2026-11-01'), + ('2026-11-11'), + ('2026-12-25'), + ('2027-01-01'), + ('2027-03-29'), + ('2027-05-01'), + ('2027-05-06'), + ('2027-05-08'), + ('2027-05-17'), + ('2027-07-14'), + ('2027-08-15'), + ('2027-11-01'), + ('2027-11-11'), + ('2027-12-25'), + ('2028-01-01'), + ('2028-04-17'), + ('2028-05-01'), + ('2028-05-08'), + ('2028-05-25'), + ('2028-06-05'), + ('2028-07-14'), + ('2028-08-15'), + ('2028-11-01'), + ('2028-11-11'), + ('2028-12-25') +; + + +-- !Downs + +DROP TABLE public_holidays; diff --git a/project/build.properties b/project/build.properties index 27430827b..e8a1e246e 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.6 +sbt.version=1.9.7