diff --git a/app/controllers/PaperGuide.scala b/app/controllers/PaperGuide.scala new file mode 100644 index 000000000..88e66c153 --- /dev/null +++ b/app/controllers/PaperGuide.scala @@ -0,0 +1,121 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2013 Association du Paris Java User Group. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package controllers + +import java.io.{PrintWriter, File} + +import models.{Speaker, Slot, ConferenceDescriptor, ScheduleConfiguration} +import org.apache.commons.lang3.StringEscapeUtils +import play.api.i18n.Messages + +/** + * Simple controller used to export the whole program as a CSV file. + * Practical if you want to print the schedule. + * + * @author created by N.Martignole, Innoteria, on 11/04/16. + */ +object PaperGuide extends SecureCFPController { + + def exportProgram() = SecuredAction(IsMemberOf("cfp")) { + implicit request: SecuredRequest[play.api.mvc.AnyContent] => + implicit val context = scala.concurrent.ExecutionContext.Implicits.global + + + val allScheduledConf: List[ScheduleConfiguration] = for (confType <- ConferenceDescriptor.ConferenceProposalTypes.ALL; + scheduleId <- ScheduleConfiguration.getPublishedSchedule(confType.id); + scheduledConf <- ScheduleConfiguration.loadScheduledConfiguration(scheduleId) + ) yield scheduledConf + + val file = new File("./target", "DEVOXX_UTF8_" + allScheduledConf.hashCode() + ".csv") + val writer = new PrintWriter(file, "MacRoman") // !!! ENCODING !!! +// val writer = new PrintWriter(file, "UTF-8") + writer.println("id,name,day,from,to,roomName,proposalId,proposalTitle,proposalLang,track,firstSpeaker,secondarySpeaker,thirdSpeaker,fourthSpeaker,fifthSpeaker") + + allScheduledConf.foreach { + scheduleConf: ScheduleConfiguration => + scheduleConf.slots.foreach { + slot: Slot => + writer.print(slot.id) + writer.print(",") + writer.print(slot.name) + writer.print(",") + writer.print(slot.day) + writer.print(",") + writer.print(slot.from.toString("HH:mm")) + writer.print(",") + writer.print(slot.to.toString("HH:mm")) + writer.print(",") + writer.print(slot.room.name) + writer.print(",") + slot.proposal.map { + proposal => + writer.print(proposal.id) + writer.print(",") + writer.print(StringEscapeUtils.escapeCsv(proposal.title)) + writer.print(",") + writer.print(proposal.lang) + writer.print(",") + writer.print(StringEscapeUtils.escapeCsv(Messages(proposal.track.label))) + writer.print(",") + + Speaker.findByUUID(proposal.mainSpeaker).map { + speaker: Speaker => + writer.print(StringEscapeUtils.escapeCsv(speaker.cleanName)) + writer.print(",") + }.getOrElse(writer.print(",,")) + + proposal.secondarySpeaker.map { + secSpeaker => + Speaker.findByUUID(secSpeaker).map { + s: Speaker => + writer.print(StringEscapeUtils.escapeCsv(s.cleanName)) + writer.print(",") + }.getOrElse(writer.print(",,")) + }.getOrElse(writer.print(",,")) + + val otherSpeakersNames = proposal.otherSpeakers.map{ + id:String=> + Speaker.findByUUID(id).map{s=> + StringEscapeUtils.escapeCsv(s.cleanName) + "," + }.getOrElse(",,") + } + + val paddedToSixSpeakers = otherSpeakersNames.padTo(6,",") + + paddedToSixSpeakers.foreach{token=>writer.print(token)} + + }.getOrElse { + writer.print(",,,,,,,,") + } + writer.println("") + + } + } + writer.close() + + Ok("Done - Check target CFP folder") + + } + +} diff --git a/app/controllers/Publisher.scala b/app/controllers/Publisher.scala index 5bec82a98..4339bde25 100644 --- a/app/controllers/Publisher.scala +++ b/app/controllers/Publisher.scala @@ -25,7 +25,7 @@ package controllers import akka.util.Crypt import library.search.ElasticSearch -import library.{LogURL, SendQuestionToSpeaker, ZapActor} +import library.{LogURL, ZapActor} import models._ import play.api.cache.Cache import play.api.data.Form @@ -36,14 +36,14 @@ import play.api.mvc._ /** - * Publisher is the controller responsible for the Web content of your conference Program. - * Created by nicolas on 12/02/2014. - */ + * Publisher is the controller responsible for the Web content of your conference Program. + * Created by nicolas on 12/02/2014. + */ object Publisher extends Controller { def homePublisher = Action { implicit request => val result = views.html.Publisher.homePublisher() - val etag = Crypt.md5(result.toString()+"dvx").toString + val etag = Crypt.md5(result.toString() + "dvx").toString val maybeETag = request.headers.get(IF_NONE_MATCH) maybeETag match { @@ -54,16 +54,23 @@ object Publisher extends Controller { def showAllSpeakers = Action { implicit request => - import play.api.Play.current - val speakers = Cache.getOrElse[List[Speaker]]("allSpeakersWithAcceptedTerms", 600) { - Speaker.allSpeakersWithAcceptedTerms() - } - val etag = speakers.hashCode().toString + "_2" - val maybeETag = request.headers.get(IF_NONE_MATCH) - maybeETag match { - case Some(oldEtag) if oldEtag == etag => NotModified - case other => Ok(views.html.Publisher.showAllSpeakers(speakers)).withHeaders(ETAG -> etag) + + // First load published slots + val publishedConf = ScheduleConfiguration.loadAllPublishedSlots().filter(_.proposal.isDefined) + val allSpeakersIDs = publishedConf.flatMap(_.proposal.get.allSpeakerUUIDs).toSet + val etag = allSpeakersIDs.hashCode.toString + + request.headers.get(IF_NONE_MATCH) match { + case Some(tag) if tag == etag => + NotModified + + case other => + val onlySpeakersThatAcceptedTerms: Set[String] = allSpeakersIDs.filterNot(uuid => Speaker.needsToAccept(uuid)) + val speakers = Speaker.loadSpeakersFromSpeakerIDs(onlySpeakersThatAcceptedTerms) + Ok(views.html.Publisher.showAllSpeakers(speakers)).withHeaders(ETAG -> etag) + } + } def showSpeakerByName(name: String) = Action { @@ -126,40 +133,40 @@ object Publisher extends Controller { maybeScheduledConfiguration match { case Some(slotConfig) if day == null => { val updatedConf = slotConfig.copy(slots = slotConfig.slots) - Ok(views.html.Publisher.showAgendaByConfType(updatedConf, confType, "wednesday")) + Ok(views.html.Publisher.showAgendaByConfType(updatedConf.slots, confType, "wednesday")) } case Some(slotConfig) if day == "monday" => { val updatedConf = slotConfig.copy(slots = slotConfig.slots.filter(_.day == "monday") , timeSlots = slotConfig.timeSlots.filter(_.start.getDayOfWeek == 1)) - Ok(views.html.Publisher.showAgendaByConfType(updatedConf, confType, "monday")) + Ok(views.html.Publisher.showAgendaByConfType(updatedConf.slots, confType, "monday")) } case Some(slotConfig) if day == "tuesday" => { val updatedConf = slotConfig.copy( slots = slotConfig.slots.filter(_.day == "tuesday") , timeSlots = slotConfig.timeSlots.filter(_.start.getDayOfWeek == 2) ) - Ok(views.html.Publisher.showAgendaByConfType(updatedConf, confType, "tuesday")) + Ok(views.html.Publisher.showAgendaByConfType(updatedConf.slots, confType, "tuesday")) } case Some(slotConfig) if day == "wednesday" => { val updatedConf = slotConfig.copy( slots = slotConfig.slots.filter(_.day == "wednesday") , timeSlots = slotConfig.timeSlots.filter(_.start.getDayOfWeek == 3) ) - Ok(views.html.Publisher.showAgendaByConfType(updatedConf, confType, "wednesday")) + Ok(views.html.Publisher.showAgendaByConfType(updatedConf.slots, confType, "wednesday")) } case Some(slotConfig) if day == "thursday" => { val updatedConf = slotConfig.copy( slots = slotConfig.slots.filter(_.day == "thursday") , timeSlots = slotConfig.timeSlots.filter(_.start.getDayOfWeek == 4) ) - Ok(views.html.Publisher.showAgendaByConfType(updatedConf, confType, "thursday")) + Ok(views.html.Publisher.showAgendaByConfType(updatedConf.slots, confType, "thursday")) } case Some(slotConfig) if day == "friday" => { val updatedConf = slotConfig.copy( slots = slotConfig.slots.filter(_.day == "friday") , timeSlots = slotConfig.timeSlots.filter(_.start.getDayOfWeek == 5) ) - Ok(views.html.Publisher.showAgendaByConfType(updatedConf, confType, "friday")) + Ok(views.html.Publisher.showAgendaByConfType(updatedConf.slots, confType, "friday")) } case None => NotFound(views.html.Publisher.agendaNotYetPublished()) @@ -169,9 +176,8 @@ object Publisher extends Controller { def showByDay(day: String) = Action { implicit request => - def _showDay(slots: List[Slot], day: String) = { - val rooms = slots.groupBy(_.room).keys.toList.sortBy(_.id) + val rooms = slots.groupBy(_.room).keys.toList val allSlots = ScheduleConfiguration.getPublishedScheduleByDay(day) Ok(views.html.Publisher.showOneDay(allSlots, rooms, day)) } diff --git a/app/controllers/RestAPI.scala b/app/controllers/RestAPI.scala index 837644a15..e261646c9 100644 --- a/app/controllers/RestAPI.scala +++ b/app/controllers/RestAPI.scala @@ -151,7 +151,8 @@ object RestAPI extends Controller { )) ) ) - Ok(jsonObject).as(JSON).withHeaders(ETAG -> etag, "Links" -> ("<" + routes.RestAPI.profile("conference").absoluteURL().toString + ">; rel=\"profile\"")) + Ok(jsonObject).as(JSON).withHeaders(ETAG -> etag, + "Links" -> ("<" + routes.RestAPI.profile("conference").absoluteURL() + ">; rel=\"profile\"")) } } }.getOrElse(NotFound("Conference not found")) diff --git a/app/models/ConferenceDescriptor.scala b/app/models/ConferenceDescriptor.scala index 6780fa5ac..e212d96a3 100644 --- a/app/models/ConferenceDescriptor.scala +++ b/app/models/ConferenceDescriptor.scala @@ -212,6 +212,8 @@ object ConferenceDescriptor { // Tip : I use the ID to sort-by on the view per day... So if the exhibition floor id is "aaa" it will be // the first column on the HTML Table + + // Do not change the ID's once the program is published val HALL_EXPO = Room("a_hall", "Exhibition floor", 2300, "special", "") val HALL_A = Room("x_hall_a", "Open Data Camp", 100, "special", "") @@ -459,23 +461,17 @@ object ConferenceDescriptor { val keynoteThursdaySlot2 = ConferenceRooms.keynoteRoom.map { r2 => SlotBuilder(ConferenceProposalTypes.KEY.id, "thursday", - new DateTime("2016-04-21T09:40:00.000+02:00").toDateTime(DateTimeZone.forID("Europe/Paris")), - new DateTime("2016-04-21T10:00:00.000+02:00").toDateTime(DateTimeZone.forID("Europe/Paris")), r2) + new DateTime("2016-04-21T09:45:00.000+02:00").toDateTime(DateTimeZone.forID("Europe/Paris")), + new DateTime("2016-04-21T10:05:00.000+02:00").toDateTime(DateTimeZone.forID("Europe/Paris")), r2) } val keynoteThursdaySlot3 = ConferenceRooms.keynoteRoom.map { r3 => SlotBuilder(ConferenceProposalTypes.KEY.id, "thursday", - new DateTime("2016-04-21T10:05:00.000+02:00").toDateTime(DateTimeZone.forID("Europe/Paris")), - new DateTime("2016-04-21T10:25:00.000+02:00").toDateTime(DateTimeZone.forID("Europe/Paris")), r3) - } - val keynoteThursdaySlot4 = ConferenceRooms.keynoteRoom.map { - r4 => - SlotBuilder(ConferenceProposalTypes.KEY.id, "thursday", - new DateTime("2016-04-21T10:25:00.000+02:00").toDateTime(DateTimeZone.forID("Europe/Paris")), - new DateTime("2016-04-21T10:45:00.000+02:00").toDateTime(DateTimeZone.forID("Europe/Paris")), r4) + new DateTime("2016-04-21T10:10:00.000+02:00").toDateTime(DateTimeZone.forID("Europe/Paris")), + new DateTime("2016-04-21T10:30:00.000+02:00").toDateTime(DateTimeZone.forID("Europe/Paris")), r3) } - keynoteThursdayWelcome ++keynoteThursdaySlot1 ++ keynoteThursdaySlot2 ++ keynoteThursdaySlot3 ++ keynoteThursdaySlot4 + keynoteThursdayWelcome ++keynoteThursdaySlot1 ++ keynoteThursdaySlot2 ++ keynoteThursdaySlot3 } val keynoteSlotsFriday: List[Slot] = { diff --git a/app/models/Slot.scala b/app/models/Slot.scala index db42e4d1b..c2c74ab7f 100644 --- a/app/models/Slot.scala +++ b/app/models/Slot.scala @@ -25,8 +25,6 @@ package models import org.joda.time.DateTime import play.api.libs.json.Json -import play.api.libs.json._ -import play.api.libs.functional.syntax._ /** * Time slots and Room are defined as static file. @@ -38,8 +36,6 @@ import play.api.libs.functional.syntax._ case class Room(id: String, name: String, capacity: Int, setup: String, recorded:String) extends Ordered[Room] { - import scala.math.Ordered.orderingToOrdered - def index: Int = { val regexp = "[\\D\\s]+(\\d+)".r id match { @@ -48,7 +44,17 @@ case class Room(id: String, name: String, capacity: Int, setup: String, recorded } } - def compare(that: Room): Int = (this.id.substring(0, 3), this.index) compare(that.id.substring(0, 3), that.index) + def compare(that: Room): Int = { + // TODO a virer apres Devoxx FR 2016 + // Hack for Devoxx France => I cannot change the Room IDs so I fix the order in an IndexedSeq here + if(Room.fixedOrderForRoom.indexOf(this.id) < Room.fixedOrderForRoom.indexOf(that.id)){ + return -1 + } +if(Room.fixedOrderForRoom.indexOf(this.id) > Room.fixedOrderForRoom.indexOf(that.id)){ + return 1 + } + return 0 + } } object Room { @@ -62,6 +68,33 @@ object Room { ConferenceDescriptor.ConferenceRooms.allRooms.find(r => r.id == roomId).getOrElse(OTHER) } + // TODO à virer apres Devoxx FR 2016 + val fixedOrderForRoom = IndexedSeq("a_hall", + "b_amphi", + "c_maillot", + "d_par241", + "f_neu251", + "e_neu252", + "par242AB", + "par242A", + "par242AT", + "par242B", + "par242BT", + "par243", + "neu253", + "neu253_t", + "par243_t", + "par201", + "par202_203", + "par204", + "par221M-222M", + "par224M-225M", + "neu_232_232", + "neu_234_235", + "neu_212_213", + "x_hall_a" + ) + } case class SlotBreak(id: String, nameEN: String, nameFR: String, room: Room) diff --git a/app/views/CFPAdmin/allSpeakersWithAcceptedTalksAndBadge.scala.html b/app/views/CFPAdmin/allSpeakersWithAcceptedTalksAndBadge.scala.html index 190784019..c18b196e8 100644 --- a/app/views/CFPAdmin/allSpeakersWithAcceptedTalksAndBadge.scala.html +++ b/app/views/CFPAdmin/allSpeakersWithAcceptedTalksAndBadge.scala.html @@ -1,4 +1,4 @@ -@(proposals: List[(Speaker, Iterable[Proposal])])(implicit lang: Lang, flash: Flash, req: RequestHeader) +@(speakersAndProposals: List[(Speaker, Iterable[Proposal])])(implicit lang: Lang, flash: Flash, req: RequestHeader) @main("CFP Speakers") {
- @proposals.sortBy(_._1.name.map(_.capitalize)).map{case(speaker,props)=> -@speaker.name.get.head.toString;@speaker.name.map(_.toUpperCase);@speaker.firstName.get;@speaker.email;@speaker.company.getOrElse("");@speaker.twitter.getOrElse("") } + @speakersAndProposals.sortBy(_._1.name.map(_.capitalize)).map{case(speaker,props)=> +"@speaker.name.get.head.toString","@speaker.name.map(_.toUpperCase)","@speaker.firstName.get","@speaker.email.trim","@speaker.company.getOrElse("")","@speaker.cleanTwitter.map(_.drop(1))" }diff --git a/app/views/Publisher/showAgendaByConfType.scala.html b/app/views/Publisher/showAgendaByConfType.scala.html index ceeba8a6d..f3123d858 100644 --- a/app/views/Publisher/showAgendaByConfType.scala.html +++ b/app/views/Publisher/showAgendaByConfType.scala.html @@ -1,7 +1,7 @@ -@(schedule: ScheduleConfiguration, talkType: String, day: String)(implicit lang: Lang, flash: Flash, req: RequestHeader) -@import org.joda.time.DateTimeZone +@(slots: List[Slot], talkType: String, day: String)(implicit lang: Lang, flash: Flash, req: RequestHeader) + @import org.joda.time.DateTimeZone - @views.html.Publisher.devoxxFR2016(Messages("agenda-" + talkType),Some(s"Agenda for Devoxx FR 2016 for $day"+Messages("day-" + day))) { + @views.html.Publisher.devoxxFR2016(Messages("agenda-" + talkType), Some(s"Agenda for Devoxx FR 2016 for $day" + Messages("day-" + day))) {
Salle | - @schedule.slots.groupBy(_.room).keys.toList.sorted.map { room: Room => +@Messages("publisher.room") | + @slots.groupBy(_.room).keys.toList.sorted.map { room: Room =>@room.name | }||||||
---|---|---|---|---|---|---|---|---|
09:00 - 11:00 |
Keynotes dès 9h précise - Le programme détaillé sera publié ultérieurement. Accueil + Petit-déjeuner dès 7h30 - + Accueil + Petit-déjeuner dès 7h30 |
|||||||
09:30 - 11:00 |
Keynotes 9h30 - Le programme détaillé sera publié ultérieurement. |
|||||||
- @time.toDateTime(DateTimeZone.forID("Europe/Brussels")).toString("HH:mm") - @slots.head.to.toDateTime(DateTimeZone.forID("Europe/Brussels")).toString("HH:mm") + @subSlots.head.from.toDateTime(DateTimeZone.forID("Europe/Brussels")).toString("HH:mm") + - @subSlots.head.to.toDateTime(DateTimeZone.forID("Europe/Brussels")).toString("HH:mm") | - @slots.sortBy(_.room).zipWithIndex.map { case(slot,index) => - -- @slot.proposal.map { p: Proposal => - @tags.publisher.renderIconForTrack(p.track) @Messages(p.track.label) - @tags.publisher.renderFavorite(p.id) - @p.title - @p.allSpeakerUUIDs.map { speakerUUID => - @tags.publisher.renderSpeaker(speakerUUID) - } - - - } - @if(slot.proposal.isEmpty && slot.break.isEmpty ){ - Pas encore programmé - } - | + @subSlots.groupBy(_.room).keys.toList.sorted.map { room: Room => + @tags.publisher.tagRenderRow(subSlots, room, 17) }|||||||
Salle | - @schedule.slots.groupBy(_.room).keys.toList.sorted.map { room: Room => -@room.name | +@Messages("publisher.room") | + @slots.groupBy(_.room).keys.toList.sorted.map { roomName: Room => +@roomName.name | }
Room | - @rooms.sorted.map { roomName: Room => -@roomName.name | - } + + @views.html.Publisher.devoxxFR2016(Messages(s"sw.show.$day")) { +
---|
@Messages("publisher.room") | + @slots.groupBy(_.room).keys.toList.sorted.map { roomName: Room => +@roomName.name | + }
---|---|
- @subSlots.head.from.toDateTime(DateTimeZone.forID("Europe/Brussels")).toString("HH:mm") - @subSlots.head.to.toDateTime(DateTimeZone.forID("Europe/Brussels")).toString("HH:mm") - | - @rooms.map { room => - @tags.publisher.tagRenderRow(subSlots,room,rooms.size) - } -|
Room | - @slots.groupBy(_.room).keys.toList.sorted.map { roomName: Room => -@roomName.name | + @slots.groupBy(s => s.from.getMillis).toList.sortWith(_._1 < _._1).map { case (_, subSlots) => + @tags.publisher.renderOneDay(subSlots,slots,rooms) } -