@@ -2,13 +2,14 @@ package lila.relay
2
2
3
3
import akka .actor ._
4
4
import org .joda .time .DateTime
5
- import play .api .libs .ws .WS
5
+ import play .api .libs .json ._
6
+ import play .api .libs .ws .{ WS , WSResponse }
6
7
import play .api .Play .current
7
8
import scala .concurrent .duration ._
8
9
9
10
import lila .base .LilaException
10
- import lila .tree .Node .Comments
11
11
import lila .study .MultiPgn
12
+ import lila .tree .Node .Comments
12
13
13
14
private final class RelayFetch (
14
15
sync : RelaySync ,
@@ -130,7 +131,10 @@ private object RelayFetch {
130
131
131
132
private def doFetch (upstream : Upstream , max : Int ): Fu [RelayGames ] = (upstream match {
132
133
case Upstream .DgtOneFile (file) => dgtOneFile(file, max)
133
- case Upstream .DgtManyFiles (dir) => dgtManyFiles(dir, max)
134
+ case Upstream .DgtManyFiles (dir) =>
135
+ dgtManyFiles(dir, max, DgtMany .RoundPgn ) recoverWith {
136
+ case _ : lila.base.LilaException => dgtManyFiles(dir, max, DgtMany .Indexjson )
137
+ }
134
138
}) flatMap multiPgnToGames.apply
135
139
136
140
private def dgtOneFile (file : String , max : Int ): Fu [MultiPgn ] =
@@ -139,29 +143,68 @@ private object RelayFetch {
139
143
case res => fufail(s " [ ${res.status}] " )
140
144
}
141
145
142
- import play .api .libs .json ._
146
+ private object DgtJson {
147
+ case class PairingPlayer (fname : Option [String ], mname : Option [String ], lname : Option [String ], title : Option [String ]) {
148
+ def fullName = some {
149
+ List (fname, mname, lname).flatten mkString " "
150
+ }.filter(_.nonEmpty)
151
+ }
152
+ case class RoundJsonPairing (white : PairingPlayer , black : PairingPlayer , result : String ) {
153
+ import chess .format .pgn ._
154
+ def tags = Tags (List (
155
+ white.fullName map { v => Tag (_.White , v) },
156
+ white.title map { v => Tag (_.WhiteTitle , v) },
157
+ black.fullName map { v => Tag (_.Black , v) },
158
+ black.title map { v => Tag (_.BlackTitle , v) },
159
+ Tag (_.Result , result).some
160
+ ).flatten)
161
+ }
162
+ case class RoundJson (pairings : List [RoundJsonPairing ])
163
+ implicit val pairingPlayerReads = Json .reads[PairingPlayer ]
164
+ implicit val roundPairingReads = Json .reads[RoundJsonPairing ]
165
+ implicit val roundReads = Json .reads[RoundJson ]
143
166
144
- private case class RoundJsonPairing (live : Boolean )
145
- private case class RoundJson (pairings : List [RoundJsonPairing ])
146
- private implicit val roundPairingReads = Json .reads[RoundJsonPairing ]
147
- private implicit val roundReads = Json .reads[RoundJson ]
167
+ case class GameJson (moves : List [String ], result : Option [String ])
168
+ implicit val gameReads = Json .reads[GameJson ]
169
+ }
170
+ import DgtJson ._
171
+
172
+ private sealed abstract class DgtMany (val indexFile : String , val gameFile : Int => String , val toPgn : (WSResponse , RoundJsonPairing ) => String )
173
+ private object DgtMany {
174
+ case object RoundPgn extends DgtMany (" round.json" , n => s " game- $n.pgn " , (r, _) => r.body)
175
+ case object Indexjson extends DgtMany (" index.json" , n => s " game- $n.json " , {
176
+ case (res, pairing) => res.json.validate[GameJson ] match {
177
+ case JsSuccess (game, _) =>
178
+ val moves = game.moves.map(_ split ' ' ) map { move =>
179
+ chess.format.pgn.Move (
180
+ san = ~ move.headOption,
181
+ secondsLeft = move.lift(1 ).map(_.takeWhile(_.isDigit)) flatMap parseIntOption
182
+ )
183
+ } mkString " "
184
+ s " ${pairing.tags}\n\n $moves"
185
+ case JsError (err) => " "
186
+ }
187
+ })
188
+ }
148
189
149
- private def dgtManyFiles (dir : String , max : Int ): Fu [MultiPgn ] = {
150
- val roundUrl = s " $dir/round.json "
151
- httpGet(roundUrl ) flatMap {
190
+ private def dgtManyFiles (dir : String , max : Int , format : DgtMany ): Fu [MultiPgn ] = {
191
+ val indexFile = s " $dir/ ${format.indexFile} "
192
+ httpGet(indexFile ) flatMap {
152
193
case res if res.status == 200 => roundReads reads res.json match {
153
194
case JsError (err) => fufail(err.toString)
154
- case JsSuccess (round, _) => (1 to round.pairings.size.atMost(max)).map { number =>
155
- val gameUrl = s " $dir/game- $number.pgn "
156
- httpGet(gameUrl).flatMap {
157
- case res if res.status == 200 => fuccess(number -> res.body)
158
- case res => fufail(s " [ ${res.status}] game- $number.pgn " )
159
- }
195
+ case JsSuccess (round, _) => round.pairings.zipWithIndex.map {
196
+ case (pairing, i) =>
197
+ val number = i + 1
198
+ val gameUrl = s " $dir/ ${format.gameFile(number)}"
199
+ httpGet(gameUrl).flatMap {
200
+ case res if res.status == 200 => fuccess(number -> format.toPgn(res, pairing))
201
+ case res => fufail(s " [ ${res.status}] $gameUrl" )
202
+ }
160
203
}.sequenceFu map { results =>
161
204
MultiPgn (results.sortBy(_._1).map(_._2).toList)
162
205
}
163
206
}
164
- case res => fufail(s " [ ${res.status}] round.json " )
207
+ case res => fufail(s " [ ${res.status}] $indexFile " )
165
208
}
166
209
}
167
210
0 commit comments