Skip to content

Commit

Permalink
implement study explorer quotes
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Sep 19, 2017
1 parent 8430800 commit d6a2e30
Show file tree
Hide file tree
Showing 11 changed files with 151 additions and 124 deletions.
15 changes: 6 additions & 9 deletions app/controllers/Importer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,13 @@ object Importer extends LilaController {
)
}

import lila.game.GameRepo
import org.joda.time.DateTime
private val masterGameEncodingFixedAt = new DateTime(2016, 3, 9, 0, 0)

def masterGame(id: String, orientation: String) = Open { implicit ctx =>
def redirectAtFen(game: lila.game.Game) = Redirect {
val url = routes.Round.watcher(game.id, orientation).url
val fenParam = get("fen").??(f => s"?fen=$f")
s"$url$fenParam"
Env.explorer.importer(id) map {
_ ?? { game =>
val url = routes.Round.watcher(game.id, orientation).url
val fenParam = get("fen").??(f => s"?fen=$f")
Redirect(s"$url$fenParam")
}
}
Env.explorer.importer(id) map2 redirectAtFen
}
}
9 changes: 7 additions & 2 deletions modules/explorer/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.typesafe.config.Config
final class Env(
config: Config,
gameColl: lila.db.dsl.Coll,
importer: lila.importer.Importer,
gameImporter: lila.importer.Importer,
system: ActorSystem
) {

Expand All @@ -18,6 +18,11 @@ final class Env(
internalEndpoint = InternalEndpoint
)

lazy val importer = new ExplorerImporter(
endpoint = InternalEndpoint,
gameImporter = gameImporter
)

def cli = new lila.common.Cli {
def process = {
case "explorer" :: "index" :: since :: Nil => indexer(since) inject "done"
Expand All @@ -36,7 +41,7 @@ object Env {
lazy val current = "explorer" boot new Env(
config = lila.common.PlayApp loadConfig "explorer",
gameColl = lila.game.Env.current.gameColl,
importer = lila.importer.Env.current.importer,
gameImporter = lila.importer.Env.current.importer,
system = lila.common.PlayApp.system
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import org.joda.time.DateTime
import lila.game.{ Game, GameRepo }
import lila.importer.{ Importer, ImportData }

final class ExplorerImport(
final class ExplorerImporter(
endpoint: String,
importer: Importer
gameImporter: Importer
) {

private val masterGameEncodingFixedAt = new DateTime(2016, 3, 9, 0, 0)
Expand All @@ -17,7 +17,7 @@ final class ExplorerImport(
case Some(game) if game.createdAt.isAfter(masterGameEncodingFixedAt) => fuccess(game.some)
case _ => (GameRepo remove id) >> fetchPgn(id) flatMap {
case None => fuccess(none)
case Some(pgn) => importer(
case Some(pgn) => gameImporter(
ImportData(pgn, none),
user = "lichess".some,
forceId = id.some
Expand Down
23 changes: 10 additions & 13 deletions modules/importer/src/main/DataForm.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,45 +39,42 @@ case class ImportData(pgn: String, analyse: Option[String]) {
case ParsedPgn(_, _, sans) if sans.size > maxPlies => !!("Replay is too long")
case parsed @ ParsedPgn(_, tags, sans) => Reader.full(pgn) map {
case replay @ Replay(setup, _, game) =>
def tag(which: Tag.type => TagType): Option[String] =
tags find (_.name == which(Tag)) map (_.value)

val initBoard = tag(_.FEN) flatMap Forsyth.<< map (_.board)
val fromPosition = initBoard.nonEmpty && tag(_.FEN) != Forsyth.initial.some
val initBoard = parsed.tag(_.FEN) flatMap Forsyth.<< map (_.board)
val fromPosition = initBoard.nonEmpty && parsed.tag(_.FEN) != Forsyth.initial.some
val variant = {
tag(_.Variant).map(Chess960.fixVariantName).flatMap(chess.variant.Variant.byName) | {
parsed.tag(_.Variant).map(Chess960.fixVariantName).flatMap(chess.variant.Variant.byName) | {
if (fromPosition) chess.variant.FromPosition
else chess.variant.Standard
}
} match {
case chess.variant.Chess960 if !Chess960.isStartPosition(setup.board) => chess.variant.FromPosition
case chess.variant.FromPosition if tag(_.FEN).isEmpty => chess.variant.Standard
case chess.variant.FromPosition if parsed.tag(_.FEN).isEmpty => chess.variant.Standard
case v => v
}
val initialFen = tag(_.FEN) flatMap {
val initialFen = parsed.tag(_.FEN) flatMap {
Forsyth.<<<@(variant, _)
} map Forsyth.>> map FEN.apply

val status = tag(_.Termination).map(_.toLowerCase) match {
val status = parsed.tag(_.Termination).map(_.toLowerCase) match {
case Some("normal") | None => Status.Resign
case Some("abandoned") => Status.Aborted
case Some("time forfeit") => Status.Outoftime
case Some("rules infraction") => Status.Cheat
case Some(_) => Status.UnknownFinish
}

val result = tag(_.Result) ifFalse game.situation.end collect {
val result = parsed.tag(_.Result) ifFalse game.situation.end collect {
case "1-0" => Result(status, Color.White.some)
case "0-1" => Result(status, Color.Black.some)
case "*" => Result(Status.Started, none)
case "1/2-1/2" if status == Status.Outoftime => Result(status, none)
case "1/2-1/2" => Result(Status.Draw, none)
}

val date = tag(_.Date)
val date = parsed.tag(_.Date)

def name(whichName: TagPicker, whichRating: TagPicker): String = tag(whichName).fold("?") { n =>
n + ~tag(whichRating).map(e => s" (${e take 8})")
def name(whichName: TagPicker, whichRating: TagPicker): String = parsed.tag(whichName).fold("?") { n =>
n + ~parsed.tag(whichRating).map(e => s" (${e take 8})")
}

val dbGame = Game.make(
Expand Down
8 changes: 8 additions & 0 deletions modules/study/src/main/Env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ final class Env(
lightUserApi: lila.user.LightUserApi,
gamePgnDump: lila.game.PgnDump,
importer: lila.importer.Importer,
explorerImporter: lila.explorer.ExplorerImporter,
evalCacheHandler: lila.evalCache.EvalCacheSocketHandler,
notifyApi: lila.notify.NotifyApi,
getPref: User => Fu[lila.pref.Pref],
Expand Down Expand Up @@ -84,6 +85,11 @@ final class Env(
domain = NetDomain
)

private lazy val explorerGame = new ExplorerGame(
importer = explorerImporter,
lightUser = lightUserApi.sync
)

private lazy val studyMaker = new StudyMaker(
lightUser = lightUserApi.sync,
chapterMaker = chapterMaker
Expand All @@ -104,6 +110,7 @@ final class Env(
studyMaker = studyMaker,
inviter = studyInvite,
tagsFixer = new ChapterTagsFixer(chapterRepo, gamePgnDump),
explorerGameHandler = explorerGame,
lightUser = lightUserApi.sync,
scheduler = system.scheduler,
chat = hub.actor.chat,
Expand Down Expand Up @@ -154,6 +161,7 @@ object Env {
lightUserApi = lila.user.Env.current.lightUserApi,
gamePgnDump = lila.game.Env.current.pgnDump,
importer = lila.importer.Env.current.importer,
explorerImporter = lila.explorer.Env.current.importer,
evalCacheHandler = lila.evalCache.Env.current.socketHandler,
notifyApi = lila.notify.Env.current.api,
getPref = lila.pref.Env.current.api.getPref,
Expand Down
50 changes: 29 additions & 21 deletions modules/study/src/main/ExplorerGame.scala
Original file line number Diff line number Diff line change
@@ -1,37 +1,45 @@
package lila.study

import lila.game.{ Game, Namer, PgnImport }
import lila.user.User
import org.joda.time.DateTime
import scala.util.Try

import chess.format.pgn.{ Parser, Reader, ParsedPgn, Tag, TagType }
import lila.common.LightUser
import lila.game.{ Game, Namer }
import lila.tree.Node.Comment
import lila.user.User

private final class ExplorerGame(
importer: lila.explorer.ExplorerImport
importer: lila.explorer.ExplorerImporter,
lightUser: LightUser.GetterSync
) {

def quote(userId: User.ID, study: Study, chapter: Chapter, path: Path, gameId: Game.ID): Fu[Comment] =
importer(gameId) flatMap {
_.fold(false) { game =>

val comment = Comment(
def quote(userId: User.ID, gameId: Game.ID): Fu[Option[Comment]] =
lightUser(userId) ?? { author =>
importer(gameId) map {
_ ?? { game =>
Comment(
id = Comment.Id.make,
text = game.pgnImport.fold(lichessTitle)(importTitle(game)),
text = Comment.Text(gameTitle(game)),
by = Comment.Author.User(author.id, author.titleName)
)
).some
}
}
}

private def importTitle(g: Game)(pgnImport: PgnImport): String =
Parser.full(pgnImport.pgn) flatMap {
case ParsedPgn(_, tags, _) =>
def tag(which: Tag.type => TagType): Option[String] =
tags find (_.name == which(Tag)) map (_.value)
private def gameYear(pgn: Option[ParsedPgn], g: Game): Int = pgn.flatMap { p =>
p.tag(_.UTCDate) orElse p.tag(_.Date)
}.flatMap { pgnDate =>
Try(DateTime.parse(pgnDate, Tag.UTCDate.format)).toOption map (_.getYear)
} | g.createdAt.getYear

ImportData(pgnImport.pgn, none).preprocess(none).fold(
_ => lichessTitle(g),
processed =>
val players = Namer.vsText(game, withRatings = true)
val result = chess.Color.showResult(game.winnerColor)
val text = s"$players, $result
private def gameTitle(g: Game): String = {
val pgn = g.pgnImport.flatMap(pgnImport => Parser.full(pgnImport.pgn).toOption)
val white = pgn.flatMap(_.tag(_.White)) | Namer.playerText(g.whitePlayer)(lightUser)
val black = pgn.flatMap(_.tag(_.Black)) | Namer.playerText(g.blackPlayer)(lightUser)
val result = chess.Color.showResult(g.winnerColor)
val event = pgn.flatMap(_.tag(_.Event)) | gameYear(pgn, g).toString
s"$white - $black, $result, $event"
}

def insert(userId: User.ID, study: Study, chapter: Chapter, gameId: Game.ID) = ???
Expand Down
111 changes: 66 additions & 45 deletions modules/study/src/main/StudyApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ final class StudyApi(
chapterMaker: ChapterMaker,
inviter: StudyInvite,
tagsFixer: ChapterTagsFixer,
explorerGameHandler: ExplorerGame,
lightUser: lila.common.LightUser.GetterSync,
scheduler: akka.actor.Scheduler,
chat: ActorSelection,
Expand Down Expand Up @@ -332,66 +333,60 @@ final class StudyApi(

def setComment(userId: User.ID, studyId: Study.Id, position: Position.Ref, text: Comment.Text, uid: Uid) = sequenceStudyWithChapter(studyId, position.chapterId) {
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
(study.members get userId) ?? { byMember =>
lightUser(userId) ?? { author =>
val comment = Comment(
id = Comment.Id.make,
text = text,
by = Comment.Author.User(author.id, author.titleName)
)
chapter.setComment(comment, position.path) match {
case Some(newChapter) =>
studyRepo.updateNow(study)
newChapter.root.nodeAt(position.path) ?? { node =>
node.comments.findBy(comment.by) ?? { c =>
chapterRepo.setComments(newChapter, position.path, node.comments.filterEmpty) >>- {
sendTo(study, Socket.SetComment(position, c, uid))
indexStudy(study)
sendStudyEnters(study, userId)
}
lightUser(userId) ?? { author =>
val comment = Comment(
id = Comment.Id.make,
text = text,
by = Comment.Author.User(author.id, author.titleName)
)
chapter.setComment(comment, position.path) match {
case Some(newChapter) =>
studyRepo.updateNow(study)
newChapter.root.nodeAt(position.path) ?? { node =>
node.comments.findBy(comment.by) ?? { c =>
chapterRepo.setComments(newChapter, position.path, node.comments.filterEmpty) >>- {
sendTo(study, Socket.SetComment(position, c, uid))
indexStudy(study)
sendStudyEnters(study, userId)
}
}
case None =>
fufail(s"Invalid setComment $studyId $position") >>-
reloadUidBecauseOf(study, uid, chapter.id)
}
}
case None =>
fufail(s"Invalid setComment $studyId $position") >>-
reloadUidBecauseOf(study, uid, chapter.id)
}
}
}
}

def deleteComment(userId: User.ID, studyId: Study.Id, position: Position.Ref, id: Comment.Id, uid: Uid) = sequenceStudyWithChapter(studyId, position.chapterId) {
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
(study.members get userId) ?? { byMember =>
chapter.deleteComment(id, position.path) match {
case Some(newChapter) =>
chapterRepo.update(newChapter) >>-
sendTo(study, Socket.DeleteComment(position, id, uid)) >>-
indexStudy(study)
case None =>
fufail(s"Invalid deleteComment $studyId $position $id") >>-
reloadUidBecauseOf(study, uid, chapter.id)
}
chapter.deleteComment(id, position.path) match {
case Some(newChapter) =>
chapterRepo.update(newChapter) >>-
sendTo(study, Socket.DeleteComment(position, id, uid)) >>-
indexStudy(study)
case None =>
fufail(s"Invalid deleteComment $studyId $position $id") >>-
reloadUidBecauseOf(study, uid, chapter.id)
}
}
}

def toggleGlyph(userId: User.ID, studyId: Study.Id, position: Position.Ref, glyph: Glyph, uid: Uid) = sequenceStudyWithChapter(studyId, position.chapterId) {
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
(study.members get userId) ?? { byMember =>
chapter.toggleGlyph(glyph, position.path) match {
case Some(newChapter) =>
studyRepo.updateNow(study)
newChapter.root.nodeAt(position.path) ?? { node =>
chapterRepo.setGlyphs(newChapter, position.path, node.glyphs) >>-
newChapter.root.nodeAt(position.path).foreach { node =>
sendTo(study, Socket.SetGlyphs(position, node.glyphs, uid))
}
}
case None =>
fufail(s"Invalid toggleGlyph $studyId $position $glyph") >>-
reloadUidBecauseOf(study, uid, chapter.id)
}
chapter.toggleGlyph(glyph, position.path) match {
case Some(newChapter) =>
studyRepo.updateNow(study)
newChapter.root.nodeAt(position.path) ?? { node =>
chapterRepo.setGlyphs(newChapter, position.path, node.glyphs) >>-
newChapter.root.nodeAt(position.path).foreach { node =>
sendTo(study, Socket.SetGlyphs(position, node.glyphs, uid))
}
}
case None =>
fufail(s"Invalid toggleGlyph $studyId $position $glyph") >>-
reloadUidBecauseOf(study, uid, chapter.id)
}
}
}
Expand All @@ -412,6 +407,32 @@ final class StudyApi(
}
}

def explorerGame(userId: User.ID, studyId: Study.Id, data: actorApi.ExplorerGame, uid: Uid) = sequenceStudyWithChapter(studyId, data.position.chapterId) {
case Study.WithChapter(study, chapter) => Contribute(userId, study) {
if (data.insert) ???
else explorerGameHandler.quote(userId, data.gameId) flatMap {
_ ?? { comment =>
chapter.setComment(comment, data.position.path) match {
case Some(newChapter) =>
studyRepo.updateNow(study)
newChapter.root.nodeAt(data.position.path) ?? { node =>
node.comments.findBy(comment.by) ?? { c =>
chapterRepo.setComments(newChapter, data.position.path, node.comments.filterEmpty) >>- {
sendTo(study, Socket.SetComment(data.position, c, uid))
indexStudy(study)
sendStudyEnters(study, userId)
}
}
}
case None =>
fufail(s"Invalid explorerGame quote $studyId $data") >>-
reloadUidBecauseOf(study, uid, chapter.id)
}
}
}
}
}

def addChapter(byUserId: User.ID, studyId: Study.Id, data: ChapterMaker.Data, sticky: Boolean, socket: ActorRef, uid: Uid) = sequenceStudy(studyId) { study =>
Contribute(byUserId, study) {
chapterRepo.nextOrderByStudy(study.id) flatMap { order =>
Expand Down
19 changes: 0 additions & 19 deletions modules/study/src/main/StudyCommenter.scala

This file was deleted.

Loading

0 comments on commit d6a2e30

Please sign in to comment.