Skip to content

Commit

Permalink
possibility to search for analysed games only (in Games -> Advanced S…
Browse files Browse the repository at this point in the history
…earch).
  • Loading branch information
ornicar committed May 17, 2013
1 parent 8c7c1c6 commit 93abed1
Show file tree
Hide file tree
Showing 12 changed files with 97 additions and 61 deletions.
6 changes: 6 additions & 0 deletions app/views/base/checkbox.scala.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@(field: play.api.data.Field, text: String, value: Int)

<label>
@text
<input type="checkbox" id="@field.id" name="@field.name" value="@value" @((field.value == Some(value.toString)).fold("checked", "")) />
</label>
8 changes: 8 additions & 0 deletions app/views/game/search.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ <h1 class="title">@trans.advancedSearch()</h1>
<div class="half">Order @base.select(form("sort")("order"), Sorting.orders)</div>
</td>
</tr>
<tr>
<th>
<label>Analyzed</label>
</th>
<td class="single">
@{ base.checkbox(form("analyzed"), Query.analyzeds._2, 1) }
</td>
</tr>
</table>
</form>
<div class="search_result">
Expand Down
10 changes: 6 additions & 4 deletions modules/analyse/src/main/Analyser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ import lila.game.{ Game, GameRepo, PgnRepo }
import lila.db.api._
import tube.analysisTube
import lila.game.tube.gameTube
import makeTimeout.veryLarge

import akka.pattern.ask

private[analyse] final class Analyser(ai: lila.hub.ActorLazyRef) {

private implicit val timeout = makeTimeout minutes 5
final class Analyser(ai: lila.hub.ActorLazyRef) {

def get(id: String): Fu[Option[Analysis]] = $find.byId[Analysis](id)

def has(id: String): Fu[Boolean] = AnalysisRepo isDone id

def hasMany(ids: Seq[String]): Fu[Set[String]] =
$primitive[Analysis, String]($select byIds ids, "_id")(_.asOpt[String]) map (_.toSet)

def getOrGenerate(id: String, userId: String, admin: Boolean): Fu[Analysis] = {

def generate: Fu[Analysis] =
Expand All @@ -31,7 +33,7 @@ private[analyse] final class Analyser(ai: lila.hub.ActorLazyRef) {
initialFen GameRepo initialFen id
analysis {
ai ? lila.hub.actorApi.ai.Analyse(id, pgn, initialFen)
} mapTo manifest[Analysis]
} mapTo manifest[Analysis]
} yield analysis) flatFold (
e AnalysisRepo.fail(id, e).mapTo[Analysis],
a AnalysisRepo.done(id, a) >> fuccess(a)
Expand Down
3 changes: 3 additions & 0 deletions modules/gameSearch/src/main/DataForm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ private[gameSearch] final class DataForm {
"dateMin" -> optional(stringIn(Query.dates)),
"dateMax" -> optional(stringIn(Query.dates)),
"status" -> optional(numberIn(Query.statuses)),
"analyzed" -> optional(numberIn(Query.analyzeds :: Nil)),
"sort" -> mapping(
"field" -> stringIn(Sorting.fields),
"order" -> stringIn(Sorting.orders)
Expand All @@ -57,6 +58,7 @@ private[gameSearch] case class SearchData(
dateMin: Option[String] = None,
dateMax: Option[String] = None,
status: Option[Int] = None,
analyzed: Option[Int] = None,
sort: SearchSort = SearchSort()) {

lazy val query = Query(
Expand All @@ -73,6 +75,7 @@ private[gameSearch] case class SearchData(
duration = Range(durationMin, durationMax),
date = Range(dateMin flatMap toDate, dateMax flatMap toDate),
status = status,
analyzed = analyzed map (_ == 1),
sorting = Sorting(sort.field, sort.order)
)

Expand Down
48 changes: 27 additions & 21 deletions modules/gameSearch/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import org.elasticsearch.action.search.SearchResponse
final class Env(
config: Config,
system: ActorSystem,
esIndexer: Fu[EsIndexer]) {
esIndexer: Fu[EsIndexer],
analyser: lila.analyse.Analyser) {

private val IndexName = config getString "index"
private val TypeName = config getString "type"
Expand All @@ -30,7 +31,8 @@ final class Env(
)), name = IndexerName + "-low-level")

private val indexer: ActorRef = system.actorOf(Props(new Indexer(
lowLevel = lowLevelIndexer
lowLevel = lowLevelIndexer,
isAnalyzed = analyser.has _
)), name = IndexerName)

lazy val paginator = new lila.search.PaginatorBuilder(
Expand Down Expand Up @@ -68,27 +70,30 @@ final class Env(
esIndexer map { es
$enumerate.bulk[Option[GameModel]](query, batchSize) { gameOptions
val games = gameOptions.flatten
val gameIds = games.map(_.id).toSeq
val nbGames = games.size
nb = nb + nbGames
PgnRepo.associate(games.map(_.id).toSeq) map { pgns
val pairs = (pgns map {
case (id, pgn) games.find(_.id == id) map (_ -> pgn)
}).flatten
es bulk_send {
(pairs map {
case (game, pgn) es.index_prepare(
IndexName,
TypeName,
game.id,
Json stringify Game.from(game, pgn)
).request
})
PgnRepo.associate(gameIds) flatMap { pgns
analyser hasMany gameIds map { analyzedIds
val pairs = (pgns map {
case (id, pgn) games.find(_.id == id) map (_ -> pgn)
}).flatten
es bulk_send {
(pairs map {
case (game, pgn) es.index_prepare(
IndexName,
TypeName,
game.id,
Json stringify Game.from(game, pgn, analyzedIds contains game.id)
).request
})
}
nbSkipped = nbSkipped + nbGames - pairs.size
val perMs = batchSize / (nowMillis - started)
started = nowMillis
loginfo("[game search] Indexed %d of %d, skipped %d, at %d/s".format(
nb, size, nbSkipped, math.round(perMs * 1000)))
}
nbSkipped = nbSkipped + nbGames - pairs.size
val perMs = batchSize / (nowMillis - started)
started = nowMillis
loginfo("[game search] Indexed %d of %d, skipped %d, at %d/s".format(
nb, size, nbSkipped, math.round(perMs * 1000)))
}
}
}
Expand All @@ -100,5 +105,6 @@ object Env {
lazy val current = "[boot] gameSearch" describes new Env(
config = lila.common.PlayApp loadConfig "gameSearch",
system = lila.common.PlayApp.system,
esIndexer = lila.search.Env.current.esIndexer)
esIndexer = lila.search.Env.current.esIndexer,
analyser = lila.analyse.Env.current.analyser)
}
9 changes: 6 additions & 3 deletions modules/gameSearch/src/main/Game.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ private[gameSearch] object Game {
val opening = "op"
val date = "da"
val duration = "du"
val analyzed = "an"
}
import fields._

Expand Down Expand Up @@ -53,12 +54,13 @@ private[gameSearch] object Game {
field(ai, "short"),
field(opening, "string"),
field(date, "date", attrs = Json.obj("format" -> Date.format)),
field(duration, "short")
field(duration, "short"),
field(analyzed, "boolean")
))
)
}

def from(game: GameModel, pgn: String): JsObject = Json.obj(
def from(game: GameModel, pgn: String, anal: Boolean): JsObject = Json.obj(
status -> game.status.is(_.Timeout).fold(Status.Resign, game.status).id,
turns -> math.ceil(game.turns.toFloat / 2),
rated -> game.rated,
Expand All @@ -69,6 +71,7 @@ private[gameSearch] object Game {
ai -> Json.toJson(game.aiLevel),
date -> (Date.formatter print game.createdAt),
duration -> game.estimateTotalTime,
opening -> Json.toJson(OpeningExplorer openingOf pgn map (_.code.toLowerCase))
opening -> Json.toJson(OpeningExplorer openingOf pgn map (_.code.toLowerCase)),
analyzed -> anal
)
}
8 changes: 6 additions & 2 deletions modules/gameSearch/src/main/Indexer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import lila.search.{ actorApi ⇒ S }

import akka.actor._

private[gameSearch] final class Indexer(lowLevel: ActorRef) extends Actor {
private[gameSearch] final class Indexer(
lowLevel: ActorRef,
isAnalyzed: String Fu[Boolean]) extends Actor {

def receive = {

case InsertGame(game) PgnRepo getOption game.id foreach {
_ foreach { pgn
lowLevel ! S.InsertOne(game.id, Game.from(game, pgn))
isAnalyzed(game.id) foreach { analyzed
lowLevel ! S.InsertOne(game.id, Game.from(game, pgn, analyzed))
}
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions modules/gameSearch/src/main/Query.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ case class Query(
opening: Option[String] = None,
date: Range[DateTime] = Range.none,
duration: Range[Int] = Range.none,
sorting: Sorting = Sorting.default) extends lila.search.Query {
sorting: Sorting = Sorting.default,
analyzed: Option[Boolean] = None) extends lila.search.Query {

def nonEmpty =
user1.nonEmpty ||
Expand Down Expand Up @@ -63,7 +64,8 @@ case class Query(
toFilters(variant, fields.variant),
toFilters(rated, fields.rated),
toFilters(opening, fields.opening),
toFilters(status, fields.status)
toFilters(status, fields.status),
toFilters(analyzed, fields.analyzed)
).flatten.toNel map { fs andFilter(fs.list: _*) }

private def hasAiFilters = hasAi.toList map { a
Expand Down Expand Up @@ -100,6 +102,8 @@ object Query {

val aiLevels = (1 to 8) map { l l -> ("Stockfish level " + l) }

val analyzeds = 1 -> "Analysis available"

val dates = List("0d" -> "Now") ++
options(List(1, 2, 6), "h", "%d hour{s} ago") ++
options(1 to 6, "d", "%d day{s} ago") ++
Expand Down
4 changes: 1 addition & 3 deletions modules/security/src/main/UserSpy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@ private[security] object UserSpy {

private def explore(users: Set[User], ips: Set[IP], _users: Set[User]): Fu[Set[User]] = {
nextIps(users, ips) flatMap { nIps
nextUsers(nIps, users) map { nUsers
nUsers ++: users ++: _users
}
nextUsers(nIps, users) map { _ ++: users ++: _users }
}
}

Expand Down
2 changes: 1 addition & 1 deletion project/Build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ object ApplicationBuild extends Build {
play.api, reactivemongo, playReactivemongo, spray.caching)
)

lazy val gameSearch = project("gameSearch", Seq(common, hub, chess, search, game)).settings(
lazy val gameSearch = project("gameSearch", Seq(common, hub, chess, search, game, analyse)).settings(
libraryDependencies ++= provided(
play.api, reactivemongo, playReactivemongo, scalastic)
)
Expand Down
50 changes: 27 additions & 23 deletions public/javascripts/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,47 @@ $(function() {
function realtimeResults() {
$("div.search_status").text("Searching...");
$result.load(
$form.attr("action") + "?" + $form.serialize() + " .search_result",
function(text, status) {
if (status == "error") {
$(".search_status").text("Something is wrong with the search engine!");
} else {
$('body').trigger('lichess.content_loaded');
var $permalink = $result.find("a.permalink");
$permalink.attr("href", $permalink.attr("href") + "?" + $form.serialize());
$result.find('.search_infinitescroll:has(.pager a)').each(function() {
var $next = $(this).find(".pager a:last")
$next.attr("href", $next.attr("href") + "&" + $form.serialize());
$form.attr("action") + "?" + $form.serialize() + " .search_result", function(text, status) {
if (status == "error") {
$(".search_status").text("Something is wrong with the search engine!");
} else {
$('body').trigger('lichess.content_loaded');
var $permalink = $result.find("a.permalink");
$permalink.attr("href", $permalink.attr("href") + "?" + $form.serialize());
$result.find('.search_infinitescroll:has(.pager a)').each(function() {
var $next = $(this).find(".pager a:last")
$next.attr("href", $next.attr("href") + "&" + $form.serialize());
$(this).infinitescroll({
navSelector: ".pager",
nextSelector: $next,
itemSelector: ".search_infinitescroll .paginated_element",
loading: {
msgText: "",
img: "/assets/images/hloader3.gif",
finishedMsg: "---"
img: "/assets/images/hloader3.gif",
finishedMsg: "---"
}
}, function() {
$("#infscr-loading").remove();
$('body').trigger('lichess.content_loaded');
});
});
}
});
});
}
});
}

function winnerChoices() {
var options = ["<option value=''></option>"];
$usernames.each(function() {
var user = $.trim($(this).val());
if (user.length > 1) {
options.push("<option value='"+user+"'>"+user+"</option>");
options.push("<option value='" + user + "'>" + user + "</option>");
}
});
$winner.html(options.join(""));
$winnerRow.toggle(options.length > 1);
}

$form.find("select").change(realtimeResults);
$form.find("select, input[type=checkbox]").change(realtimeResults);
$usernames.bind("keyup", winnerChoices).trigger("keyup");
$usernames.bindWithDelay("keyup", realtimeResults, 400);

Expand All @@ -60,9 +59,9 @@ $(function() {
});

// https://github.com/bgrins/bindWithDelay/blob/master/bindWithDelay.js
$.fn.bindWithDelay = function( type, data, fn, timeout, throttle ) {
$.fn.bindWithDelay = function(type, data, fn, timeout, throttle) {

if ( $.isFunction( data ) ) {
if ($.isFunction(data)) {
throttle = timeout;
timeout = fn;
fn = data;
Expand All @@ -78,15 +77,20 @@ $.fn.bindWithDelay = function( type, data, fn, timeout, throttle ) {
var wait = null;

function cb() {
var e = $.extend(true, { }, arguments[0]);
var e = $.extend(true, {}, arguments[0]);
var ctx = this;
var throttler = function() {
wait = null;
fn.apply(ctx, [e]);
};

if (!throttle) { clearTimeout(wait); wait = null; }
if (!wait) { wait = setTimeout(throttler, timeout); }
if (!throttle) {
clearTimeout(wait);
wait = null;
}
if (!wait) {
wait = setTimeout(throttler, timeout);
}
}

cb.guid = fn.guid;
Expand Down
2 changes: 0 additions & 2 deletions todo
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,13 @@ show forum search everywhere
better admin tools for team forums http:https://en.lichess.org/inbox/r8scbx64
optional email http:https://en.lichess.org/forum/lichess-feedback/secret-question----password-recovery
answer http:https://en.lichess.org/forum/lichess-feedback/expanded-chat-functionality-perhaps
FIX TOUCHPAD SIGN IN http:https://en.lichess.org/forum/lichess-feedback/cant-sign-in#6
chess variants https://github.com/ornicar/lila/issues/2:https://github.com/ornicar/lila/issues/25
publish scalastic 0.90.0-thib
stream game export
show fen only after game is finished http:https://en.lichess.org/forum/lichess-feedback/please-disable-live-fen-notation?page=1
I owe the admins decent tools
tell opponent chat is disabled
from MoralIntentions email:
- The possibility to search for analysed games only (in Games -> Advanced Search).
- The possibility to use a practice board to play against yourself or to demonstrate something when you're giving chess lessons with your laptop or an interactive whiteboard (with the same features as and with a button beneath "Play with the machine" in Play).
- A time control option of Simple delay and/or Bronstein delay (http:https://en.wikipedia.org/wiki/Time_control).
- The possibility to give other players a "thumbs up" or a "thumbs down" on their profile (which is only visible to the player who has given the thumbs up or thumbs down) so you can filter games (in Play) based on thumbs up (some players lack sportsmanship, this is a way to avoid them). It would be great if we could also filter "Who is online" (in People) that way, so we can see if it's likely that we'll find a fun opponent to play against.
Expand Down

0 comments on commit 93abed1

Please sign in to comment.