diff --git a/airline-data/src/main/scala/com/patson/data/Constants.scala b/airline-data/src/main/scala/com/patson/data/Constants.scala index 83042d0e1..aa6df4ce9 100644 --- a/airline-data/src/main/scala/com/patson/data/Constants.scala +++ b/airline-data/src/main/scala/com/patson/data/Constants.scala @@ -73,7 +73,7 @@ object Constants { val AIRLINE_MODIFIER_INDEX_PREFIX = "airline_modifier_index_" val AIRLINE_MODIFIER_PROPERTY_TABLE = "airline_modifier_property" - + val INCOME_TABLE = "income" val CASH_FLOW_TABLE = "cash_flow" val AIRLINE_LOGO_TABLE = "airline_logo" @@ -176,7 +176,7 @@ object Constants { //Christmas Event val SANTA_CLAUS_INFO_TABLE = "santa_claus_info" val SANTA_CLAUS_GUESS_TABLE = "santa_claus_guess" - + // val DATABASE_CONNECTION = "jdbc:sqlite:../airline-data/db/default.db" // val DB_DRIVER = "org.sqlite.JDBC" val configFactory = ConfigFactory.load() @@ -192,5 +192,5 @@ object Constants { val DATABASE_PASSWORD = if (configFactory.hasPath("mysqldb.password")) configFactory.getString("mysqldb.password") else "admin" println(s"!!!!!!!!!!!!!!!FINAL DB str $DATABASE_CONNECTION with user $DATABASE_USER") - + } \ No newline at end of file diff --git a/airline-data/src/main/scala/com/patson/data/LinkSource.scala b/airline-data/src/main/scala/com/patson/data/LinkSource.scala index 66173327b..4dd0defea 100644 --- a/airline-data/src/main/scala/com/patson/data/LinkSource.scala +++ b/airline-data/src/main/scala/com/patson/data/LinkSource.scala @@ -11,19 +11,19 @@ import com.patson.util.{AirlineCache, AirplaneModelCache, AirportCache} import scala.collection.mutable import scala.collection.mutable.{HashMap, HashSet, ListBuffer, Set} - + object LinkSource { val FULL_LOAD = Map(DetailType.AIRLINE -> true, DetailType.AIRPORT -> true, DetailType.AIRPLANE -> true) val SIMPLE_LOAD = Map(DetailType.AIRLINE -> false, DetailType.AIRPORT -> false, DetailType.AIRPLANE -> false) val ID_LOAD : Map[DetailType.Type, Boolean] = Map.empty - + private[this]val BASE_QUERY = "SELECT * FROM " + LINK_TABLE - + def loadLinksByCriteria(criteria : List[(String, Any)], loadDetails : Map[DetailType.Value, Boolean] = SIMPLE_LOAD) = { - var queryString = BASE_QUERY - + var queryString = BASE_QUERY + if (!criteria.isEmpty) { queryString += " WHERE " for (i <- 0 until criteria.size - 1) { @@ -31,7 +31,7 @@ object LinkSource { } queryString += criteria.last._1 + " = ?" } - + loadLinksByQueryString(queryString, criteria.map(_._2), loadDetails) } @@ -46,7 +46,7 @@ object LinkSource { loadLinksByQueryString(queryString, criteria.map(_._2), loadDetails).map(_.asInstanceOf[Link]) } - + def loadLinksByIds(ids : List[Int], loadDetails : Map[DetailType.Value, Boolean] = SIMPLE_LOAD) = { if (ids.isEmpty) { List.empty @@ -55,35 +55,35 @@ object LinkSource { for (i <- 0 until ids.size - 1) { queryString.append("?,") } - + queryString.append("?)") loadLinksByQueryString(queryString.toString(), ids, loadDetails) } } - + def loadLinksByQueryString(queryString : String, parameters : List[Any], loadDetails : Map[DetailType.Value, Boolean] = SIMPLE_LOAD) = { val connection = Meta.getConnection() - - try { + + try { val preparedStatement = connection.prepareStatement(queryString) - + for (i <- 0 until parameters.size) { preparedStatement.setObject(i + 1, parameters(i)) } - + val resultSet = preparedStatement.executeQuery() - + val links = new ListBuffer[Transport]() - + val linkIds : Set[Int] = new HashSet[Int] val airportIds : Set[Int] = new HashSet[Int] - + while (resultSet.next()) { airportIds += resultSet.getInt("from_airport") airportIds += resultSet.getInt("to_airport") linkIds += resultSet.getInt("id") } - + val assignedAirplaneCache : Map[Int, Map[Airplane, LinkAssignment]] = loadDetails.get(DetailType.AIRPLANE) match { case Some(fullLoad) => loadAssignedAirplanesByLinks(connection, linkIds.toList) case None => Map.empty @@ -95,20 +95,20 @@ object LinkSource { } case None => airportIds.map(id => (id, Airport.fromId(id))).toMap } - + resultSet.beforeFirst() while (resultSet.next()) { val fromAirportId = resultSet.getInt("from_airport") val toAirportId = resultSet.getInt("to_airport") val airlineId = resultSet.getInt("airline") - + val fromAirport = airportCache.get(fromAirportId) //Do not use AirportCache as fullLoad will be slow val toAirport = airportCache.get(toAirportId) //Do not use AirportCache as fullLoad will be slow val airline = loadDetails.get(DetailType.AIRLINE) match { case Some(fullLoad) => AirlineCache.getAirline(airlineId, fullLoad).orElse(Some(Airline.fromId(airlineId))) case None => Some(Airline.fromId(airlineId)) } - + if (fromAirport.isDefined && toAirport.isDefined && airline.isDefined) { val transportType = TransportType(resultSet.getInt("transport_type")) val link = { @@ -151,13 +151,13 @@ object LinkSource { } } } - - links += link + + links += link } else { println("Failed loading link [" + resultSet.getInt("id") + "] as some details cannot be loaded " + fromAirport + toAirport + airline) } } - + resultSet.close() preparedStatement.close() links.toList @@ -207,21 +207,21 @@ object LinkSource { } } } - + def loadFlightNumbers(airlineId : Int) : List[Int] = { val connection = Meta.getConnection() - - try { + + try { val preparedStatement = connection.prepareStatement("SELECT flight_number FROM " + LINK_TABLE + " WHERE airline = ?") - + preparedStatement.setInt(1, airlineId) - + val resultSet = preparedStatement.executeQuery() - - val flightNumbers = ListBuffer[Int]() + + val flightNumbers = ListBuffer[Int]() while (resultSet.next()) { flightNumbers.append(resultSet.getInt("flight_number")) - } + } resultSet.close() preparedStatement.close() flightNumbers.toList @@ -229,7 +229,7 @@ object LinkSource { connection.close() } } - + def loadAssignedAirplanesByLinks(connection : Connection, linkIds : List[Int]) : Map[Int, Map[Airplane, LinkAssignment]] = { if (linkIds.isEmpty) { Map.empty @@ -238,23 +238,23 @@ object LinkSource { for (i <- 0 until linkIds.size - 1) { queryString.append("?,") } - + queryString.append("?)") val linkAssignmentStatement = connection.prepareStatement(queryString.toString) for (i <- 0 until linkIds.size) { linkAssignmentStatement.setInt(i + 1, linkIds(i)) } - + val assignmentResultSet = linkAssignmentStatement.executeQuery - + val airplaneIds = new HashSet[Int] while (assignmentResultSet.next()) { airplaneIds += assignmentResultSet.getInt("airplane") } - + val airplaneCache = AirplaneSource.loadAirplanesByIds(airplaneIds.toList).map { airplane => (airplane.id, airplane) }.toMap assignmentResultSet.beforeFirst() - + val assignments = new HashMap[Int, HashMap[Airplane, LinkAssignment]]() while (assignmentResultSet.next()) { val link = assignmentResultSet.getInt("link") @@ -269,18 +269,18 @@ object LinkSource { assignments.put(linkId, HashMap.empty) } } - + assignmentResultSet.close() linkAssignmentStatement.close() - + val assignedPlanesByLinkId = assignments.toList.map { case (linkId, mutableMap) => (linkId, mutableMap.toMap) }.toMap - + assignedPlanesByLinkId } } - + def loadFlightLinkById(linkId : Int, loadDetails : Map[DetailType.Value, Boolean] = FULL_LOAD) : Option[Link] = { val result = loadFlightLinksByCriteria(List(("id", linkId)), loadDetails) if (result.isEmpty) { @@ -505,7 +505,7 @@ object LinkSource { } preparedStatement.setTimestamp(13, new java.sql.Timestamp(new Date().getTime())) preparedStatement.setInt(14, link.id) - + val updateCount = preparedStatement.executeUpdate() println("Updated " + updateCount + " link!") @@ -564,7 +564,7 @@ object LinkSource { } } } - + preparedStatement.executeBatch() ChangeHistorySource.saveLinkChanges(changeEntries.toList) @@ -573,7 +573,7 @@ object LinkSource { preparedStatement.close() connection.close() } - + } def hasChange(existingLink : Transport, newLink : Transport) : Boolean = { @@ -584,13 +584,13 @@ object LinkSource { newLink.price.businessVal != existingLink.price.businessVal || newLink.price.firstVal != existingLink.price.firstVal } - - + + def updateAssignedPlanes(linkId : Int, assignedAirplanes : Map[Airplane, LinkAssignment]) = { val connection = Meta.getConnection() try { connection.setAutoCommit(false) - + //remove all the existing ones assigned to this link val removeStatement = connection.prepareStatement("DELETE FROM " + LINK_ASSIGNMENT_TABLE + " WHERE link = ?") removeStatement.setInt(1, linkId) @@ -608,8 +608,8 @@ object LinkSource { insertStatement.executeUpdate() insertStatement.close } - } - + } + connection.commit() } finally { connection.close() @@ -656,7 +656,7 @@ object LinkSource { def deleteLink(linkId : Int) = { deleteLinksByCriteria(List(("id", linkId))) } - + def deleteAllLinks() = { deleteLinksByCriteria(List.empty) } @@ -664,7 +664,7 @@ object LinkSource { def deleteLinksByAirlineId(airlineId : Int) = { deleteLinksByCriteria(List(("airline", airlineId))) } - + def deleteLinksByCriteria(criteria : List[(String, Any)]) = { //open the hsqldb val connection = Meta.getConnection() @@ -672,7 +672,7 @@ object LinkSource { val purgingLinks = loadLinksByCriteria(criteria, Map(DetailType.AIRLINE -> true, DetailType.AIRPORT -> false, DetailType.AIRPLANE -> false)).map(link => (link.id, link)).toMap var queryString = "DELETE FROM link " - + if (!criteria.isEmpty) { queryString += " WHERE " for (i <- 0 until criteria.size - 1) { @@ -680,15 +680,15 @@ object LinkSource { } queryString += criteria.last._1 + " = ?" } - + val preparedStatement = connection.prepareStatement(queryString) - + for (i <- 0 until criteria.size) { preparedStatement.setObject(i + 1, criteria(i)._2) } val deletedCount = preparedStatement.executeUpdate() - + preparedStatement.close() println("Deleted " + deletedCount + " link records") @@ -764,7 +764,7 @@ object LinkSource { entry } - + def saveLinkConsumptions(linkConsumptions: List[LinkConsumptionDetails]) = { //open the hsqldb val connection = Meta.getConnection() @@ -783,7 +783,7 @@ object LinkSource { preparedStatement.setInt(8, linkConsumption.link.soldSeats(ECONOMY)) preparedStatement.setInt(9, linkConsumption.link.soldSeats(BUSINESS)) preparedStatement.setInt(10, linkConsumption.link.soldSeats(FIRST)) - preparedStatement.setInt(11, linkConsumption.link.computedQuality) + preparedStatement.setInt(11, linkConsumption.link.computedQuality()) preparedStatement.setInt(12, linkConsumption.fuelCost) preparedStatement.setInt(13, linkConsumption.crewCost) preparedStatement.setInt(14, linkConsumption.airportFees) @@ -831,10 +831,10 @@ object LinkSource { val latestCycleStatement = connection.prepareStatement("SELECT MAX(cycle) FROM " + LINK_CONSUMPTION_TABLE) val resultSet = latestCycleStatement.executeQuery() val latestCycle = if (resultSet.next()) { resultSet.getInt(1) } else 0 - latestCycleStatement.close() - - val deleteFrom = if (latestCycle - cyclesFromLatest < 0) 0 else latestCycle - cyclesFromLatest - + latestCycleStatement.close() + + val deleteFrom = if (latestCycle - cyclesFromLatest < 0) 0 else latestCycle - cyclesFromLatest + val deleteStatement = connection.prepareStatement("DELETE FROM " + LINK_CONSUMPTION_TABLE + " WHERE cycle <= ?") deleteStatement.setInt(1, deleteFrom) val deletedConsumption = deleteStatement.executeUpdate() @@ -844,17 +844,17 @@ object LinkSource { connection.close() } } - + def loadLinkConsumptions(cycleCount : Int = 1) = { loadLinkConsumptionsByCriteria(List.empty, cycleCount) } - + def loadLinkConsumptionsByLinkId(linkId : Int, cycleCount : Int = 1) = { loadLinkConsumptionsByCriteria(List(("link", linkId)), cycleCount) } - + def loadLinkConsumptionsByLinksId(linkIds : List[Int], cycleCount : Int = 1) = { - + if (linkIds.isEmpty) { List.empty } else { @@ -862,19 +862,19 @@ object LinkSource { for (i <- 0 until linkIds.size - 1) { queryString.append("?,") } - + queryString.append("?)") loadLinkConsumptionsByQuery(queryString.toString(), linkIds, cycleCount) } } - + def loadLinkConsumptionsByAirline(airlineId : Int, cycleCount : Int = 1) = { loadLinkConsumptionsByCriteria(List(("airline", airlineId)), cycleCount) } - + def loadLinkConsumptionsByCriteria(criteria : List[(String, Any)], cycleCount : Int) = { - var queryString = "SELECT * FROM link_consumption WHERE cycle > ?" - + var queryString = "SELECT * FROM link_consumption WHERE cycle > ?" + if (!criteria.isEmpty) { queryString += " AND " for (i <- 0 until criteria.size - 1) { @@ -883,30 +883,30 @@ object LinkSource { queryString += criteria.last._1 + " = ?" } queryString += " ORDER BY cycle DESC" - + loadLinkConsumptionsByQuery(queryString, criteria.map(_._2), cycleCount) } - + def loadLinkConsumptionsByQuery(queryString: String, parameters : List[Any], cycleCount : Int) = { val connection = Meta.getConnection() - + try { val latestCycleStatement = connection.prepareStatement("SELECT MAX(cycle) FROM " + LINK_CONSUMPTION_TABLE) val latestCycleResultSet = latestCycleStatement.executeQuery() val latestCycle = if (latestCycleResultSet.next()) { latestCycleResultSet.getInt(1) } else 0 latestCycleStatement.close() - + val preparedStatement = connection.prepareStatement(queryString) - + preparedStatement.setInt(1, latestCycle - cycleCount) for (i <- 0 until parameters.size) { preparedStatement.setObject(i + 2, parameters(i)) } - + val resultSet = preparedStatement.executeQuery() - + val linkConsumptions = new ListBuffer[LinkConsumptionDetails]() - + resultSet.beforeFirst() while (resultSet.next()) { val linkId = resultSet.getInt("link") diff --git a/airline-data/src/main/scala/com/patson/model/FlightPreference.scala b/airline-data/src/main/scala/com/patson/model/FlightPreference.scala index 5e64b2561..df43c5886 100644 --- a/airline-data/src/main/scala/com/patson/model/FlightPreference.scala +++ b/airline-data/src/main/scala/com/patson/model/FlightPreference.scala @@ -8,8 +8,8 @@ import com.patson.Util import java.util.concurrent.ThreadLocalRandom /** * Flight preference has a computeCost method that convert the existing price of a link to a "perceived price". The "perceived price" will be refer to "cost" here - * - * When a link contains certain properties that the "Flight preference" likes/hates, it might reduce (if like) or increase (if hate) the "perceived price" + * + * When a link contains certain properties that the "Flight preference" likes/hates, it might reduce (if like) or increase (if hate) the "perceived price" */ abstract class FlightPreference(homeAirport : Airport) { def computeCost(baseCost : Double, link : Transport, linkClass : LinkClass) : Double @@ -127,7 +127,7 @@ abstract class FlightPreference(homeAirport : Airport) { def qualityAdjustRatio(homeAirport : Airport, link : Transport, linkClass : LinkClass) : Double = { val qualityExpectation = homeAirport.expectedQuality(link.flightType, linkClass) - val qualityDelta = link.computedQuality - qualityExpectation + val qualityDelta = link.computedQuality() - qualityExpectation val GOOD_QUALITY_DELTA = 20 val priceAdjust = if (qualityDelta < 0) { @@ -185,8 +185,8 @@ abstract class FlightPreference(homeAirport : Airport) { if (loungeSensitivity == 0 || linkClass.level < BUSINESS.level) { 1.0 } else { - val fromLounge = link.from.getLounge(link.airline.id, link.airline.getAllianceId, activeOnly = true) - val toLounge = link.to.getLounge(link.airline.id, link.airline.getAllianceId, activeOnly = true) + val fromLounge = link.from.getLounge(link.airline.id, link.airline.getAllianceId(), activeOnly = true) + val toLounge = link.to.getLounge(link.airline.id, link.airline.getAllianceId(), activeOnly = true) val fromLoungeLevel = fromLounge.map(_.level).getOrElse(0) val toLoungeLevel = toLounge.map(_.level).getOrElse(0) @@ -224,39 +224,39 @@ abstract class FlightPreference(homeAirport : Airport) { object FlightPreferenceType extends Enumeration { type FlightType = Value - - - protected case class Val(title : String, description : String) extends super.Val { - - } - implicit def valueToFlightPreferenceTypeVal(x: Value) = x.asInstanceOf[Val] - - val BUDGET = Val("Budget", "") + + + protected case class Val(title : String, description : String) extends super.Val { + + } + implicit def valueToFlightPreferenceTypeVal(x: Value) = x.asInstanceOf[Val] + + val BUDGET = Val("Budget", "") val SIMPLE = Val("Carefree", "") val SPEED = Val("Swift", "") - val APPEAL = Val("Comprehensive", "") + val APPEAL = Val("Comprehensive", "") val LOYAL = Val("Brand Conscious", "") - val ELITE = Val("Elite", "") + val ELITE = Val("Elite", "") } import FlightPreferenceType._ /** * priceSensitivity : how sensitive to the price, base value is 1 (100%) - * + * * 1 : cost is the same as price no adjustment - * > 1 : more sensitive to price, a price that is deviated from "standard price" will have its effect amplified, for example a 2 (200%) would mean a $$150 ticket with suggested price of $$100, will be perceived as $$200 + * > 1 : more sensitive to price, a price that is deviated from "standard price" will have its effect amplified, for example a 2 (200%) would mean a $$150 ticket with suggested price of $$100, will be perceived as $$200 * < 1 : less sensitive to price, a price that is deviated from "standard price" will have its effect weakened, for example a 0.5 (50%) would mean a $$150 ticket with suggested price of $$100, will be perceived as $$125 - * + * * Take note that 0 would means a preference that totally ignore the price difference (could be dangerous as very expensive ticket will get through) */ case class SimplePreference(homeAirport : Airport, priceSensitivity : Double, preferredLinkClass: LinkClass) extends FlightPreference(homeAirport : Airport) { def computeCost(baseCost : Double, link : Transport, linkClass : LinkClass) = { val noise = 0.8 + getFlatTopBellRandom(0.2, 0.1) - + val finalCost = baseCost * noise if (finalCost >= 0) { - finalCost + finalCost } else { //just to play safe - do NOT allow negative cost link 0 } @@ -277,9 +277,9 @@ case class SimplePreference(homeAirport : Airport, priceSensitivity : Double, pr SIMPLE } } - + override val connectionCostRatio = 0.5 //more okay with taking connection - + def isApplicable(fromAirport : Airport, toAirport : Airport) : Boolean = true } @@ -296,16 +296,16 @@ case class SpeedPreference(homeAirport : Airport, preferredLinkClass: LinkClass) //NOISE? val finalCost = baseCost * noise - - finalCost + + finalCost } - + val getPreferenceType = { SPEED } - + def isApplicable(fromAirport : Airport, toAirport : Airport) : Boolean = true - + override val connectionCostRatio = 2.0 } @@ -337,9 +337,9 @@ case class AppealPreference(homeAirport : Airport, preferredLinkClass : LinkClas //NOISE? val finalCost = perceivedPrice * noise - + if (finalCost >= 0) { - return finalCost + return finalCost } else { //just to play safe - do NOT allow negative cost link return 0 } @@ -356,7 +356,7 @@ case class AppealPreference(homeAirport : Airport, preferredLinkClass : LinkClas } } } - + def isApplicable(fromAirport : Airport, toAirport : Airport) : Boolean = { if (loungeLevelRequired > 0) { fromAirport.size >= Lounge.LOUNGE_PASSENGER_AIRPORT_SIZE_REQUIREMENT && toAirport.size >= Lounge.LOUNGE_PASSENGER_AIRPORT_SIZE_REQUIREMENT @@ -372,7 +372,7 @@ object AppealPreference { count += 1 AppealPreference(homeAirport, linkClass, loungeLevelRequired = loungeLevelRequired, loyaltyRatio = loyaltyRatio, count) } - + } @@ -399,25 +399,21 @@ class FlightPreferencePool(preferencesWithWeight : List[(FlightPreference, Int)] }.view.mapValues { _.flatMap { case (flightPreference, weight) => (0 until weight).foldRight(List[FlightPreference]()) { (_, foldList) => - flightPreference :: foldList + flightPreference :: foldList } } }.toMap - - - + + + // val pool = preferencesWithWeight.foldRight(List[FlightPreference]()) { -// (entry, foldList) => +// (entry, foldList) => // Range(0, entry._2, 1).foldRight(List[FlightPreference]())((_, childFoldList) => entry._1 :: childFoldList) ::: foldList // } - + def draw(linkClass: LinkClass, fromAirport : Airport, toAirport : Airport) : FlightPreference = { //Random.shuffle(pool).apply(0) val poolForClass = pool(linkClass).filter(_.isApplicable(fromAirport, toAirport)) poolForClass(ThreadLocalRandom.current().nextInt(poolForClass.length)) } } - - - - diff --git a/airline-rest/app/controllers/Application.scala b/airline-rest/app/controllers/Application.scala index 4db4a5ce4..dc6f3c761 100644 --- a/airline-rest/app/controllers/Application.scala +++ b/airline-rest/app/controllers/Application.scala @@ -22,17 +22,17 @@ object Application extends Controller { val airport = Airport.fromId((json \ "id").as[Int]) JsSuccess(airport) } - + def writes(airport: Airport): JsValue = { val averageIncome = airport.power / airport.population val incomeLevel = (Math.log(averageIncome / 1000) / Math.log(1.1)).toInt -// val appealMap = airport.airlineAppeals.foldRight(Map[Airline, Int]()) { -// case(Tuple2(airline, appeal), foldMap) => foldMap + Tuple2(airline, appeal.loyalty) +// val appealMap = airport.airlineAppeals.foldRight(Map[Airline, Int]()) { +// case(Tuple2(airline, appeal), foldMap) => foldMap + Tuple2(airline, appeal.loyalty) // } -// val awarenessMap = airport.airlineAppeals.foldRight(Map[Airline, Int]()) { -// case(Tuple2(airline, appeal), foldMap) => foldMap + Tuple2(airline, appeal.awareness) +// val awarenessMap = airport.airlineAppeals.foldRight(Map[Airline, Int]()) { +// case(Tuple2(airline, appeal), foldMap) => foldMap + Tuple2(airline, appeal.awareness) // } - + var airportObject = JsObject(List( "id" -> JsNumber(airport.id), "name" -> JsString(airport.name), @@ -46,25 +46,25 @@ object Application extends Controller { "slots" -> JsNumber(airport.slots), "availableSlots" -> JsNumber(airport.availableSlots), "incomeLevel" -> JsNumber(if (incomeLevel < 0) 0 else incomeLevel))) - - + + if (airport.isSlotAssignmentsInitialized) { - airportObject = airportObject + ("slotAssignmentList" -> JsArray(airport.getAirlineSlotAssignments().toList.map { + airportObject = airportObject + ("slotAssignmentList" -> JsArray(airport.getAirlineSlotAssignments().toList.map { case (airlineId, slotAssignment) => Json.obj("airlineId" -> airlineId, "airlineName" -> AirlineSource.loadAirlineById(airlineId).fold("")(_.name), "slotAssignment" -> slotAssignment) } )) } if (airport.isAirlineAppealsInitialized) { - airportObject = airportObject + ("appealList" -> JsArray(airport.getAirlineAppeals().toList.map { + airportObject = airportObject + ("appealList" -> JsArray(airport.getAirlineAppeals().toList.map { case (airlineId, appeal) => Json.obj("airlineId" -> airlineId, "airlineName" -> AirlineSource.loadAirlineById(airlineId).fold("")(_.name), "loyalty" -> BigDecimal(appeal.loyalty).setScale(2, BigDecimal.RoundingMode.HALF_EVEN), "awareness" -> BigDecimal(appeal.awareness).setScale(2, BigDecimal.RoundingMode.HALF_EVEN)) } )) } - + airportObject } } - + case class AirportSlotData(airlineId: Int, slotCount: Int) val airportSlotForm = Form( mapping( @@ -72,7 +72,7 @@ object Application extends Controller { "slotCount" -> number )(AirportSlotData.apply)(AirportSlotData.unapply) ) - + def getAirports(count : Int) = Action { val airports = AirportSource.loadAllAirports() val selectedAirports = airports.takeRight(count) @@ -80,7 +80,7 @@ object Application extends Controller { ACCESS_CONTROL_ALLOW_ORIGIN -> "*" ) } - + def getAirport(airportId : Int) = Action { AirportSource.loadAirportById(airportId, true) match { case Some(airport) => Ok(Json.toJson(airport)).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") @@ -88,30 +88,30 @@ object Application extends Controller { } } def getAirportSlots(airportId : Int, airlineId : Int) = Action { - AirportSource.loadAirportById(airportId, true) match { - case Some(airport) => + AirportSource.loadAirportById(airportId, true) match { + case Some(airport) => val maxSlots = airport.getMaxSlotAssignment(airlineId) val assignedSlots = airport.getAirlineSlotAssignment(airlineId) Ok(Json.obj("assignedSlots" -> JsNumber(assignedSlots), "maxSlots" -> JsNumber(maxSlots))).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") case None => NotFound.withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } } - + def postAirportSlots(airportId : Int) = Action { implicit request => - AirportSource.loadAirportById(airportId, true) match { - case Some(airport) => - val AirportSlotData(airlineId, slotCount) = airportSlotForm.bindFromRequest.get + AirportSource.loadAirportById(airportId, true) match { + case Some(airport) => + val AirportSlotData(airlineId, slotCount) = airportSlotForm.bindFromRequest().get try { airport.setAirlineSlotAssignment(airlineId, slotCount) Ok(Json.toJson(airport)).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } catch { - case e:IllegalArgumentException => + case e:IllegalArgumentException => BadRequest("Not allowed to allocate this amount of slots").withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } case None => NotFound.withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } } - + def options(path: String) = Action { Ok("").withHeaders( "Access-Control-Allow-Origin" -> "*", @@ -122,5 +122,5 @@ object Application extends Controller { ) } - case class LinkInfo(fromId : Int, toId : Int, price : Double, capacity : Int) + case class LinkInfo(fromId : Int, toId : Int, price : Double, capacity : Int) } diff --git a/airline-rest/app/controllers/LinkApplication.scala b/airline-rest/app/controllers/LinkApplication.scala index 7d12f0463..85be7863b 100644 --- a/airline-rest/app/controllers/LinkApplication.scala +++ b/airline-rest/app/controllers/LinkApplication.scala @@ -40,14 +40,14 @@ object LinkApplication extends Controller { val airline = AirlineSource.loadAirlineById(airlineId).get val distance = Util.calculateDistance(fromAirport.latitude, fromAirport.longitude, toAirport.latitude, toAirport.longitude) val rawQuality = json.\("quality").as[Int] - + val link = Link(fromAirport, toAirport, airline, price, distance.toInt, capacity, rawQuality, distance.toInt * 60 / 800, 1) - (json \ "id").asOpt[Int].foreach { link.id = _ } + (json \ "id").asOpt[Int].foreach { link.id = _ } JsSuccess(link) } } - - + + implicit object LinkFormat extends Format[Link] { def reads(json: JsValue): JsResult[Link] = { val fromAirportId = json.\("fromAirportId").as[Int] @@ -62,16 +62,16 @@ object LinkApplication extends Controller { val airplaneIds = json.\("airplanes").as[List[Int]] val frequency = json.\("frequency").as[Int] val modelId = json.\("model").as[Int] - val capacity = frequency * ModelSource.loadModelById(modelId).fold(0)(_.capacity) + val capacity = frequency * ModelSource.loadModelById(modelId).fold(0)(_.capacity) val duration = ModelSource.loadModelById(modelId).fold(Integer.MAX_VALUE)(Computation.calculateDuration(_, distance)) - + val airplanes = airplaneIds.foldRight(List[Airplane]()) { (airplaneId, foldList) => - AirplaneSource.loadAirplanesWithAssignedLinkByAirplaneId(airplaneId) match { + AirplaneSource.loadAirplanesWithAssignedLinkByAirplaneId(airplaneId) match { case Some((airplane, Some(link))) if (link.from.id != fromAirport.id || link.to.id != toAirport.id) => throw new IllegalStateException("Airplane with id " + airplaneId + " is assigned to other link") case Some((airplane, _)) => airplane :: foldList - case None => + case None => throw new IllegalStateException("Airplane with id " + airplaneId + " does not exist") } } @@ -81,13 +81,13 @@ object LinkApplication extends Controller { } else if (rawQuality < 0) { rawQuality = 0 } - + val link = Link(fromAirport, toAirport, airline, price, distance, capacity, rawQuality, duration, frequency) link.setAssignedAirplanes(airplanes) - (json \ "id").asOpt[Int].foreach { link.id = _ } + (json \ "id").asOpt[Int].foreach { link.id = _ } JsSuccess(link) } - + def writes(link: Link): JsValue = JsObject(List( "id" -> JsNumber(link.id), "fromAirportId" -> JsNumber(link.from.id), @@ -103,7 +103,7 @@ object LinkApplication extends Controller { "distance" -> JsNumber(link.distance), "capacity" -> JsNumber(link.capacity), "rawQuality" -> JsNumber(link.rawQuality), - "computedQuality" -> JsNumber(link.computedQuality), + "computedQuality" -> JsNumber(link.computedQuality()), "duration" -> JsNumber(link.duration), "frequency" -> JsNumber(link.frequency), "availableSeat" -> JsNumber(link.availableSeats), @@ -112,7 +112,7 @@ object LinkApplication extends Controller { "toLatitude" -> JsNumber(link.to.latitude), "toLongitude" -> JsNumber(link.to.longitude))) } - + implicit object LinkConsumptionFormat extends Writes[LinkConsumptionDetails] { def writes(linkConsumption: LinkConsumptionDetails): JsValue = { val fromAirport = AirportSource.loadAirportById(linkConsumption.fromAirportId) @@ -130,37 +130,37 @@ object LinkApplication extends Controller { "profit" -> JsNumber(linkConsumption.profit), "capacity" -> JsNumber(linkConsumption.capacity), "soldSeats" -> JsNumber(linkConsumption.soldSeats))) - + } } - + implicit object ModelPlanLinkInfoWrites extends Writes[ModelPlanLinkInfo] { def writes(modelPlanLinkInfo : ModelPlanLinkInfo): JsValue = { val jsObject = JsObject(List( - "modelId" -> JsNumber(modelPlanLinkInfo.model.id), + "modelId" -> JsNumber(modelPlanLinkInfo.model.id), "modelName" -> JsString(modelPlanLinkInfo.model.name), - "duration" -> JsNumber(modelPlanLinkInfo.duration), + "duration" -> JsNumber(modelPlanLinkInfo.duration), "maxFrequency" -> JsNumber(modelPlanLinkInfo.maxFrequency), "isAssigned" -> JsBoolean(modelPlanLinkInfo.isAssigned))) - + var airplaneArray = JsArray() modelPlanLinkInfo.airplanes.foreach { - case(airplane, isAssigned) => + case(airplane, isAssigned) => airplaneArray = airplaneArray.append(JsObject(List("airplaneId" -> JsNumber(airplane.id), "isAssigned" -> JsBoolean(isAssigned)))) } jsObject + ("airplanes" -> airplaneArray) } - + } - + implicit object LinkWithProfitWrites extends Writes[(Link, Int)] { - def writes(linkWithProfit: (Link, Int)): JsValue = { + def writes(linkWithProfit: (Link, Int)): JsValue = { val link = linkWithProfit._1 val profit = linkWithProfit._2 Json.toJson(link).asInstanceOf[JsObject] + ("profit" -> JsNumber(profit)) } } - + case class PlanLinkData(airlineId: Int, fromAirportId: Int, toAirportId: Int) val planLinkForm = Form( mapping( @@ -169,53 +169,53 @@ object LinkApplication extends Controller { "toAirportId" -> number )(PlanLinkData.apply)(PlanLinkData.unapply) ) - + def addTestLink() = Action { request => if (request.body.isInstanceOf[AnyContentAsJson]) { val newLink = request.body.asInstanceOf[AnyContentAsJson].json.as[Link](TestLinkReads) println("PUT (test)" + newLink) - + LinkSource.saveLink(newLink) match { case Some(link) => - Created(Json.toJson(link)).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") + Created(Json.toJson(link)).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") case None => UnprocessableEntity("Cannot insert link").withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } } else { BadRequest("Cannot insert link").withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } } - + def addLinkBlock(request : Request[AnyContent]) : Result = { if (request.body.isInstanceOf[AnyContentAsJson]) { val incomingLink = request.body.asInstanceOf[AnyContentAsJson].json.as[Link] - if (incomingLink.getAssignedAirplanes.isEmpty) { + if (incomingLink.getAssignedAirplanes().isEmpty) { return BadRequest("Cannot insert link - no airplane assigned").withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } - + val links = LinkSource.loadLinksByCriteria(List(("airline", incomingLink.airline.id), ("from_airport", incomingLink.from.id)), true).filter { _.to.id == incomingLink.to.id } if (!links.isEmpty) { incomingLink.id = links(0).id } - + val isNewLink = links.isEmpty - + //validate frequency by duration val maxFrequency = Computation.calculateMaxFrequency(incomingLink.duration) if (maxFrequency * incomingLink.getAssignedAirplanes().size < incomingLink.frequency) { //TODO log error! println("max frequecny exceeded, max " + maxFrequency + " found " + incomingLink.frequency) - return BadRequest("Cannot insert link - frequency exceeded limit").withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") + return BadRequest("Cannot insert link - frequency exceeded limit").withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } //TODO validate slot on airport ....probably rethink how to simplify all these calculation! - - val airplanesForThisLink = incomingLink.getAssignedAirplanes + + val airplanesForThisLink = incomingLink.getAssignedAirplanes() //validate all airplanes are same model val airplaneModels = airplanesForThisLink.foldLeft(Set[Model]())(_ + _.model) //should be just one element if (airplaneModels.size != 1) { return BadRequest("Cannot insert link - not all airplanes are same model").withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } - + //check if the assigned planes are either previously unassigned or assigned to this link - val occupiedAirplanes = airplanesForThisLink.flatMap { airplaneForThisLink => + val occupiedAirplanes = airplanesForThisLink.flatMap { airplaneForThisLink => val assignedLink = AirplaneSource.loadAirplanesWithAssignedLinkByAirplaneId(airplaneForThisLink.id).get._2 if (assignedLink.isDefined && assignedLink.get.id != incomingLink.id) { List(airplaneForThisLink) @@ -223,21 +223,21 @@ object LinkApplication extends Controller { List.empty } } - + if (!occupiedAirplanes.isEmpty) { return BadRequest("Cannot insert link - some airplanes already occupied " + occupiedAirplanes).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } - + println("PUT " + incomingLink) - + if (isNewLink) { LinkSource.saveLink(incomingLink) match { - case Some(link) => Created(Json.toJson(link)).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") + case Some(link) => Created(Json.toJson(link)).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") case None => UnprocessableEntity("Cannot insert link").withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } } else { LinkSource.updateLink(incomingLink) match { - case 1 => Accepted(Json.toJson(incomingLink)).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") + case 1 => Accepted(Json.toJson(incomingLink)).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") case _ => UnprocessableEntity("Cannot update link").withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } } @@ -245,23 +245,23 @@ object LinkApplication extends Controller { BadRequest("Cannot put link").withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } } - + def addLink() = Action { request => addLinkBlock(request) } - + def getLink(linkId : Int) = Action { request => val link = LinkSource.loadLinkById(linkId) Ok(Json.toJson(link)).withHeaders( ACCESS_CONTROL_ALLOW_ORIGIN -> "*" ) } - + def getAllLinks() = Action { val links = LinkSource.loadAllLinks() Ok(Json.toJson(links)).withHeaders( ACCESS_CONTROL_ALLOW_ORIGIN -> "*" ) } - + def getLinks(airlineId : Int, getProfit : Boolean) = Action { val links = LinkSource.loadLinksByAirlineId(airlineId) if (!getProfit) { @@ -272,35 +272,35 @@ object LinkApplication extends Controller { val consumptions = LinkSource.loadLinkConsumptionsByAirline(airlineId).foldLeft(Map[Int, LinkConsumptionDetails]()) { (foldMap, linkConsumptionDetails) => foldMap + (linkConsumptionDetails.linkId -> linkConsumptionDetails) } - val linksWithProfit = links.map { link => - (link, consumptions.get(link.id).fold(0)(_.profit)) + val linksWithProfit = links.map { link => + (link, consumptions.get(link.id).fold(0)(_.profit)) } Ok(Json.toJson(linksWithProfit)).withHeaders( ACCESS_CONTROL_ALLOW_ORIGIN -> "*" ) } - + } - + def deleteAllLinks() = Action { val count = LinkSource.deleteAllLinks() Ok(Json.obj("count" -> count)).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } - + def deleteLink(linkId: Int) = Action { - val count = LinkSource.deleteLink(linkId) + val count = LinkSource.deleteLink(linkId) Ok(Json.obj("count" -> count)).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } - + def getLinkConsumption(linkId : Int) = Action { - val linkConsumptions = LinkSource.loadLinkConsumptionsByLinkId(linkId) + val linkConsumptions = LinkSource.loadLinkConsumptionsByLinkId(linkId) if (linkConsumptions.isEmpty) { - Ok(Json.obj()).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") + Ok(Json.obj()).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } else { Ok(Json.toJson(linkConsumptions(0))).withHeaders(ACCESS_CONTROL_ALLOW_ORIGIN -> "*") } } - + def getAllLinkConsumptions() = Action { val linkConsumptions = LinkSource.loadLinkConsumptions() Ok(Json.toJson(linkConsumptions)).withHeaders( @@ -309,22 +309,22 @@ object LinkApplication extends Controller { } - + def planLink() = Action { implicit request => - val PlanLinkData(airlineId, fromAirportId, toAirportId) = planLinkForm.bindFromRequest.get + val PlanLinkData(airlineId, fromAirportId, toAirportId) = planLinkForm.bindFromRequest().get AirportSource.loadAirportById(fromAirportId, true) match { case Some(fromAirport) => AirportSource.loadAirportById(toAirportId, true) match { case Some(toAirport) => - var existingLink : Option[Link] = None - + var existingLink : Option[Link] = None + val distance = Util.calculateDistance(fromAirport.latitude, fromAirport.longitude, toAirport.latitude, toAirport.longitude).toInt val (maxFrequencyFromAirport, maxFrequencyToAirport) = getMaxFrequencyByAirports(fromAirport, toAirport, Airline.fromId(airlineId)) - + val airplanesWithAssignedLinks : List[(Airplane, Option[Link])] = AirplaneSource.loadAirplanesWithAssignedLinkByOwner(airlineId) val freeAirplanes = airplanesWithAssignedLinks.filter { case (_ , Some(_)) => false - case (airplane, None) => + case (airplane, None) => airplane.model.range >= distance }.map(_._1) val assignedToThisLinkAirplanes = airplanesWithAssignedLinks.filter { @@ -332,41 +332,41 @@ object LinkApplication extends Controller { existingLink = Some(link) true case _ => false - }.map(_._1) - + }.map(_._1) + //group airplanes by model, also add boolean to indicated whether the airplane is assigned to this link val availableAirplanesByModel = Map[Model, ListBuffer[(Airplane, Boolean)]]() var assignedModel : Option[Model] = existingLink match { case Some(link) => link.getAssignedModel() case None => None } - - freeAirplanes.foreach { freeAirplane => - availableAirplanesByModel.getOrElseUpdate(freeAirplane.model, ListBuffer[(Airplane, Boolean)]()).append((freeAirplane, false)) + + freeAirplanes.foreach { freeAirplane => + availableAirplanesByModel.getOrElseUpdate(freeAirplane.model, ListBuffer[(Airplane, Boolean)]()).append((freeAirplane, false)) } - assignedToThisLinkAirplanes.foreach { assignedAirplane => + assignedToThisLinkAirplanes.foreach { assignedAirplane => availableAirplanesByModel.getOrElseUpdate(assignedAirplane.model, ListBuffer[(Airplane, Boolean)]()).append((assignedAirplane, true)) } val planLinkInfoByModel = ListBuffer[ModelPlanLinkInfo]() - - availableAirplanesByModel.foreach { - case(model, airplaneList) => + + availableAirplanesByModel.foreach { + case(model, airplaneList) => val duration = Computation.calculateDuration(model, distance) - val existingSlotsUsedByThisModel= if (assignedModel.isDefined && assignedModel.get.id == model.id) { existingLink.get.frequency } else { 0 } + val existingSlotsUsedByThisModel= if (assignedModel.isDefined && assignedModel.get.id == model.id) { existingLink.get.frequency } else { 0 } val maxFrequencyByModel : Int = Computation.calculateMaxFrequency(duration) - - planLinkInfoByModel.append(ModelPlanLinkInfo(model, duration, maxFrequencyByModel, assignedModel.isDefined && assignedModel.get.id == model.id, airplaneList.toList)) + + planLinkInfoByModel.append(ModelPlanLinkInfo(model, duration, maxFrequencyByModel, assignedModel.isDefined && assignedModel.get.id == model.id, airplaneList.toList)) } - - var resultObject = Json.obj("distance" -> distance, - "suggestedPrice" -> Pricing.computeStandardPrice(distance), - "maxFrequencyFromAirport" -> maxFrequencyFromAirport, + + var resultObject = Json.obj("distance" -> distance, + "suggestedPrice" -> Pricing.computeStandardPrice(distance), + "maxFrequencyFromAirport" -> maxFrequencyFromAirport, "maxFrequencyToAirport" -> maxFrequencyToAirport) + ("modelPlanLinkInfo", Json.toJson(planLinkInfoByModel.toList)) - + if (existingLink.isDefined) { resultObject = resultObject + ("existingLink", Json.toJson(existingLink)) } - + Ok(resultObject).withHeaders( ACCESS_CONTROL_ALLOW_ORIGIN -> "*" ) @@ -376,20 +376,20 @@ object LinkApplication extends Controller { } } - + class PlanLinkResult(distance : Double, availableAirplanes : List[Airplane]) //case class AirplaneWithPlanRouteInfo(airplane : Airplane, duration : Int, maxFrequency : Int, limitingFactor : String, isAssigned : Boolean) case class ModelPlanLinkInfo(model: Model, duration : Int, maxFrequency : Int, isAssigned : Boolean, airplanes : List[(Airplane, Boolean)]) - + private def getMaxFrequencyByAirports(fromAirport : Airport, toAirport : Airport, airline : Airline) : (Int, Int) = { val airlineId = airline.id val links = LinkSource.loadLinksByCriteria(List(("airline", airlineId), ("from_airport", fromAirport.id)), true).filter { _.to.id == toAirport.id } val existingLink : Option[Link] = if (links.size == 1) Some(links(0)) else None - + val existingSlotsByThisLink = existingLink.fold(0)(_.frequency) - val maxFrequencyFromAirport : Int = fromAirport.getMaxSlotAssignment(airlineId) - fromAirport.getAirlineSlotAssignment(airlineId) + existingSlotsByThisLink + val maxFrequencyFromAirport : Int = fromAirport.getMaxSlotAssignment(airlineId) - fromAirport.getAirlineSlotAssignment(airlineId) + existingSlotsByThisLink val maxFrequencyToAirport : Int = toAirport.getMaxSlotAssignment(airlineId) - toAirport.getAirlineSlotAssignment(airlineId) + existingSlotsByThisLink - + (maxFrequencyFromAirport, maxFrequencyToAirport) } } diff --git a/airline-web/app/controllers/AccountApplication.scala b/airline-web/app/controllers/AccountApplication.scala index 19618fefb..84507d8db 100644 --- a/airline-web/app/controllers/AccountApplication.scala +++ b/airline-web/app/controllers/AccountApplication.scala @@ -31,7 +31,7 @@ class AccountApplication @Inject()(cc: ControllerComponents) extends AbstractCon * validation, submission, errors, redisplaying, ... */ val form: Form[PasswordReset] = Form( - + // Define a mapping that will handle User values mapping( "resetToken" -> text, @@ -47,20 +47,20 @@ class AccountApplication @Inject()(cc: ControllerComponents) extends AbstractCon // so we have to define custom binding/unbinding functions { // Binding: Create a User from the mapping result (ignore the second password and the accept field) - (token, passwords) => PasswordReset(token, passwords._1) - } + (token, passwords) => PasswordReset(token, passwords._1) + } { // Unbinding: Create the mapping values from an existing User value passwordReset => Some(passwordReset.token, (passwordReset.password, "")) } ) - + val forgotIdForm : Form[ForgotId] = Form( - + // Define a mapping that will handle User values mapping( "email" -> text/*.verifying( - "Email address is not found", + "Email address is not found", email => !(UserSource.loadUsersByCriteria(List(("email", email))).isEmpty) )*/ ) @@ -68,20 +68,20 @@ class AccountApplication @Inject()(cc: ControllerComponents) extends AbstractCon // so we have to define custom binding/unbinding functions { // Binding: Create a User from the mapping result (ignore the second password and the accept field) - (email) => ForgotId(email) - } + (email) => ForgotId(email) + } { // Unbinding: Create the mapping values from an existing User value forgotId => Some(forgotId.email) } ) - + val forgotPasswordForm : Form[ForgotPassword] = Form( - + // Define a mapping that will handle User values mapping( "userName" -> text.verifying( - "User Name is not found", + "User Name is not found", userName => UserSource.loadUserByUserName(userName).isDefined ), "email" -> text @@ -90,14 +90,14 @@ class AccountApplication @Inject()(cc: ControllerComponents) extends AbstractCon // so we have to define custom binding/unbinding functions { // Binding: Create a User from the mapping result (ignore the second password and the accept field) - (userName, email) => ForgotPassword(userName, email) - } + (userName, email) => ForgotPassword(userName, email) + } { // Unbinding: Create the mapping values from an existing User value forgotPassword => Some(forgotPassword.userName, forgotPassword.email) } ) - + /** * Display an empty form. */ @@ -109,35 +109,35 @@ class AccountApplication @Inject()(cc: ControllerComponents) extends AbstractCon } case None => Forbidden } - - + + } - + def forgotId() = Action { implicit request => Ok(html.forgotId(forgotIdForm.fill(ForgotId("")))) } - + def forgotPassword() = Action { implicit request => Ok(html.forgotPassword(forgotPasswordForm.fill(ForgotPassword("", "")))) } - + // def sendEmail() = Action { // EmailUtil.sendEmail("patson_luk@hotmail.com", "info@airline-club.com", "testing", "testing"); // Ok(Json.obj()) // } - - - + + + /** * Handle form submission. */ def passwordResetSubmit = Action { implicit request => - form.bindFromRequest.fold( + form.bindFromRequest().fold( // Form has errors, redisplay it errors => { println(errors) BadRequest(html.passwordReset(errors)) - }, + }, userInput => { UserSource.loadResetUser(userInput.token) match { case Some(username) => { @@ -154,17 +154,17 @@ class AccountApplication @Inject()(cc: ControllerComponents) extends AbstractCon } ) } - + /** * Handle form submission. */ def forgotIdSubmit = Action { implicit request => - forgotIdForm.bindFromRequest.fold( + forgotIdForm.bindFromRequest().fold( // Form has errors, redisplay it errors => { println(errors) BadRequest(html.forgotId(errors)) - }, + }, userInput => { val users = UserSource.loadUsersByCriteria(List(("email", userInput.email))) if (users.size > 0) { @@ -177,14 +177,14 @@ class AccountApplication @Inject()(cc: ControllerComponents) extends AbstractCon } ) } - + def forgotPasswordSubmit = Action { implicit request => - forgotPasswordForm.bindFromRequest.fold( + forgotPasswordForm.bindFromRequest().fold( // Form has errors, redisplay it errors => { println(errors) BadRequest(html.forgotPassword(errors)) - }, + }, userInput => { val user = UserSource.loadUserByUserName(userInput.userName).get if (user.email == userInput.email) { @@ -200,7 +200,7 @@ class AccountApplication @Inject()(cc: ControllerComponents) extends AbstractCon } else { println("Want to reset password for " + user.userName + " but email does not match!") } - + Ok(html.checkEmail()) } ) @@ -211,18 +211,18 @@ class AccountApplication @Inject()(cc: ControllerComponents) extends AbstractCon users.foreach { user => message ++= (user.userName + "\r\n") } - + message.toString() } - + def getResetPasswordMessage(user : User, baseUrl: String) = { val resetLink = generateResetLink(user, baseUrl) "Please follow this link \r\n" + resetLink + "\r\nto reset your password." } - + def generateResetLink(user : User, baseUrl : String) = { val resetToken = UUID.randomUUID().toString() - + UserSource.saveResetUser(user.userName, resetToken) s"$baseUrl?resetToken=" + resetToken diff --git a/airline-web/app/controllers/AirlineApplication.scala b/airline-web/app/controllers/AirlineApplication.scala index e202b3e30..57618bce4 100644 --- a/airline-web/app/controllers/AirlineApplication.scala +++ b/airline-web/app/controllers/AirlineApplication.scala @@ -37,8 +37,8 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon "airlineCode" -> JsString(airline.getAirlineCode()), "skipTutorial" -> JsBoolean(airline.isSkipTutorial), "initialized" -> JsBoolean(airline.isInitialized)) - - airline.getCountryCode.foreach { countryCode => + + airline.getCountryCode().foreach { countryCode => values = values :+ ("countryCode" -> JsString(countryCode)) } @@ -66,7 +66,7 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon } result = result + ("slogan" -> JsString(airline.slogan.getOrElse(""))) - + alliance.foreach { alliance => result = result + ("allianceName" -> JsString(alliance.name)) + ("allianceId" -> JsNumber(alliance.id)) } @@ -78,7 +78,7 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon result } } - + object ResetAmountInfoWrites extends Writes[ResetAmountInfo] { def writes(info: ResetAmountInfo): JsValue = { JsObject(List( @@ -151,18 +151,18 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon val alliances = AllianceSource.loadAllAlliances().map(alliance => (alliance.id, alliance)).toMap Ok(Json.toJson(airlinesByUser.toList.map { - case(airline, user) => (airline, user, userStatusMap.get(user), airline.getAllianceId.map(alliances(_)), airlineModifiers.getOrElse(airline.id, List.empty), request.user.isAdmin) + case(airline, user) => (airline, user, userStatusMap.get(user), airline.getAllianceId().map(alliances(_)), airlineModifiers.getOrElse(airline.id, List.empty), request.user.isAdmin) })).withHeaders( ACCESS_CONTROL_ALLOW_ORIGIN -> "http://localhost:9000", "Access-Control-Allow-Credentials" -> "true" ) } - + def getAirline(airlineId : Int, extendedInfo : Boolean) = AuthenticatedAirline(airlineId) { request => val airline = request.user var airlineJson = Json.toJson(airline)(OwnedAirlineWrites).asInstanceOf[JsObject] - AirlineSource.loadAirlineHeadquarter(airlineId).foreach { headquarter => + AirlineSource.loadAirlineHeadquarter(airlineId).foreach { headquarter => airlineJson = airlineJson + ("headquarterAirport"-> Json.toJson(headquarter)) } val bases = AirlineSource.loadAirlineBasesByAirline(airlineId) @@ -175,18 +175,18 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon ("allianceRole" -> JsString(allianceMembership.role.toString)) + ("isAllianceAdmin" -> JsBoolean(AllianceRole.isAdmin(allianceMembership.role))) } - + if (extendedInfo) { val links = LinkSource.loadFlightLinksByAirlineId(airlineId) val airportsServed = links.flatMap { link => List(link.from, link.to) }.toSet.size - + val destinations = if (airportsServed > 0) airportsServed - 1 else 0 //minus home base - + val currentCycle = CycleSource.loadCycle() val airplanes = AirplaneSource.loadAirplanesByOwner(airlineId).filter(_.isReady) - + val fleetSize = airplanes.length val fleetAge = if (fleetSize > 0) airplanes.map(currentCycle - _.constructedCycle).sum / fleetSize else 0 val assets = Bank.getAssets(airlineId) @@ -231,7 +231,7 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon val targetBase = existingBase match { case Some(base) => base.copy(scale = base.scale + 1) - case None => AirlineBase(airline = request.user, airport = airport, countryCode = airport.countryCode, scale = 1, foundedCycle = CycleSource.loadCycle(), headquarter = request.user.getHeadQuarter.isEmpty) + case None => AirlineBase(airline = request.user, airport = airport, countryCode = airport.countryCode, scale = 1, foundedCycle = CycleSource.loadCycle(), headquarter = request.user.getHeadQuarter().isEmpty) } result = result + ("targetBase" -> Json.toJson(targetBase)) @@ -261,7 +261,7 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon def getBaseRejection(airline : Airline, targetBase : AirlineBase) : Option[String] = { val airport = targetBase.airport val cost = targetBase.getValue - if (cost > airline.getBalance) { + if (cost > airline.getBalance()) { return Some("Not enough cash to build/upgrade the base") } if (targetBase.scale < 1) { @@ -333,7 +333,7 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon val cost = newLounge.getValue - lounge.getValue (cost, newLounge, inputFacility.level - lounge.level) case None => - val newLounge = Lounge(airline, airline.getAllianceId, airport, name = inputFacility.name, level = 1, LoungeStatus.ACTIVE, CycleSource.loadCycle()) + val newLounge = Lounge(airline, airline.getAllianceId(), airport, name = inputFacility.name, level = 1, LoungeStatus.ACTIVE, CycleSource.loadCycle()) val cost = newLounge.getValue (cost, newLounge, inputFacility.level) } @@ -357,7 +357,7 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon cost = 0 } - if (cost > 0 && cost > airline.getBalance) { + if (cost > 0 && cost > airline.getBalance()) { return Consideration(cost, newLounge, Some("Not enough cash to build/upgrade the lounge")) } @@ -616,7 +616,7 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon loungeJson + ("profit" -> JsNumber(profit)) case None => //Ok(Json.obj()) - Json.toJson(Lounge(airline = airline, allianceId = airline.getAllianceId, airport = airport, name = "", level = 0, status = LoungeStatus.INACTIVE, foundedCycle = 0)).asInstanceOf[JsObject] + Json.toJson(Lounge(airline = airline, allianceId = airline.getAllianceId(), airport = airport, name = "", level = 0, status = LoungeStatus.INACTIVE, foundedCycle = 0)).asInstanceOf[JsObject] } @@ -654,7 +654,7 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon case _ => BadRequest("unknown facility type : " + inputFacility.facilityType) - //Ok(Json.obj("lounge" -> Json.toJson(Lounge(airline = airline, allianceId = airline.getAllianceId, airport = airport, level = 0, status = LoungeStatus.INACTIVE, foundedCycle = 0)))) + //Ok(Json.obj("lounge" -> Json.toJson(Lounge(airline = airline, allianceId = airport = airport, level = 0, status = LoungeStatus.INACTIVE, foundedCycle = 0)))) } case None => NotFound } @@ -880,7 +880,7 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon } def uploadLogo(airlineId : Int) = AuthenticatedAirline(airlineId) { request => - if (request.user.getReputation < 40) { + if (request.user.getReputation() < 40) { Ok(Json.obj("error" -> JsString("Cannot upload img at current reputation"))) //have to send ok as the jquery plugin's error cannot read the response } else { request.body.asMultipartFormData.map { data => @@ -903,7 +903,7 @@ class AirlineApplication @Inject()(cc: ControllerComponents) extends AbstractCon } def uploadLivery(airlineId : Int) = AuthenticatedAirline(airlineId) { request => - if (request.user.getReputation < 40) { + if (request.user.getReputation() < 40) { Ok(Json.obj("error" -> JsString("Cannot upload img at current reputation"))) //have to send ok as the jquery plugin's error cannot read the response } else { request.body.asMultipartFormData.map { data => diff --git a/airline-web/app/controllers/AirplaneApplication.scala b/airline-web/app/controllers/AirplaneApplication.scala index 244553990..badc268f2 100644 --- a/airline-web/app/controllers/AirplaneApplication.scala +++ b/airline-web/app/controllers/AirplaneApplication.scala @@ -34,7 +34,7 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo result } } - + implicit object AirplaneWithAssignedLinkWrites extends Writes[(Airplane, LinkAssignments)] { def writes(airplaneWithAssignedLink : (Airplane, LinkAssignments)): JsValue = { val airplane = airplaneWithAssignedLink._1 @@ -76,7 +76,7 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo case class ModelWithDiscounts(originalModel : Model, discounts : List[ModelDiscount]) sealed case class AirplanesByModel(model : Model, assignedAirplanes : List[Airplane], availableAirplanes : List[Airplane], constructingAirplanes: List[Airplane]) - + object AirplanesByModelWrites extends Writes[List[AirplanesByModel]] { def writes(airplanesByModelList: List[AirplanesByModel]): JsValue = { var result = Json.obj() @@ -158,7 +158,7 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo } result } - + def getAirplaneModelsByAirline(airlineId : Int) = AuthenticatedAirline(airlineId) { request => val originalModels = ModelSource.loadAllModels() val originalModelsById = originalModels.map(model => (model.id, model)).toMap @@ -204,7 +204,7 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo Ok(result) } - + def getRejections(models : List[Model], airline : Airline) : Map[Model, Option[String]] = { val allManufacturingCountries = models.map(_.countryCode).toSet @@ -218,16 +218,16 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo models.map { model => (model, getRejection(model, 1, countryRelations(model.countryCode), ownedModels, airline)) }.toMap - + } - + def getRejection(model: Model, quantity : Int, airline : Airline) : Option[String] = { val relationship = AirlineCountryRelationship.getAirlineCountryRelationship(model.countryCode, airline) val ownedModels = AirplaneOwnershipCache.getOwnership(airline.id).map(_.model).toSet getRejection(model, quantity, relationship, ownedModels, airline) } - + def getRejection(model: Model, quantity : Int, relationship : AirlineCountryRelationship, ownedModels : Set[Model], airline : Airline) : Option[String]= { if (airline.getHeadQuarter().isEmpty) { //no HQ return Some("Must build HQs before purchasing any airplanes") @@ -248,10 +248,10 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo if (cost > airline.getBalance()) { return Some("Not enough cash to purchase this airplane model") } - + return None } - + def getUsedRejections(usedAirplanes : List[Airplane], model : Model, airline : Airline) : Map[Airplane, String] = { if (airline.getHeadQuarter().isEmpty) { //no HQ return usedAirplanes.map((_, "Must build HQs before purchasing any airplanes")).toMap @@ -267,7 +267,7 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo val rejection = s"Cannot buy used airplane of " + model.name + s" until your relationship with ${CountryCache.getCountry(model.countryCode).get.name} is improved to at least ${Model.BUY_RELATIONSHIP_THRESHOLD}" return usedAirplanes.map((_, rejection)).toMap } - + val ownedModels = AirplaneOwnershipCache.getOwnership(airline.id).map(_.model).toSet val ownedModelFamilies = ownedModels.map(_.family) if (!ownedModelFamilies.contains(model.family) && ownedModelFamilies.size >= airline.airlineGrade.getModelFamilyLimit) { @@ -275,11 +275,11 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo val rejection = "Can only own up to " + airline.airlineGrade.getModelFamilyLimit + " different airplane " + familyToken + " at current airline grade" return usedAirplanes.map((_, rejection)).toMap } - + val rejections = scala.collection.mutable.Map[Airplane, String]() usedAirplanes.foreach { airplane => if (airplane.dealerValue > airline.getBalance()) { - rejections.put(airplane, "Not enough cash to purchase this airplane") + rejections.put(airplane, "Not enough cash to purchase this airplane") } } return rejections.toMap @@ -362,12 +362,12 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo } } - + def getUsedAirplanes(airlineId : Int, modelId : Int) = AuthenticatedAirline(airlineId) { request => ModelSource.loadModelById(modelId) match { - case Some(model) => + case Some(model) => val usedAirplanes = AirplaneSource.loadAirplanesCriteria(List(("a.model", modelId), ("is_sold", true))) - + val rejections = getUsedRejections(usedAirplanes, model, request.user) var result = Json.arr() usedAirplanes.foreach { airplane => @@ -381,7 +381,7 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo case None => BadRequest("model not found") } } - + def buyUsedAirplane(airlineId : Int, airplaneId : Int, homeAirportId : Int, configurationId : Int) = AuthenticatedAirline(airlineId) { request => this.synchronized { AirplaneSource.loadAirplaneById(airplaneId) match { @@ -435,9 +435,9 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo } } } - - - + + + def getAirplane(airlineId : Int, airplaneId : Int) = AuthenticatedAirline(airlineId) { AirplaneSource.loadAirplaneById(airplaneId) match { case Some(airplane) => @@ -452,7 +452,7 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo BadRequest("airplane not found") } } - + def sellAirplane(airlineId : Int, airplaneId : Int) = AuthenticatedAirline(airlineId) { AirplaneSource.loadAirplaneById(airplaneId) match { case Some(airplane) => @@ -492,12 +492,12 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo BadRequest("airplane not found") } } - + def replaceAirplane(airlineId : Int, airplaneId : Int) = AuthenticatedAirline(airlineId) { request => AirplaneSource.loadAirplaneById(airplaneId) match { case Some(airplane) => if (airplane.owner.id == airlineId) { - val currentCycle = CycleSource.loadCycle + val currentCycle = CycleSource.loadCycle() if (!airplane.isReady) { BadRequest("airplane is not yet constructed") } else if (airplane.purchasedCycle > (currentCycle - airplane.model.constructionTime)) { @@ -544,7 +544,7 @@ class AirplaneApplication @Inject()(cc: ControllerComponents) extends AbstractCo BadRequest("airplane not found") } } - + def addAirplane(airlineId : Int, modelId : Int, quantity : Int, homeAirportId : Int, configurationId : Int) = AuthenticatedAirline(airlineId) { request => ModelSource.loadModelById(modelId) match { case None => diff --git a/airline-web/app/controllers/AirportAssetApplication.scala b/airline-web/app/controllers/AirportAssetApplication.scala index 4446cdb78..ae8d138f1 100644 --- a/airline-web/app/controllers/AirportAssetApplication.scala +++ b/airline-web/app/controllers/AirportAssetApplication.scala @@ -142,7 +142,7 @@ class AirportAssetApplication @Inject()(cc: ControllerComponents) extends Abstra def getAirportAssetsWithAirline(airlineId : Int) = AuthenticatedAirline(airlineId) { request => val assets = AirportAssetSource.loadAirportAssetsByAirline(airlineId) - Ok(Json.toJson(assets)(Writes.traversableWrites(OwnedAirportAssetWrites))) + Ok(Json.toJson(assets)(Writes.list(OwnedAirportAssetWrites))) } def getAirportAssetDetailsWithoutAirline(assetId : Int) = Action { request => diff --git a/airline-web/app/controllers/AlertApplication.scala b/airline-web/app/controllers/AlertApplication.scala index 01227d053..f67efcaab 100644 --- a/airline-web/app/controllers/AlertApplication.scala +++ b/airline-web/app/controllers/AlertApplication.scala @@ -43,27 +43,27 @@ class AlertApplication @Inject()(cc: ControllerComponents) extends AbstractContr "duration" -> JsNumber(alert.duration), "cycleDelta" -> JsNumber(alert.cycle - currentCycle) )) - + alert.targetId.foreach { targetId => result = result + ("targetId" -> JsNumber(targetId)) } - + result } } - - + + val LOG_RANGE = 100 //load 100 weeks worth of alerts - - + + def getAlerts(airlineId : Int) = AuthenticatedAirline(airlineId) { request => val alerts = AlertSource.loadAlertsByAirline(request.user.id).sortBy(_.cycle)(Ordering[Int].reverse) //Ok(Json.toJson(alerts)(Writes.traversableWrites(AlertWrites(CycleSource.loadCycle())))) implicit val alertWrites = AlertWrites(CycleSource.loadCycle()) Ok(Json.toJson(alerts)) } - - - + + + } diff --git a/airline-web/app/controllers/AllianceApplication.scala b/airline-web/app/controllers/AllianceApplication.scala index cbf806009..6e924f92b 100644 --- a/airline-web/app/controllers/AllianceApplication.scala +++ b/airline-web/app/controllers/AllianceApplication.scala @@ -35,7 +35,7 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo }) )) } - + implicit object AllianceMemberWrites extends Writes[AllianceMember] { //case class AllianceMember(alliance: Alliance, airline : Airline, role : AllianceRole.Value, joinedCycle : Int) def writes(allianceMember: AllianceMember): JsValue = JsObject(List( @@ -51,16 +51,16 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo "allianceId" -> JsNumber(allianceMember.allianceId), "allianceName" -> JsString(AllianceCache.getAlliance(allianceMember.allianceId).get.name))) } - - - + + + implicit object HistoryWrites extends Writes[AllianceHistory] { //case class AllianceHistory(allianceName : String, airline : Airline, event : AllianceEvent.Value, cycle : Int, var id : Int = 0) def writes(history: AllianceHistory): JsValue = JsObject(List( "cycle" -> JsNumber(history.cycle), "description" -> JsString(getHistoryDescription(history)) )) - + def getHistoryDescription(history : AllianceHistory) : String = { val eventAction = AllianceUtil.getAllianceEventText(history.event) history.airline.name + " " + eventAction + " " + history.allianceName @@ -147,25 +147,25 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo case class FormAlliance(allianceName : String) val formAllianceForm : Form[FormAlliance] = Form( - + // Define a mapping that will handle User values mapping( "allianceName" -> text(minLength = 1, maxLength = 50).verifying( "Alliance name can only contain space and characters", allianceName => allianceName.forall(char => char.isLetter || char == ' ') && !"".equals(allianceName.trim())).verifying( - "This Alliance name is not available", + "This Alliance name is not available", allianceName => !AllianceSource.loadAllAlliances(false).map { _.name.toLowerCase() }.contains(allianceName.toLowerCase()) ) ) { //binding - (allianceName) => FormAlliance(allianceName.trim) - } + (allianceName) => FormAlliance(allianceName.trim) + } { //unbinding formAlliance => Some(formAlliance.allianceName) } ) - - + + def getAirlineAllianceDetails(airlineId : Int) = AuthenticatedAirline(airlineId) { request => var result = Json.obj() AllianceSource.loadAllianceMemberByAirline(request.user) match { @@ -185,9 +185,9 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo } result = result + ("isAdmin" -> JsBoolean(AllianceRole.isAdmin(allianceMember.role))) - case None => //do nothing + case None => //do nothing } - + val history = AllianceSource.loadAllianceHistoryByAirline(airlineId) if (!history.isEmpty) { result = result + ("history" -> Json.toJson(history.sortBy(_.cycle)(Ordering.Int.reverse))) @@ -200,7 +200,7 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo } def formAlliance(airlineId : Int) = AuthenticatedAirline(airlineId) { implicit request => - formAllianceForm.bindFromRequest.fold( + formAllianceForm.bindFromRequest().fold( // Form has errors, redisplay it erroredForm => Ok(Json.obj("rejection" -> JsString(erroredForm.error("allianceName").get.message))), { formAllianceInput => //make sure the current airline is not in any alliance @@ -226,15 +226,15 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo } ) } - - + + def getAlliances(airlineId : Option[Int]) = Action { request => val alliances : List[Alliance] = AllianceSource.loadAllAlliances(true) - + var result = Json.arr() - + val alliancesWithRanking : Map[Int, (Int, BigDecimal)] = AllianceRankingUtil.getRankings() - + alliances.foreach { alliance => val isCurrentMember = airlineId match { @@ -260,21 +260,21 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo //allianceJson = allianceJson + ("maxFrequencyBonus" -> JsNumber(Alliance.getMaxFrequencyBonus(ranking))) } } - - - + + + val historyEntries : List[AllianceHistory] = AllianceSource.loadAllianceHistoryByAllianceName(alliance.name) allianceJson = allianceJson + ("history" -> Json.toJson(historyEntries.sortBy(_.cycle)(Ordering.Int.reverse))) result = result.append(allianceJson) } - + Ok(result) } - + object SimpleLinkWrites extends Writes[List[Link]] { def writes(links: List[Link]): JsValue = { var result = Json.arr() - + links.foreach { link => var linkJson = JsObject(List( "id" -> JsNumber(link.id), @@ -298,7 +298,7 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo "capacity" -> Json.toJson(link.capacity), "flightType" -> JsString(link.flightType.toString()), "flightCode" -> JsString(LinkUtil.getFlightCode(link.airline, link.flightNumber)))) - result = result.append(linkJson) + result = result.append(linkJson) } result } @@ -316,7 +316,7 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo result } } - + def getAllAllianceDetails(allianceId : Int) = Action { request => AllianceCache.getAlliance(allianceId, true) match { case None => NotFound("Alliance with " + allianceId + " is not found") @@ -505,7 +505,7 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo getApplyRejection(request.user, alliance) match { case Some(rejection) => BadRequest(rejection) case None => //ok - val currentCycle = CycleSource.loadCycle + val currentCycle = CycleSource.loadCycle() val newMember = AllianceMember(allianceId = alliance.id, airline = request.user, role = APPLICANT, joinedCycle = currentCycle) AllianceSource.saveAllianceMember(newMember) val history = AllianceHistory(allianceName = alliance.name, airline = request.user, event = APPLY_ALLIANCE, cycle = currentCycle) @@ -545,7 +545,7 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo } def addToAlliance(airlineId : Int, targetAirlineId : Int) = AuthenticatedAirline(airlineId) { implicit request => - val currentCycle = CycleSource.loadCycle + val currentCycle = CycleSource.loadCycle() AllianceSource.loadAllianceMemberByAirline(request.user) match { case None => BadRequest("Current airline " + request.user + " cannot add airline id "+ targetAirlineId + " to alliance as current airline does not belong to any alliance") @@ -583,7 +583,7 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo } def promoteMember(airlineId : Int, targetAirlineId : Int) = AuthenticatedAirline(airlineId) { implicit request => - val currentCycle = CycleSource.loadCycle + val currentCycle = CycleSource.loadCycle() AllianceSource.loadAllianceMemberByAirline(request.user) match { case None => BadRequest("Current airline " + request.user + " cannot promote airline id " + targetAirlineId + " to alliance as current airline does not belong to any alliance") case Some(currentMember) => @@ -617,7 +617,7 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo } def demoteMember(airlineId : Int, targetAirlineId : Int) = AuthenticatedAirline(airlineId) { implicit request => - val currentCycle = CycleSource.loadCycle + val currentCycle = CycleSource.loadCycle() AllianceSource.loadAllianceMemberByAirline(request.user) match { case None => BadRequest("Current airline " + request.user + " cannot demote airline id " + targetAirlineId + " to alliance as current airline does not belong to any alliance") case Some(currentMember) => @@ -644,43 +644,43 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo } } - + def getApplyRejection(airline : Airline, alliance : Alliance) : Option[String] = { val approvedMembers = alliance.members.filter(_.role != AllianceRole.APPLICANT) - - if (airline.getHeadQuarter.isEmpty) { + + if (airline.getHeadQuarter().isEmpty) { return Some("Airline does not have headquarters") } - + if (approvedMembers.size >= Alliance.MAX_MEMBER_COUNT) { return Some("Alliance has reached max member size " + Alliance.MAX_MEMBER_COUNT + " already") } - - - val allAllianceHeadquarters = approvedMembers.flatMap(_.airline.getHeadQuarter).map(_.airport) - val airlineHeadquarters = airline.getHeadQuarter.get.airport - + + val allAllianceHeadquarters = approvedMembers.flatMap(_.airline.getHeadQuarter()).map(_.airport) + + val airlineHeadquarters = airline.getHeadQuarter().get.airport + if (allAllianceHeadquarters.contains(airlineHeadquarters)) { return Some("One of the alliance members has Headquarters at " + getAirportText(airlineHeadquarters)) } - + val allAllianceBases = approvedMembers.flatMap { _.airline.getBases().filter( !_.headquarter) }.map(_.airport) - val airlineBases = airline.getBases.filter(!_.headquarter).map(_.airport) + val airlineBases = airline.getBases().filter(!_.headquarter).map(_.airport) val overlappingBases = allAllianceBases.filter(allianceBase => airlineBases.contains(allianceBase)) - + // println("ALL " + allAllianceBases) // println("YOURS " + airlineHeadquarters) - + if (!overlappingBases.isEmpty) { var message = "Alliance members have overlapping airport bases: " overlappingBases.foreach { overlappingBase => message += getAirportText(overlappingBase) + "; " } - + return Some(message) } - + AllianceSource.loadAllianceMemberByAirline(airline) match { case Some(allianceMember) => if (allianceMember.allianceId != alliance.id) { @@ -691,7 +691,7 @@ class AllianceApplication @Inject()(cc: ControllerComponents) extends AbstractCo } return None } - + def getAirportText(airport : Airport) = { airport.city + " - " + airport.name } diff --git a/airline-web/app/controllers/BankApplication.scala b/airline-web/app/controllers/BankApplication.scala index e1196ffd3..af6abb82f 100644 --- a/airline-web/app/controllers/BankApplication.scala +++ b/airline-web/app/controllers/BankApplication.scala @@ -34,11 +34,11 @@ class BankApplication @Inject()(cc: ControllerComponents) extends AbstractContro ) def viewLoans(airlineId : Int) = AuthenticatedAirline(airlineId) { request : AuthenticatedRequest[Any, Airline] => - Ok(Json.toJson(BankSource.loadLoansByAirline(request.user.id))(Writes.traversableWrites(new LoanWrites(CycleSource.loadCycle())))) + Ok(Json.toJson(BankSource.loadLoansByAirline(request.user.id))(Writes.list(new LoanWrites(CycleSource.loadCycle())))) } - + def takeOutLoan(airlineId : Int) = AuthenticatedAirline(airlineId) { implicit request => - val LoanRequest(requestedAmount, requestedTerm) = loanForm.bindFromRequest.get + val LoanRequest(requestedAmount, requestedTerm) = loanForm.bindFromRequest().get val loanReply = Bank.getMaxLoan(airlineId) val currentCycle = CycleSource.loadCycle() if (loanReply.rejectionOption.isDefined) { @@ -59,18 +59,18 @@ class BankApplication @Inject()(cc: ControllerComponents) extends AbstractContro } } } - + def getLoanOptions(airlineId : Int, loanAmount : Long) = AuthenticatedAirline(airlineId) { request => val loanReply = Bank.getMaxLoan(request.user.id) if (loanAmount <= loanReply.maxLoan) { val options = Bank.getLoanOptions(loanAmount) - Ok(Json.toJson(options)(Writes.traversableWrites(new LoanWrites(CycleSource.loadCycle())))) + Ok(Json.toJson(options)(Writes.list(new LoanWrites(CycleSource.loadCycle())))) } else { BadRequest("Borrowing [" + loanAmount + "] which is above limit [" + loanReply.maxLoan + "]") } - + } - + def getMaxLoan(airlineId : Int) = AuthenticatedAirline(airlineId) { request => val loanReply = Bank.getMaxLoan(request.user.id) var result = Json.obj("maxAmount" -> JsNumber(loanReply.maxLoan)).asInstanceOf[JsObject] @@ -79,15 +79,15 @@ class BankApplication @Inject()(cc: ControllerComponents) extends AbstractContro } Ok(result) } - + def repayLoan(airlineId : Int, loanId : Int) = AuthenticatedAirline(airlineId) { request => val currentCycle = CycleSource.loadCycle() BankSource.loadLoanById(loanId) match { - case Some(loan) => { + case Some(loan) => { if (loan.airlineId != request.user.id) { - BadRequest("Cannot repay loan not owned by this airline") + BadRequest("Cannot repay loan not owned by this airline") } else { - val balance = request.user.getBalance + val balance = request.user.getBalance() if (balance < loan.earlyRepayment(currentCycle)) { BadRequest("Not enough cash to repay this loan") } else { diff --git a/airline-web/app/controllers/ChatApplication.scala b/airline-web/app/controllers/ChatApplication.scala index 76f828f91..0bf04d5cc 100644 --- a/airline-web/app/controllers/ChatApplication.scala +++ b/airline-web/app/controllers/ChatApplication.scala @@ -1,8 +1,8 @@ package controllers import javax.inject._ -import akka.actor._ -import akka.stream.Materializer +import org.apache.pekko.actor._ +import org.apache.pekko.stream.Materializer import play.api._ import play.api.mvc._ @@ -25,7 +25,7 @@ class ChatApplication @Inject()(cc: ControllerComponents)(implicit actorSystem: // (request: RequestHeader) => // (out: ActorRef) => // Props(new ClientActor(out, chat)) - // } + // } def chatSocket = WebSocket.acceptOrResult[String, String] { request => Future.successful(request.session.get("userToken").flatMap(SessionUtil.getUserId(_)) match { case None => diff --git a/airline-web/app/controllers/LinkApplication.scala b/airline-web/app/controllers/LinkApplication.scala index 8c57c6f78..6da17d927 100644 --- a/airline-web/app/controllers/LinkApplication.scala +++ b/airline-web/app/controllers/LinkApplication.scala @@ -44,16 +44,16 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro val distance = Util.calculateDistance(fromAirport.latitude, fromAirport.longitude, toAirport.latitude, toAirport.longitude).toInt val rawQuality = json.\("quality").as[Int] val flightType = Computation.getFlightType(fromAirport, toAirport, distance) - + val link = Link(fromAirport, toAirport, airline, LinkClassValues.getInstance(price), distance, LinkClassValues.getInstance(capacity), rawQuality, distance.toInt * 60 / 800, 1, flightType) - (json \ "id").asOpt[Int].foreach { link.id = _ } + (json \ "id").asOpt[Int].foreach { link.id = _ } JsSuccess(link) } } - - - - + + + + implicit object LinkConsumptionFormat extends Writes[LinkConsumptionDetails] { def writes(linkConsumption: LinkConsumptionDetails): JsValue = { // val fromAirport = AirportCache.getAirport(linkConsumption.fromAirportId) @@ -89,7 +89,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro "cancellationCount" -> JsNumber(linkConsumption.link.cancellationCount), "satisfaction" -> JsNumber(linkConsumption.satisfaction), "cycle" -> JsNumber(linkConsumption.cycle))) - + } } @@ -136,7 +136,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro result } } - + implicit object ModelPlanLinkInfoWrites extends Writes[ModelPlanLinkInfo] { def writes(modelPlanLinkInfo : ModelPlanLinkInfo): JsValue = { val jsObject = JsObject(List( @@ -149,7 +149,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro "flightMinutesRequired" -> JsNumber(modelPlanLinkInfo.flightMinutesRequired), "maxFrequency" -> JsNumber(modelPlanLinkInfo.maxFrequency), "isAssigned" -> JsBoolean(modelPlanLinkInfo.isAssigned))) - + var airplaneArray = JsArray() modelPlanLinkInfo.airplanes.foreach { @@ -162,7 +162,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro } } - + implicit object LinkExtendedInfoWrites extends Writes[LinkExtendedInfo] { def writes(entry: LinkExtendedInfo): JsValue = { val link = entry.link @@ -196,7 +196,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro } implicit object RouteWrites extends Writes[Route] { - def writes(route : Route): JsValue = { + def writes(route : Route): JsValue = { Json.toJson(route.links) } } @@ -220,7 +220,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro } } - + case class PlanLinkData(fromAirportId: Int, toAirportId: Int) val planLinkForm = Form( mapping( @@ -228,17 +228,17 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro "toAirportId" -> number )(PlanLinkData.apply)(PlanLinkData.unapply) ) - - val countryByCode = CountrySource.loadAllCountries.map(country => (country.countryCode, country)).toMap - + + val countryByCode = CountrySource.loadAllCountries().map(country => (country.countryCode, country)).toMap + def addTestLink() = Action { request => if (request.body.isInstanceOf[AnyContentAsJson]) { val newLink = request.body.asInstanceOf[AnyContentAsJson].json.as[Link](TestLinkReads) println("PUT (test)" + newLink) - + LinkSource.saveLink(newLink) match { case Some(link) => - Created(Json.toJson(link)) + Created(Json.toJson(link)) case None => UnprocessableEntity("Cannot insert link") } } else { @@ -259,7 +259,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro val airline = request.user - if (incomingLink.getAssignedAirplanes.isEmpty) { + if (incomingLink.getAssignedAirplanes().isEmpty) { return BadRequest("Cannot insert link - no airplane assigned") } @@ -274,7 +274,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro } //validate slots - val airplanesForThisLink = incomingLink.getAssignedAirplanes + val airplanesForThisLink = incomingLink.getAssignedAirplanes() //validate all airplanes are same model val airplaneModels = airplanesForThisLink.foldLeft(Set[Model]())(_ + _._1.model) //should be just one element if (airplaneModels.size != 1) { @@ -646,7 +646,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro def planLink(airlineId : Int) = AuthenticatedAirline(airlineId) { implicit request => - val PlanLinkData(fromAirportId, toAirportId) = planLinkForm.bindFromRequest.get + val PlanLinkData(fromAirportId, toAirportId) = planLinkForm.bindFromRequest().get val airline = request.user preparePlanLink(airline, fromAirportId, toAirportId) match { case Right((fromAirport, toAirport)) => { @@ -719,14 +719,14 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro Pricing.computeStandardPrice(distance, Computation.getFlightType(fromAirport, toAirport, distance), FIRST)) //adjust suggestedPrice with Lounge - toAirport.getLounge(airline.id, airline.getAllianceId, activeOnly = true).foreach { lounge => + toAirport.getLounge(airline.id, airline.getAllianceId(), activeOnly = true).foreach { lounge => suggestedPrice = LinkClassValues.getInstance(suggestedPrice(ECONOMY), (suggestedPrice(BUSINESS) / (1 + lounge.getPriceReduceFactor(distance))).toInt, (suggestedPrice(FIRST) / (1 + lounge.getPriceReduceFactor(distance))).toInt) } - fromAirport.getLounge(airline.id, airline.getAllianceId, activeOnly = true).foreach { lounge => + fromAirport.getLounge(airline.id, airline.getAllianceId(), activeOnly = true).foreach { lounge => suggestedPrice = LinkClassValues.getInstance(suggestedPrice(ECONOMY), (suggestedPrice(BUSINESS) / (1 + lounge.getPriceReduceFactor(distance))).toInt, (suggestedPrice(FIRST) / (1 + lounge.getPriceReduceFactor(distance))).toInt) @@ -879,7 +879,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro def getRejectionReason(airline : Airline, fromAirport: Airport, toAirport : Airport, existingLink : Option[Link]) : Option[(String, RejectionType.Value)]= { import RejectionType._ - if (airline.getCountryCode.isEmpty) { + if (airline.getCountryCode().isEmpty) { return Some(("Airline has no HQ!", NO_BASE)) } val toCountryCode = toAirport.countryCode @@ -966,7 +966,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro // def getVipRoutes() = Action { // Ok(Json.toJson(RouteHistorySource.loadVipRoutes())) // } - + def getRelatedLinkConsumption(airlineId : Int, linkId : Int, cycleDelta : Int, selfOnly : Boolean, economy : Boolean, business : Boolean, first : Boolean) = AuthenticatedAirline(airlineId) { LinkSource.loadFlightLinkById(linkId, LinkSource.SIMPLE_LOAD) match { case Some(link) => { @@ -1195,7 +1195,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro var overlappingLinksJson = Json.arr() overlappingLinks.filter(_.capacity.total > 0).foreach { overlappingLink => //only work on links that have capacity - overlappingLinksJson = overlappingLinksJson.append(Json.toJson(LinkSource.loadLinkConsumptionsByLinkId(overlappingLink.id, cycleCount))(Writes.traversableWrites(MinimumLinkConsumptionWrite))) + overlappingLinksJson = overlappingLinksJson.append(Json.toJson(LinkSource.loadLinkConsumptionsByLinkId(overlappingLink.id, cycleCount))(Writes.list(MinimumLinkConsumptionWrite))) rivals += overlappingLink.airline } @@ -1391,19 +1391,19 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro } } - val linkChangesJson = Json.toJson(linkChanges)(Writes.traversableWrites(new LinkChangeToEventWrites(currentCycle, link))).as[JsArray] + val linkChangesJson = Json.toJson(linkChanges)(Writes.list(new LinkChangeToEventWrites(currentCycle, link))).as[JsArray] result = result ++ linkChangesJson } //self alliance history - result = result ++ Json.toJson(AllianceSource.loadAllianceHistoryByAirline(airlineId).filter(_.cycle >= fromCycle).filter(entry => isRelevantAllianceHistoryEvent(entry.event)))(Writes.traversableWrites(new AllianceHistoryToEventWrites(currentCycle))).as[JsArray] + result = result ++ Json.toJson(AllianceSource.loadAllianceHistoryByAirline(airlineId).filter(_.cycle >= fromCycle).filter(entry => isRelevantAllianceHistoryEvent(entry.event)))(Writes.list(new AllianceHistoryToEventWrites(currentCycle))).as[JsArray] //other airline alliance history (complicate if current airline left/joined during the period, for now just get the history of current alliance request.user.getAllianceId().foreach { allianceId => AllianceSource.loadAllianceById(allianceId).foreach { alliance => result = result ++ Json.toJson(AllianceSource.loadAllianceHistoryByAllianceName(alliance.name).filter { entry => entry.cycle >= fromCycle && entry.airline.id != airlineId && isRelevantAllianceHistoryEvent(entry.event) - })(Writes.traversableWrites(new AllianceHistoryToEventWrites(currentCycle))).as[JsArray] + })(Writes.list(new AllianceHistoryToEventWrites(currentCycle))).as[JsArray] } } @@ -1570,7 +1570,7 @@ class LinkApplication @Inject()(cc: ControllerComponents) extends AbstractContro var result = Json.obj("negotiationInfo" -> Json.toJson(negotiationInfo)(NegotiationInfoWrites(incomingLink)), - "delegateInfo" -> Json.toJson(request.user.getDelegateInfo), + "delegateInfo" -> Json.toJson(request.user.getDelegateInfo()), "toAirport" -> Json.toJson(incomingLink.to), "fromAirport" -> Json.toJson(incomingLink.from)) diff --git a/airline-web/app/controllers/LinkUtil.scala b/airline-web/app/controllers/LinkUtil.scala index f356dc58d..6ca98d580 100644 --- a/airline-web/app/controllers/LinkUtil.scala +++ b/airline-web/app/controllers/LinkUtil.scala @@ -37,6 +37,6 @@ object LinkUtil { } def getFlightCode(airline : Airline, flightNumber : Int) = { - airline.getAirlineCode + " " + (1000 + flightNumber).toString.substring(1, 4) + airline.getAirlineCode() + " " + (1000 + flightNumber).toString.substring(1, 4) } } diff --git a/airline-web/app/controllers/LogApplication.scala b/airline-web/app/controllers/LogApplication.scala index a8c83397b..c7993ec3d 100644 --- a/airline-web/app/controllers/LogApplication.scala +++ b/airline-web/app/controllers/LogApplication.scala @@ -25,13 +25,13 @@ class LogApplication @Inject()(cc: ControllerComponents) extends AbstractControl "properties" -> Json.toJson(log.properties) )) } - - + + val LOG_RANGE = 100 //load 100 weeks worth of logs - - + + def getLogs(airlineId : Int) = AuthenticatedAirline(airlineId) { request => - val cycle = CycleSource.loadCycle + val cycle = CycleSource.loadCycle() implicit val logWrites = LogWrites(cycle) Ok(Json.toJson(LogSource.loadLogsByAirline(request.user.id, cycle - Log.RETENTION_CYCLE).sortBy(_.cycle)(Ordering[Int].reverse))) } @@ -39,7 +39,7 @@ class LogApplication @Inject()(cc: ControllerComponents) extends AbstractControl val MAX_NOTE_LENGTH : Int = 100 def putSelfNote(airlineId : Int) = AuthenticatedAirline(airlineId) { request => - val cycle = CycleSource.loadCycle + val cycle = CycleSource.loadCycle() val json = request.body.asInstanceOf[AnyContentAsJson].json json.\("note").asOpt[String] match { case Some(note) => @@ -58,8 +58,8 @@ class LogApplication @Inject()(cc: ControllerComponents) extends AbstractControl } } - - - + + + } diff --git a/airline-web/app/controllers/LogoUtil.scala b/airline-web/app/controllers/LogoUtil.scala index c4fe07e22..57ad38517 100644 --- a/airline-web/app/controllers/LogoUtil.scala +++ b/airline-web/app/controllers/LogoUtil.scala @@ -6,23 +6,23 @@ import com.patson.data.AirlineSource import javax.imageio.ImageIO object LogoUtil { - val logos : scala.collection.mutable.Map[Int, Array[Byte]] = collection.mutable.Map(AirlineSource.loadLogos().toSeq: _*) - val blank = getBlankLogo + val logos : scala.collection.mutable.Map[Int, Array[Byte]] = collection.mutable.Map(AirlineSource.loadLogos().toSeq: _*) + val blank = getBlankLogo() val imageHeight = 12 val imageWidth = 24 - + def getLogo(airlineId : Int) : Array[Byte]= { logos.get(airlineId) match { case Some(logo) => logo case None => blank } } - + def saveLogo(airlineId : Int, logo : Array[Byte]) = { AirlineSource.saveLogo(airlineId, logo) logos.put(airlineId, logo) //update cache } - + def validateUpload(logoFile : Path) : Option[String] = { val imageInputStream = ImageIO.createImageInputStream(logoFile.toFile) val readers = ImageIO.getImageReaders(imageInputStream) @@ -41,7 +41,7 @@ object LogoUtil { } return None } - + def getBlankLogo() = { //val buffer = new ByteArrayOutputStream(); val is = play.Environment.simple().resourceAsStream("/logo/blank.png") diff --git a/airline-web/app/controllers/NegotiationUtil.scala b/airline-web/app/controllers/NegotiationUtil.scala index 5edcb2e7a..479f62934 100644 --- a/airline-web/app/controllers/NegotiationUtil.scala +++ b/airline-web/app/controllers/NegotiationUtil.scala @@ -415,7 +415,7 @@ object NegotiationUtil { return NegotiationUtil.NO_NEGOTIATION_REQUIRED.copy(remarks = Some(s"Free for first $FREE_LINK_THRESHOLD routes of freq <= $FREE_LINK_FREQUENCY_THRESHOLD (< $FREE_LINK_DIFFICULTY_THRESHOLD difficulty)")) } - val info = NegotiationInfo(fromAirportRequirements, toAirportRequirements, fromAirportDiscounts, toAirportDiscounts, totalFromDiscount, totalToDiscount, finalRequirementValue, computeOdds(finalRequirementValue, Math.min(MAX_ASSIGNED_DELEGATE, airline.getDelegateInfo.availableCount))) + val info = NegotiationInfo(fromAirportRequirements, toAirportRequirements, fromAirportDiscounts, toAirportDiscounts, totalFromDiscount, totalToDiscount, finalRequirementValue, computeOdds(finalRequirementValue, Math.min(MAX_ASSIGNED_DELEGATE, airline.getDelegateInfo().availableCount))) return info } @@ -701,10 +701,3 @@ case class NegotiationLoyaltyBonusTemplate(bonusFactor : Int) extends Negotiatio NegotiationLoyaltyBonus(airport, loyaltyBonus.toDouble, duration, description, intensity) } } - - - - - - - diff --git a/airline-web/app/controllers/OlympicsApplication.scala b/airline-web/app/controllers/OlympicsApplication.scala index 97a64d90b..7e548dc96 100644 --- a/airline-web/app/controllers/OlympicsApplication.scala +++ b/airline-web/app/controllers/OlympicsApplication.scala @@ -152,7 +152,7 @@ class OlympicsApplication @Inject()(cc: ControllerComponents) extends AbstractCo case None => } - val currentCycle = CycleSource.loadCycle () + val currentCycle = CycleSource.loadCycle() EventSource.loadEventById(eventId) match { case Some(olympics : Olympics) => @@ -416,5 +416,3 @@ class OlympicsApplication @Inject()(cc: ControllerComponents) extends AbstractCo } } } - - diff --git a/airline-web/app/controllers/RankingUtil.scala b/airline-web/app/controllers/RankingUtil.scala index e6382dba2..9a6ec088b 100644 --- a/airline-web/app/controllers/RankingUtil.scala +++ b/airline-web/app/controllers/RankingUtil.scala @@ -2,20 +2,20 @@ package controllers import com.patson.data.{AirlineSource, CycleSource, LinkSource, LoungeHistorySource} import com.patson.model._ - + object RankingUtil { var loadedCycle = 0 var cachedRankings : Map[RankingType.Value, List[Ranking]] = Map.empty - + // val generatedLogo = ImageUtil.generateLogo("logo/star.bmp", Color.CYAN.getRGB, Color.RED.getRGB) // LogoUtil.saveLogo(1, generatedLogo) - + def getRankings() : Map[RankingType.Value, List[Ranking]] = { checkCache() cachedRankings } - + private def checkCache() = { val currentCycle = CycleSource.loadCycle() synchronized { @@ -27,13 +27,13 @@ object RankingUtil { loadedCycle = currentCycle } } - + private[this] def updateRankings() = { val flightConsumptions = LinkSource.loadLinkConsumptions().filter(_.link.transportType == TransportType.FLIGHT) val flightConsumptionsByAirline = flightConsumptions.groupBy(_.link.airline.id) val links = LinkSource.loadAllFlightLinks().groupBy(_.airline.id) val airlinesById = AirlineSource.loadAllAirlines(fullLoad = true).map( airline => (airline.id, airline)).toMap - + val updatedRankings = scala.collection.mutable.Map[RankingType.Value, List[Ranking]]() updatedRankings.put(RankingType.PASSENGER, getPassengerRanking(flightConsumptionsByAirline, airlinesById)) updatedRankings.put(RankingType.PASSENGER_MILE, getPassengerMileRanking(flightConsumptionsByAirline, airlinesById)) @@ -41,7 +41,7 @@ object RankingUtil { updatedRankings.put(RankingType.SERVICE_QUALITY, getServiceQualityRanking(airlinesById)) updatedRankings.put(RankingType.LINK_COUNT, getLinkCountRanking(links, airlinesById)) updatedRankings.put(RankingType.LINK_PROFIT, getLinkProfitRanking(flightConsumptions, airlinesById)) - updatedRankings.put(RankingType.LOUNGE, getLoungeRanking(LoungeHistorySource.loadAll, airlinesById)) + updatedRankings.put(RankingType.LOUNGE, getLoungeRanking(LoungeHistorySource.loadAll(), airlinesById)) val (paxByAirport, paxByAirportPair) = getPaxStat(flightConsumptions) @@ -55,18 +55,18 @@ object RankingUtil { // updatedRankings.put(RankingType.PASSENGER_EU, getPassengerByZoneRanking(linkConsumptionsByAirlineAndZone, airlinesById, RankingType.PASSENGER_EU, "EU")) // updatedRankings.put(RankingType.PASSENGER_NA, getPassengerByZoneRanking(linkConsumptionsByAirlineAndZone, airlinesById, RankingType.PASSENGER_NA, "NA")) // updatedRankings.put(RankingType.PASSENGER_SA, getPassengerByZoneRanking(linkConsumptionsByAirlineAndZone, airlinesById, RankingType.PASSENGER_SA, "SA")) - - - + + + updateMovements(cachedRankings, updatedRankings.toMap) - + cachedRankings = updatedRankings.toMap } - + private[this] def getPassengerRanking(linkConsumptions : Map[Int, List[LinkConsumptionDetails]], airlinesById : Map[Int, Airline]) : List[Ranking] = { val passengersByAirline : Map[Int, Long] = linkConsumptions.view.mapValues(_.map(_.link.soldSeats.total.toLong).sum).toMap val sortedPassengersByAirline = passengersByAirline.toList.sortBy(_._2)(Ordering[Long].reverse) //sort by total passengers of each airline - + sortedPassengersByAirline.zipWithIndex.map { case((airlineId, passengers), index) => Ranking(RankingType.PASSENGER, key = airlineId, @@ -75,15 +75,15 @@ object RankingUtil { rankedValue = passengers) }.toList.sortBy(_.ranking) } - - + + private[this] def getPassengerByZoneRanking(passengersByAirlineAndZone : Map[Int, Map[String, Long]], airlinesById : Map[Int, Airline], rankingType : RankingType.Value, zone : String) : List[Ranking] = { val passengersByAirline : Map[Int, Long] = passengersByAirlineAndZone.view.mapValues { passengersByZone => passengersByZone.getOrElse(zone, 0L) }.toMap val sortedPassengersByAirline = passengersByAirline.toList.sortBy(_._2)(Ordering[Long].reverse) //sort by total passengers of each airline - + sortedPassengersByAirline.zipWithIndex.map { case((airlineId, passengers), index) => Ranking(rankingType, key = airlineId, @@ -92,24 +92,24 @@ object RankingUtil { rankedValue = passengers) }.toList.sortBy(_.ranking) } - + private[this] def getPassengersByZone(linkConsumptions : Map[Int, List[LinkConsumptionDetails]]) : Map[Int, Map[String, Long]] = { val passengersByAirlineAndZone : Map[Int, Map[String, Long]] = linkConsumptions.view.mapValues(_.groupBy(_.link.from.zone).view.mapValues { linkConsumptionsByZone => linkConsumptionsByZone.map(_.link.soldSeats.total.toLong).sum }.toMap).toMap - - passengersByAirlineAndZone + + passengersByAirlineAndZone } - - - + + + private[this] def getPassengerMileRanking(linkConsumptions : Map[Int, List[LinkConsumptionDetails]], airlinesById : Map[Int, Airline]) : List[Ranking] = { val passengerMileByAirline : Map[Int, Long] = linkConsumptions.view.mapValues(_.map { linkConsumption => linkConsumption.link.soldSeats.total.toLong * linkConsumption.link.distance }.sum).toMap - + val sortedPassengerMileByAirline= passengerMileByAirline.toList.sortBy(_._2)(Ordering[Long].reverse) //sort by total passengers of each airline - + sortedPassengerMileByAirline.zipWithIndex.map { case((airlineId, passengerMile), index) => Ranking(RankingType.PASSENGER_MILE, key = airlineId, @@ -118,10 +118,10 @@ object RankingUtil { rankedValue = passengerMile) }.toList.sortBy(_.ranking) } - + private[this] def getLinkProfitRanking(linkConsumptions : List[LinkConsumptionDetails], airlinesById : Map[Int, Airline]) : List[Ranking] = { val mostProfitableLinks : List[LinkConsumptionDetails] = linkConsumptions.sortBy(_.profit)(Ordering[Int].reverse) - + mostProfitableLinks.zipWithIndex.map { case(linkConsumption, index) => { val airlineId = linkConsumption.link.airline.id @@ -132,13 +132,13 @@ object RankingUtil { rankedValue = linkConsumption.profit) ranking } - + }.toList.sortBy(_.ranking).take(500) } - + private[this] def getLoungeRanking(loungeConsumptions : List[LoungeConsumptionDetails], airlinesById : Map[Int, Airline]) : List[Ranking] = { val mostVisitedLounges : List[LoungeConsumptionDetails] = loungeConsumptions.sortBy(entry => entry.selfVisitors + entry.allianceVisitors)(Ordering[Int].reverse) - + mostVisitedLounges.zipWithIndex.map { case(details, index) => { val lounge = details.lounge @@ -149,15 +149,15 @@ object RankingUtil { rankedValue = details.selfVisitors + details.allianceVisitors) ranking } - + }.toList.sortBy(_.ranking) } - - - + + + private[this] def getReputationRanking(airlinesById : Map[Int, Airline]) : List[Ranking] = { val airlinesBySortedReputation = airlinesById.values.toList.sortBy(_.getReputation())(Ordering[Double].reverse) - + airlinesBySortedReputation.zipWithIndex.map { case(airline, index) => Ranking(RankingType.REPUTATION, key = airline.id, @@ -168,7 +168,7 @@ object RankingUtil { } private[this] def getServiceQualityRanking(airlinesById : Map[Int, Airline]) : List[Ranking] = { val airlinesBySortedServiceQuality = airlinesById.values.toList.sortBy(_.getCurrentServiceQuality())(Ordering[Double].reverse) - + airlinesBySortedServiceQuality.zipWithIndex.map { case(airline, index) => Ranking(RankingType.SERVICE_QUALITY, key = airline.id, @@ -177,11 +177,11 @@ object RankingUtil { rankedValue = airline.getCurrentServiceQuality()) } } - + private[this] def getLinkCountRanking(linksByAirline : Map[Int, List[Link]], airlinesById : Map[Int, Airline]) : List[Ranking] = { val linkCountByAirline : Map[Int, Int] = linksByAirline.view.mapValues(_.length).toMap val sortedLinkCountByAirline = linkCountByAirline.toList.sortBy(_._2)(Ordering[Int].reverse) //sort by total passengers of each airline - + sortedLinkCountByAirline.zipWithIndex.map { case((airlineId, linkCount), index) => Ranking(RankingType.LINK_COUNT, key = airlineId, @@ -190,7 +190,7 @@ object RankingUtil { rankedValue = linkCount) }.toList } - + private[this] def getAirportRanking(paxByAirport : Map[Airport, Long]) : List[Ranking] = { paxByAirport.toList.sortBy(_._2)(Ordering[Long].reverse).zipWithIndex.map { case((airport, passengers), index) => Ranking(RankingType.AIRPORT, @@ -235,16 +235,16 @@ object RankingUtil { val previousRankingsByKey : Map[RankingType.Value, Map[Any, Int]] = previousRankings.view.mapValues { rankings => rankings.map(ranking => (ranking.key, ranking.ranking)).toMap }.toMap - - + + newRankings.foreach { case(rankingType, rankings) => { val previousRankingOfThisType = previousRankingsByKey.get(rankingType) if (previousRankingOfThisType.isDefined) { - rankings.foreach { newRankingEntry => + rankings.foreach { newRankingEntry => val previousRanking = previousRankingOfThisType.get.get(newRankingEntry.key) if (previousRanking.isDefined) { - newRankingEntry.movement = newRankingEntry.ranking - previousRanking.get + newRankingEntry.movement = newRankingEntry.ranking - previousRanking.get } } } @@ -263,7 +263,5 @@ case class Ranking(rankingType : RankingType.Value, key : Any, entry : Any, rank //class AirlineRanking(rankingType : RankingType.Value, airline : Airline, ranking : Int, rankedValue : Number, var movement : Int = 0) extends Ranking(rankingType, airline, ranking, rankedValue, movement) { -// +// //} - - diff --git a/airline-web/app/controllers/SearchApplication.scala b/airline-web/app/controllers/SearchApplication.scala index 442313daf..ac92bb36a 100644 --- a/airline-web/app/controllers/SearchApplication.scala +++ b/airline-web/app/controllers/SearchApplication.scala @@ -200,7 +200,7 @@ class SearchApplication @Inject()(cc: ControllerComponents) extends AbstractCont detailedTransport.transportType match { case TransportType.FLIGHT => val detailedLink = detailedTransport.asInstanceOf[Link] - linkJson = linkJson + ("computedQuality" -> JsNumber(detailedLink.computedQuality)) + linkJson = linkJson + ("computedQuality" -> JsNumber(detailedLink.computedQuality())) detailedLink.getAssignedModel().foreach { model => linkJson = linkJson + ("airplaneModelName" -> JsString(model.name)) } @@ -278,7 +278,7 @@ class SearchApplication @Inject()(cc: ControllerComponents) extends AbstractCont if (result.isEmpty) { Ok(Json.obj("message" -> "No match")) } else { - Ok(Json.obj("entries" -> Json.toJson(result)(Writes.traversableWrites(new AirlineSearchResultWrites(input))))) + Ok(Json.obj("entries" -> Json.toJson(result)(Writes.list(new AirlineSearchResultWrites(input))))) } } } @@ -359,7 +359,7 @@ class SearchApplication @Inject()(cc: ControllerComponents) extends AbstractCont val features = ListBuffer[LinkFeature.Value]() import LinkFeature._ - val airlineServiceQuality = link.airline.getCurrentServiceQuality + val airlineServiceQuality = link.airline.getCurrentServiceQuality() if (link.duration <= 120) { //very short flight if (link.rawQuality >= 80) { features += BEVERAGE_SERVICE @@ -466,7 +466,7 @@ class SearchApplication @Inject()(cc: ControllerComponents) extends AbstractCont - result = result + ("consumptions" -> Json.toJson(consumptions)(Writes.traversableWrites(MinimumLinkConsumptionWrite))) + result = result + ("consumptions" -> Json.toJson(consumptions)(Writes.list(MinimumLinkConsumptionWrite))) Ok(result) } @@ -486,5 +486,3 @@ class SearchApplication @Inject()(cc: ControllerComponents) extends AbstractCont case class LinkDetail(link : Link, timeslot : TimeSlot) } - - diff --git a/airline-web/app/controllers/SignUp.scala b/airline-web/app/controllers/SignUp.scala index 98b7e5ac9..dc249649a 100644 --- a/airline-web/app/controllers/SignUp.scala +++ b/airline-web/app/controllers/SignUp.scala @@ -40,14 +40,14 @@ class SignUp @Inject()(cc: ControllerComponents)(ws: WSClient) extends AbstractC * validation, submission, errors, redisplaying, ... */ val signupForm: Form[NewUser] = Form( - + // Define a mapping that will handle User values mapping( "username" -> text(minLength = 4, maxLength = 20).verifying( "username can only contain alphanumeric characters", userName => userName.forall(char => char.isLetterOrDigit && char <= 'z')).verifying( "This username is not available", - userName => !UserSource.loadUsersByCriteria(List.empty).map { _.userName.toLowerCase() }.contains(userName.toLowerCase()) + userName => !UserSource.loadUsersByCriteria(List.empty).map { _.userName.toLowerCase() }.contains(userName.toLowerCase()) ), "email" -> email, // Create a tuple mapping for the password/confirm @@ -71,25 +71,25 @@ class SignUp @Inject()(cc: ControllerComponents)(ws: WSClient) extends AbstractC { // Binding: Create a User from the mapping result (ignore the second password and the accept field) (username, email, passwords, recaptureToken, airlineName) => NewUser(username.trim, passwords._1, email.trim, recaptureToken, airlineName.trim) - } + } { // Unbinding: Create the mapping values from an existing User value user => Some(user.username, user.email, (user.password, ""), "", user.airlineName) } ) - + /** * Display an empty form. */ def form = Action { implicit request => Ok(html.signup(signupForm)) } - + /** * Display a form pre-filled with an existing User. */ // def editForm = Action { -// val existingUser = NewUser("fakeuser", "secret", "fake@gmail.com") +// val existingUser = NewUser("fakeuser", "secret", "fake@gmail.com") // Ok(html.signup(signupForm.fill(existingUser))) // } @@ -110,16 +110,16 @@ class SignUp @Inject()(cc: ControllerComponents)(ws: WSClient) extends AbstractC * Handle form submission. */ def submit = Action { implicit request => - signupForm.bindFromRequest.fold( + signupForm.bindFromRequest().fold( // Form has errors, redisplay it errors => BadRequest(html.signup(errors)), { userInput => - + if (isValidRecaptcha(userInput.recaptchaToken)) { // We got a valid User value, display the summary val user = User(userInput.username, userInput.email, Calendar.getInstance, Calendar.getInstance, UserStatus.ACTIVE, level = 0, None, List.empty) UserSource.saveUser(user) Authentication.createUserSecret(userInput.username, userInput.password) - + val newAirline = Airline(userInput.airlineName) // newAirline.setBalance(50000000) //initial balance 50 million newAirline.setMaintenanceQuality(100) @@ -128,7 +128,7 @@ class SignUp @Inject()(cc: ControllerComponents)(ws: WSClient) extends AbstractC UserSource.setUserAirline(user, newAirline) SearchUtil.addAirline(newAirline) - + // val profile = StartupProfile.profilesById(userInput.profileId) // profile.initializeAirline(newAirline) Redirect("/").withCookies(Cookie("sessionActive", "true", httpOnly = false)).withSession("userToken" -> SessionUtil.addUserId(user.id)) @@ -139,25 +139,25 @@ class SignUp @Inject()(cc: ControllerComponents)(ws: WSClient) extends AbstractC } ) } - + def isValidRecaptcha(recaptchaToken: String) : Boolean = { println("checking token " + recaptchaToken) val request = ws.url(recaptchaUrl).withQueryStringParameters("secret" -> recaptchaSecret, "response" -> recaptchaToken) - + val (successJs, scoreJs, actionJs, responseBody) = Await.result(request.get().map { response => ((response.json \ "success"), (response.json \ "score"), (response.json \ "action"), response.body) }, Duration(10, TimeUnit.SECONDS)) - + if (!successJs.as[Boolean]) { println("recaptcha response with success as false") - return false; + return false; } - + val score = scoreJs.as[Double] val action = actionJs.as[String] - + println("recaptcha score " + score + " action " + action) - + return action == recaptchaAction && score >= recaptchaScoreThreshold } } \ No newline at end of file diff --git a/airline-web/app/controllers/package.scala b/airline-web/app/controllers/package.scala index 82e8c10d4..37f983b53 100644 --- a/airline-web/app/controllers/package.scala +++ b/airline-web/app/controllers/package.scala @@ -1,5 +1,5 @@ -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer +import org.apache.pekko.actor.ActorSystem +import org.apache.pekko.stream.ActorMaterializer import com.patson.{AllianceMissionSimulation, Util} import com.patson.data._ import com.patson.data.airplane._ @@ -41,8 +41,8 @@ package object controllers { "isGenerated" -> airline.isGenerated ) - if (airline.getCountryCode.isDefined) { - result = result + ("countryCode" -> JsString(airline.getCountryCode.get)) + if (airline.getCountryCode().isDefined) { + result = result + ("countryCode" -> JsString(airline.getCountryCode().get)) } airline.getHeadQuarter().foreach { headquarters => result = result + @@ -182,7 +182,7 @@ package object controllers { link.setAssignedAirplanes(airplaneAssignments.toList.map { case(airplane, frequency) => (airplane, LinkAssignment(frequency, frequency * flightMinutesRequiredPerFlight)) }.toMap) - //(json \ "id").asOpt[Int].foreach { link.id = _ } + //(json \ "id").asOpt[Int].foreach { link.id = _ } JsSuccess(link) } @@ -206,7 +206,7 @@ package object controllers { "flightType" -> JsString(FlightType.label(link.flightType)), "capacity" -> Json.toJson(link.capacity), "rawQuality" -> JsNumber(link.rawQuality), - "computedQuality" -> JsNumber(link.computedQuality), + "computedQuality" -> JsNumber(link.computedQuality()), "duration" -> JsNumber(link.duration), "frequency" -> JsNumber(link.frequency), "availableSeat" -> Json.toJson(link.availableSeats), @@ -257,7 +257,7 @@ package object controllers { "capacity" -> Json.toJson(linkConsumption.link.capacity), "frequency" -> JsNumber(linkConsumption.link.frequency), "soldSeats" -> JsNumber(linkConsumption.link.soldSeats.total), - "quality" -> JsNumber(linkConsumption.link.computedQuality))) + "quality" -> JsNumber(linkConsumption.link.computedQuality()))) } } @@ -574,7 +574,7 @@ package object controllers { )) var bonusJson = Json.obj() - airport.getAllAirlineBonuses.toList.foreach { + airport.getAllAirlineBonuses().toList.foreach { case (airlineId, bonuses) => { var totalLoyaltyBonus = 0.0 var loyaltyBreakdownJson = Json.arr(Json.obj("description" -> "Basic", "value" -> BigDecimal(airport.getAirlineBaseAppeal(airlineId).loyalty).setScale(2, RoundingMode.HALF_EVEN))) diff --git a/airline-web/app/views/about.scala.html b/airline-web/app/views/about.scala.html index 0d070b36c..3c3fd0a7a 100644 --- a/airline-web/app/views/about.scala.html +++ b/airline-web/app/views/about.scala.html @@ -1,6 +1,4 @@ @import helper._ -@import play.api.Play.current -@import play.api.i18n.Messages.Implicits._ @@ -10,7 +8,7 @@ #map { height: 100%; } - + @@ -121,5 +119,5 @@ }) - + \ No newline at end of file diff --git a/airline-web/app/views/checkEmail.scala.html b/airline-web/app/views/checkEmail.scala.html index 84fd56961..6ae497765 100644 --- a/airline-web/app/views/checkEmail.scala.html +++ b/airline-web/app/views/checkEmail.scala.html @@ -1,6 +1,4 @@ @import helper._ -@import play.api.Play.current -@import play.api.i18n.Messages.Implicits._ @@ -10,7 +8,7 @@ #map { height: 100%; } - + @@ -22,6 +20,6 @@

Email sent!

Help Instructions have been sent to the registered email address! Please make sure info@@airline-club.com is not blocked by your spam filter.
- - + + \ No newline at end of file diff --git a/airline-web/app/views/forgotId.scala.html b/airline-web/app/views/forgotId.scala.html index 96af28fc4..06bacf474 100644 --- a/airline-web/app/views/forgotId.scala.html +++ b/airline-web/app/views/forgotId.scala.html @@ -1,8 +1,6 @@ @(forgotForm: Form[ForgotId])(implicit request: RequestHeader, messagesProvider: MessagesProvider) @import helper._ -@import play.api.Play.current -@import play.api.i18n.Messages.Implicits._ @@ -41,5 +39,5 @@

Forgot User Name

} - + \ No newline at end of file diff --git a/airline-web/app/views/forgotPassword.scala.html b/airline-web/app/views/forgotPassword.scala.html index 5cf2f6bdc..a99ec7fd0 100644 --- a/airline-web/app/views/forgotPassword.scala.html +++ b/airline-web/app/views/forgotPassword.scala.html @@ -1,8 +1,6 @@ @(forgotForm: Form[ForgotPassword])(implicit request: RequestHeader, messagesProvider: MessagesProvider) @import helper._ -@import play.api.Play.current -@import play.api.i18n.Messages.Implicits._ @@ -12,7 +10,7 @@ #map { height: 100%; } - + @@ -50,5 +48,5 @@

Forgot Password

} - + \ No newline at end of file diff --git a/airline-web/app/views/signup.scala.html b/airline-web/app/views/signup.scala.html index c677fbc7e..265a66963 100644 --- a/airline-web/app/views/signup.scala.html +++ b/airline-web/app/views/signup.scala.html @@ -1,8 +1,6 @@ @(signupForm: Form[NewUser])(implicit request: RequestHeader, messagesProvider: MessagesProvider) @import helper._ -@import play.api.Play.current -@import play.api.i18n.Messages.Implicits._ diff --git a/airline-web/app/websocket/ActorCenter.scala b/airline-web/app/websocket/ActorCenter.scala index f378020fe..99c47edc9 100644 --- a/airline-web/app/websocket/ActorCenter.scala +++ b/airline-web/app/websocket/ActorCenter.scala @@ -1,7 +1,7 @@ package websocket -import akka.actor.{Actor, ActorRef, ActorSelection, Props, Terminated} -import akka.remote.{AssociatedEvent, DisassociatedEvent, RemotingLifecycleEvent} +import org.apache.pekko.actor.{Actor, ActorRef, ActorSelection, Props, Terminated} +import org.apache.pekko.remote.{AssociatedEvent, DisassociatedEvent, RemotingLifecycleEvent} import com.patson.model.Airline import com.patson.model.notice.{AirlineNotice, NoticeCategory, TrackingNotice} import com.patson.stream.{CycleCompleted, CycleInfo, KeepAlivePing, KeepAlivePong, ReconnectPing, SimulationEvent} @@ -219,11 +219,11 @@ object ActorCenter { implicit val system = actorSystem //ActorSystem("localWebsocketSystem") val configFactory = ConfigFactory.load() - val actorHost = if (configFactory.hasPath("airline.akka-actor.host")) configFactory.getString("airline.akka-actor.host") else "127.0.0.1:2552" + val actorHost = if (configFactory.hasPath("airline.pekko-actor.host")) configFactory.getString("airline.pekko-actor.host") else "127.0.0.1:2552" println("!!!!!!!!!!!!!!!AKK ACTOR HOST IS " + actorHost) val subscribers = mutable.HashSet[ActorRef]() - val remoteMainActor = system.actorSelection("akka.tcp://" + REMOTE_SYSTEM_NAME + "@" + actorHost + "/user/" + BRIDGE_ACTOR_NAME) + val remoteMainActor = system.actorSelection("pekko://" + REMOTE_SYSTEM_NAME + "@" + actorHost + "/user/" + BRIDGE_ACTOR_NAME) val localMainActor = system.actorOf(Props(classOf[LocalMainActor], remoteMainActor), "local-main-actor") diff --git a/airline-web/app/websocket/Broadcaster.scala b/airline-web/app/websocket/Broadcaster.scala index 4093b2ab9..f9d7cfc81 100644 --- a/airline-web/app/websocket/Broadcaster.scala +++ b/airline-web/app/websocket/Broadcaster.scala @@ -1,7 +1,7 @@ package websocket -import akka.actor.ActorRef -import akka.event.{EventBus, LookupClassification} +import org.apache.pekko.actor.ActorRef +import org.apache.pekko.event.{EventBus, LookupClassification} import com.patson.model.Airline import com.patson.util.AirlineCache import controllers.{PendingActionUtil, PromptUtil} @@ -155,4 +155,3 @@ object Broadcaster { // // } //} - diff --git a/airline-web/app/websocket/MyWebsocketActor.scala b/airline-web/app/websocket/MyWebsocketActor.scala index 4ac939acf..abad94824 100644 --- a/airline-web/app/websocket/MyWebsocketActor.scala +++ b/airline-web/app/websocket/MyWebsocketActor.scala @@ -1,8 +1,8 @@ package websocket import java.util.concurrent.TimeUnit -import akka.actor._ -import akka.util.Timeout +import org.apache.pekko.actor._ +import org.apache.pekko.util.Timeout import com.patson.data.{CycleSource, UserSource} import com.patson.model.{UserModifier, UserStatus} import com.patson.model.notice.{AirlineNotice, LoyalistNotice, NoticeCategory} @@ -75,7 +75,7 @@ class MyWebSocketActor(out: ActorRef, airlineId : Int, remoteAddress : String) e case _ : NumberFormatException => println("Received websocket message " + airlineId + " which is not numeric!") } case any => - println("received " + any + " not handled") + println("received " + any + " not handled") } override def postStop() = { @@ -94,4 +94,3 @@ class MyWebSocketActor(out: ActorRef, airlineId : Int, remoteAddress : String) e } } } - diff --git a/airline-web/app/websocket/chat/ChatClientActor.scala b/airline-web/app/websocket/chat/ChatClientActor.scala index c5f5d0a55..e2bbfeb89 100644 --- a/airline-web/app/websocket/chat/ChatClientActor.scala +++ b/airline-web/app/websocket/chat/ChatClientActor.scala @@ -1,6 +1,6 @@ package websocket.chat -import akka.actor._ +import org.apache.pekko.actor._ import java.util.Calendar import java.text.SimpleDateFormat @@ -25,9 +25,9 @@ class ChatClientActor(out: ActorRef, chatControllerActor: ActorRef, val user : U override def postStop() = { logger.info("Stopping chat client on user " + user.userName + " id " + user.id) } - + val allianceIdOption = getAllianceId(user) - + val sdf = new SimpleDateFormat("HH:mm:ss") @@ -82,8 +82,8 @@ class ChatClientActor(out: ActorRef, chatControllerActor: ActorRef, val user : U } } - - + + // handles message writes to websocket case OutgoingMessage(chatMessage, latest) => { if (chatMessage.roomId == GENERAL_ROOM_ID || (Some(chatMessage.roomId) == allianceIdOption)) { @@ -120,7 +120,7 @@ class ChatClientActor(out: ActorRef, chatControllerActor: ActorRef, val user : U out ! "ping" } } - + def getAllianceId(user : User) : Option[Int] = { if (user.getAccessibleAirlines().isEmpty) { None diff --git a/airline-web/app/websocket/chat/ChatControllerActor.scala b/airline-web/app/websocket/chat/ChatControllerActor.scala index b4a53b515..39cdfb399 100644 --- a/airline-web/app/websocket/chat/ChatControllerActor.scala +++ b/airline-web/app/websocket/chat/ChatControllerActor.scala @@ -3,8 +3,8 @@ package websocket.chat import java.time.Duration import java.util.concurrent.atomic.AtomicLong import java.util.concurrent.{ConcurrentHashMap, Executors, TimeUnit} -import akka.actor._ -import akka.stream.ActorMaterializer +import org.apache.pekko.actor._ +import org.apache.pekko.stream.ActorMaterializer import com.patson.data.{AllianceSource, ChatSource} import com.patson.model.chat.ChatMessage import com.patson.model.{Airline, AllianceRole, User} @@ -35,7 +35,7 @@ final case class PreviousMessagesResponse(previousMessages : List[ChatMessage]) /** * a single actor that handles when a ClientActor joins or leaves - * + * * When a message is received from a ClientActor it would notify this actor, and this actor will send it out to all the corresponding subscribers (ClientActors) */ @@ -126,11 +126,11 @@ class ChatControllerActor extends Actor { val lastMessageIdOption = ChatSource.getLastChatId(user.id) - clientActors += sender - context.watch(sender) - ChatControllerActor.addActiveUser(sender, user) + clientActors += sender() + context.watch(sender()) + ChatControllerActor.addActiveUser(sender(), user) //You can turn these loggers off if needed - logger.info("Chat socket connected " + sender + " for user " + user.userName + " current active sessions : " + clientActors.size + " unique users : " + ChatControllerActor.getActiveUsers().size) + logger.info("Chat socket connected " + sender() + " for user " + user.userName + " current active sessions : " + clientActors.size + " unique users : " + ChatControllerActor.getActiveUsers().size) // resend the Archived Message val allianceArchivedMessages = @@ -146,13 +146,13 @@ class ChatControllerActor extends Actor { val generalMessageSource = if (user.isChatBanned) penaltyBoxMessageHistory else generalMessageHistory val sessionStart = getSessionStartMessage(generalMessageSource, allianceArchivedMessages, lastMessageIdOption) - sender ! sessionStart + sender() ! sessionStart } case PreviousMessagesRequest(airline, previousFirstMessageId, roomId) => - sender ! buildPreviousMessagesResponse(airline, previousFirstMessageId, roomId) + sender() ! buildPreviousMessagesResponse(airline, previousFirstMessageId, roomId) // case Leave => { // context become process(subscribers - sender) diff --git a/airline-web/app/websocket/package.scala b/airline-web/app/websocket/package.scala index d5136e6a4..239dd132e 100644 --- a/airline-web/app/websocket/package.scala +++ b/airline-web/app/websocket/package.scala @@ -1,6 +1,6 @@ -import akka.actor.ActorSystem +import org.apache.pekko.actor.ActorSystem import scala.concurrent.ExecutionContext package object websocket { diff --git a/airline-web/build.sbt b/airline-web/build.sbt index 0b36665bd..cb84893d4 100644 --- a/airline-web/build.sbt +++ b/airline-web/build.sbt @@ -4,14 +4,16 @@ version := "1.0-SNAPSHOT" lazy val root = (project in file(".")).enablePlugins(PlayScala) -scalaVersion := "2.13.0" +scalaVersion := "2.13.11" libraryDependencies ++= Seq( jdbc, ws, guice, + "com.google.inject" % "guice" % "5.1.0", + "com.google.inject.extensions" % "guice-assistedinject" % "5.1.0", specs2 % Test, - "com.typesafe.akka" %% "akka-remote" % "2.5.32", + "org.apache.pekko" %% "pekko-remote" % "1.0.3", "default" %% "airline-data" % "2.1", "com.google.api-client" % "google-api-client" % "1.30.4", "com.google.oauth-client" % "google-oauth-client-jetty" % "1.34.1", @@ -24,9 +26,6 @@ libraryDependencies ++= Seq( // https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-client libraryDependencies += "org.elasticsearch.client" % "elasticsearch-rest-high-level-client" % "7.17.2" - - - resolvers += "scalaz-bintray" at "https://dl.bintray.com/scalaz/releases" // Play provides two styles of routers, one expects its actions to be injected, the diff --git a/airline-web/conf/application.conf b/airline-web/conf/application.conf index ec76cb3a5..e05bf8ddf 100644 --- a/airline-web/conf/application.conf +++ b/airline-web/conf/application.conf @@ -43,17 +43,18 @@ play.i18n.langs = [ "en" ] # You can disable evolutions for a specific datasource if necessary # play.evolutions.db.default.enabled=false -akka { +# Artery Reference: https://pekko.apache.org/docs/pekko/current/remoting-artery.html +pekko { actor { provider = remote } remote { - retry-gate-closed-for = 1000 - enabled-transports = ["akka.remote.netty.tcp"] - netty.tcp { - hostname = "127.0.0.1" - port = 0 - tcp-keepalive = on + artery { + transport = tcp + canonical { + hostname = "127.0.0.1" + port = 0 + } } } } @@ -64,7 +65,7 @@ my-pinned-dispatcher { } mysqldb.host="localhost:3306" -airline.akka-actor.host="127.0.0.1:2552" +airline.pekko-actor.host="127.0.0.1:2552" google.apiKey="your-key-here" google.mapKey="your-key-here" diff --git a/airline-web/project/build.properties b/airline-web/project/build.properties index be4231f41..b08707359 100644 --- a/airline-web/project/build.properties +++ b/airline-web/project/build.properties @@ -1,4 +1,4 @@ #Activator-generated Properties #Sun Nov 01 21:21:12 PST 2015 template.uuid=fce2d244-c545-409f-806e-7a0c3681b306 -sbt.version=1.3.2 +sbt.version=1.9.9 diff --git a/airline-web/project/plugins.sbt b/airline-web/project/plugins.sbt index 4f895e8f3..3d0f259b6 100644 --- a/airline-web/project/plugins.sbt +++ b/airline-web/project/plugins.sbt @@ -1,7 +1,10 @@ // The Play plugin -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.7.3") +addSbtPlugin("org.playframework" % "sbt-plugin" % "3.0.5") -addSbtPlugin("com.typesafe.sbt" % "sbt-less" % "1.1.2") +addSbtPlugin("com.github.sbt" % "sbt-less" % "2.0.1") + +// Resolves similar issue to https://stackoverflow.com/questions/76693403/scala-play-dependency-issue +ThisBuild / libraryDependencySchemes += "org.scala-lang.modules" %% "scala-xml" % VersionScheme.Always // web plugins