diff --git a/app/search/DataForm.scala b/app/search/DataForm.scala index 64967bc8f3..115aa2059d 100644 --- a/app/search/DataForm.scala +++ b/app/search/DataForm.scala @@ -12,7 +12,11 @@ import chess.{ Mode } final class DataForm { val search = Form(mapping( - "usernames" -> optional(nonEmptyText), + "players" -> mapping( + "a" -> optional(nonEmptyText), + "b" -> optional(nonEmptyText), + "winner" -> optional(nonEmptyText) + )(SearchPlayer.apply)(SearchPlayer.unapply), "variant" -> numberIn(Query.variants), "mode" -> numberIn(Query.modes), "opening" -> stringIn(Query.openings), @@ -28,8 +32,10 @@ final class DataForm { "dateMin" -> stringIn(Query.dates), "dateMax" -> stringIn(Query.dates), "status" -> numberIn(Query.statuses), - "sortField" -> nonEmptyText.verifying(hasKey(Sorting.fields, _)), - "sortOrder" -> nonEmptyText.verifying(hasKey(Sorting.orders, _)) + "sort" -> mapping( + "field" -> nonEmptyText.verifying(hasKey(Sorting.fields, _)), + "order" -> nonEmptyText.verifying(hasKey(Sorting.orders, _)) + )(SearchSort.apply)(SearchSort.unapply) )(SearchData.apply)(SearchData.unapply)) private def numberIn(choices: Seq[(Int, String)]) = @@ -43,7 +49,7 @@ final class DataForm { } case class SearchData( - usernames: Option[String] = None, + players: SearchPlayer = SearchPlayer(), variant: Option[Int] = None, mode: Option[Int] = None, opening: Option[String] = None, @@ -59,11 +65,12 @@ case class SearchData( dateMin: Option[String] = None, dateMax: Option[String] = None, status: Option[Int] = None, - sortField: String = Sorting.default.field, - sortOrder: String = Sorting.default.order) { + sort: SearchSort = SearchSort()) { lazy val query = Query( - usernames = (~usernames).split(" ").toList map clean filter (_.nonEmpty), + user1 = players.cleanA, + user2 = players.cleanB, + winner = players.cleanWinner, variant = variant, rated = mode flatMap Mode.apply map (_.rated), opening = opening map clean, @@ -74,7 +81,7 @@ case class SearchData( duration = Range(durationMin, durationMax), date = Range(dateMin flatMap toDate, dateMax flatMap toDate), status = status, - sorting = Sorting(sortField, sortOrder) + sorting = Sorting(sort.field, sort.order) ) private def clean(s: String) = s.trim.toLowerCase @@ -88,3 +95,22 @@ case class SearchData( case _ ⇒ None } } + +case class SearchPlayer( + a: Option[String] = None, + b: Option[String] = None, + winner: Option[String] = None) { + + def cleanA = clean(a) + def cleanB = clean(b) + def cleanWinner = clean(winner) |> { w ⇒ + w filter List(a, b).flatten.contains + } + + private def clean(s: Option[String]) = + s map (_.trim.toLowerCase) filter (_.nonEmpty) +} + +case class SearchSort( + field: String = Sorting.default.field, + order: String = Sorting.default.order) diff --git a/app/search/Game.scala b/app/search/Game.scala index 72daefad09..ff7aede491 100644 --- a/app/search/Game.scala +++ b/app/search/Game.scala @@ -14,6 +14,7 @@ object Game { val rated = "ra" val variant = "va" val uids = "ui" + val winner = "wi" val averageElo = "el" val ai = "ai" val opening = "op" @@ -37,6 +38,7 @@ object Game { field(rated, "boolean"), field(variant, "short"), field(uids, "string"), + field(winner, "string"), field(averageElo, "short"), field(ai, "short"), field(opening, "string"), @@ -54,6 +56,7 @@ object Game { rated -> game.rated.some, variant -> game.variant.id.some, uids -> (game.userIds.toNel map (_.list)), + winner -> (game.winner flatMap (_.userId)), averageElo -> game.averageUsersElo, ai -> game.aiLevel, date -> (dateFormatter print createdAt).some, diff --git a/app/search/Query.scala b/app/search/Query.scala index b21b48378e..392046117a 100644 --- a/app/search/Query.scala +++ b/app/search/Query.scala @@ -9,7 +9,9 @@ import org.joda.time.DateTime import org.scala_tools.time.Imports._ case class Query( - usernames: List[String] = Nil, + user1: Option[String] = None, + user2: Option[String] = None, + winner: Option[String] = None, variant: Option[Int] = None, status: Option[Int] = None, turns: Range[Int] = Range.none, @@ -23,7 +25,9 @@ case class Query( sorting: Sorting = Sorting.default) { def nonEmpty = - usernames.nonEmpty || + user1.nonEmpty || + user2.nonEmpty || + winner.nonEmpty || variant.nonEmpty || status.nonEmpty || turns.nonEmpty || @@ -44,8 +48,11 @@ case class Query( def countRequest = CountRequest(matchAllQuery, filters) + def usernames = List(user1, user2).flatten + private def filters = List( - usernames map { u ⇒ termFilter(fields.uids, u.toLowerCase) }, + usernames map { termFilter(fields.uids, _) }, + toFilters(winner, fields.winner), turns filters fields.turns, averageElo filters fields.averageElo, duration map (60 *) filters fields.duration, @@ -108,25 +115,4 @@ object Query { } val statuses = Status.finishedNotCheated map { s ⇒ s.id -> s.name } - - def test = Query( - usernames = List("thibault"), - duration = Range(1.some, 3.some), - sorting = Sorting(fields.averageElo, "desc") - ) - def test2 = Query( - opening = "A04".some, - sorting = Sorting(fields.turns, "desc") - ) - def test3 = Query( - usernames = List("controlaltdelete"), - variant = 1.some, - turns = Range(20.some, 100.some), - averageElo = Range(1100.some, 2000.some), - opening = "A00".some, - hasAi = true.some, - aiLevel = Range.none, - date = Range(Some(DateTime.now - 1.year), none), - sorting = Sorting(fields.date, "desc") - ) } diff --git a/app/search/Sorting.scala b/app/search/Sorting.scala index 532b7fa52f..d3da65c7d7 100644 --- a/app/search/Sorting.scala +++ b/app/search/Sorting.scala @@ -21,7 +21,7 @@ object Sorting { def fieldKeys = fields map (_._1) - val orders = List(SortOrder.ASC, SortOrder.DESC) map { s ⇒ s.toString -> s.toString } + val orders = List(SortOrder.DESC, SortOrder.ASC) map { s ⇒ s.toString -> s.toString } val default = Sorting(Game.fields.date, "desc") } diff --git a/app/templating/AssetHelper.scala b/app/templating/AssetHelper.scala index d6d960902a..3dc3fa8cf3 100644 --- a/app/templating/AssetHelper.scala +++ b/app/templating/AssetHelper.scala @@ -7,7 +7,7 @@ import play.api.templates.Html trait AssetHelper { - val assetVersion = 71 + val assetVersion = 73 def cssTag(name: String) = css("stylesheets/" + name) diff --git a/app/views/search/form.scala.html b/app/views/search/form.scala.html index 1545273243..9616beb487 100644 --- a/app/views/search/form.scala.html +++ b/app/views/search/form.scala.html @@ -26,16 +26,22 @@ moreCss = moreCss) { - + + + + - - + @@ -128,8 +134,8 @@ moreCss = moreCss) {
- + - + +
@input(form("players")("a"))
+
vs @input(form("players")("b"))
+
+ + + Type up to two usernames
@@ -55,7 +61,7 @@ moreCss = moreCss) { @select(form("hasAi"), Query.hasAis, "Human or Computer".some)
-
By @select(form("sortField"), Sorting.fields)
-
Order @select(form("sortOrder"), Sorting.orders)
+
By @select(form("sort")("field"), Sorting.fields)
+
Order @select(form("sort")("order"), Sorting.orders)
@@ -137,7 +143,7 @@ moreCss = moreCss) {
@paginator.map { pager => @if(pager.nbResults > 0) { -
+
@paginator.map { pager => @pager.nbResults.localize games found } @@ -151,9 +157,11 @@ moreCss = moreCss) { @game.widgets(pager.currentPageResults)
} else { -
No game found
- } +
No game found
} + }.getOrElse { +
Search ready
+ }
} diff --git a/app/views/search/input.scala.html b/app/views/search/input.scala.html new file mode 100644 index 0000000000..48be63d07f --- /dev/null +++ b/app/views/search/input.scala.html @@ -0,0 +1,3 @@ +@(field: play.api.data.Field) + + diff --git a/app/views/search/select.scala.html b/app/views/search/select.scala.html index 29e0576b9a..8cb2ec5f6c 100644 --- a/app/views/search/select.scala.html +++ b/app/views/search/select.scala.html @@ -2,7 +2,7 @@