<commit_before>package lila.study

import chess.format.pgn.{ Glyph, Glyphs }
import chess.format.{ Uci, UciCharPair, Forsyth, FEN }
import chess.Pos
import org.joda.time.DateTime
import play.api.libs.json._

import lila.common.LightUser
import lila.common.PimpedJson._
import lila.game.{ Game, GameRepo }
import lila.socket.Socket.Uid
import lila.socket.tree.Node.Shape
import lila.user.User

final class JsonView(
    lightUser: LightUser.Getter,
    gamePgnDump: lila.game.PgnDump) {

  import JsonView._

  def apply(
    study: Study,
    chapters: List[Chapter.Metadata],
    currentChapter: Chapter,
    me: Option[User]) =
    currentChapter.setup.gameId.??(GameRepo.gameWithInitialFen).map { gameOption =>
      studyWrites.writes(study) ++ Json.obj(
        "chapters" -> chapters.map(chapterMetadataWrites.writes),
        "chapter" -> Json.obj(
          "ownerId" -> currentChapter.ownerId,
          "setup" -> {
            val setup = Json toJson currentChapter.setup
            gameOption.fold(setup) { game =>
              setup.as[JsObject] ++ Json.obj("game" -> game)
            }
          },
          "game" -> gameOption,
          "conceal" -> currentChapter.conceal,
          "features" -> Json.obj(
            "computer" -> Settings.UserSelection.allows(study.settings.computer, study, me.map(_.id)),
            "explorer" -> Settings.UserSelection.allows(study.settings.explorer, study, me.map(_.id))
          )
        )
      )
    }.chronometer
      // .mon(_.fishnet.acquire time client.skill.key)
      .logIfSlow(100, logger)(_ => s"JsonView ${study.id} ${study.name}")
      .result

  private implicit val gameWrites = OWrites[(Game, Option[FEN])] {
    case (g, fen) => Json.obj(
      "id" -> g.id,
      "tags" -> PgnTags(gamePgnDump.tags(g, fen.map(_.value), none))
    )
  }

  private implicit val lightUserWrites = OWrites[LightUser] { u =>
    Json.obj("id" -> u.id, "name" -> u.name, "title" -> u.title).noNull
  }

  private[study] implicit val memberRoleWrites = Writes[StudyMember.Role] { r =>
    JsString(r.id)
  }
  private[study] implicit val memberWrites: Writes[StudyMember] = Writes[StudyMember] { m =>
    Json.obj(
      "user" -> lightUser(m.id),
      "role" -> m.role,
      "addedAt" -> m.addedAt)
  }

  private[study] implicit val membersWrites: Writes[StudyMembers] = Writes[StudyMembers] { m =>
    Json toJson m.members
  }

  private implicit val studyWrites = OWrites[Study] { s =>
    Json.obj(
      "id" -> s.id,
      "name" -> s.name,
      "members" -> s.members,
      "position" -> s.position,
      "ownerId" -> s.ownerId,
      "settings" -> s.settings,
      "visibility" -> s.visibility,
      "views" -> s.views.value,
      "createdAt" -> s.createdAt,
      "isNew" -> s.createdAt.isAfter(DateTime.now minusSeconds 4).option(true)
    ).noNull
  }
}

object JsonView {

  case class JsData(study: JsObject, analysis: JsObject, chat: JsValue)

  private implicit val uciWrites: Writes[Uci] = Writes[Uci] { u =>
    JsString(u.uci)
  }
  private implicit val uciCharPairWrites: Writes[UciCharPair] = Writes[UciCharPair] { u =>
    JsString(u.toString)
  }
  private implicit val posReader: Reads[Pos] = Reads[Pos] { v =>
    (v.asOpt[String] flatMap Pos.posAt).fold[JsResult[Pos]](JsError(Nil))(JsSuccess(_))
  }
  private[study] implicit val pathWrites: Writes[Path] = Writes[Path] { p =>
    JsString(p.toString)
  }<commit_msg>fix study form on start<commit_after>package lila.study

import chess.format.pgn.{ Glyph, Glyphs }
import chess.format.{ Uci, UciCharPair, Forsyth, FEN }
import chess.Pos
import org.joda.time.DateTime
import play.api.libs.json._

import lila.common.LightUser
import lila.common.PimpedJson._
import lila.game.{ Game, GameRepo }
import lila.socket.Socket.Uid
import lila.socket.tree.Node.Shape
import lila.user.User

final class JsonView(
    lightUser: LightUser.Getter,
    gamePgnDump: lila.game.PgnDump) {

  import JsonView._

  def apply(
    study: Study,
    chapters: List[Chapter.Metadata],
    currentChapter: Chapter,
    me: Option[User]) =
    currentChapter.setup.gameId.??(GameRepo.gameWithInitialFen).map { gameOption =>
      studyWrites.writes(study) ++ Json.obj(
        "chapters" -> chapters.map(chapterMetadataWrites.writes),
        "chapter" -> Json.obj(
          "ownerId" -> currentChapter.ownerId,
          "setup" -> {
            val setup = Json toJson currentChapter.setup
            gameOption.fold(setup) { game =>
              setup.as[JsObject] ++ Json.obj("game" -> game)
            }
          },
          "game" -> gameOption,
          "conceal" -> currentChapter.conceal,
          "features" -> Json.obj(
            "computer" -> Settings.UserSelection.allows(study.settings.computer, study, me.map(_.id)),
            "explorer" -> Settings.UserSelection.allows(study.settings.explorer, study, me.map(_.id))
          )
        )
      )
    }.chronometer
      // .mon(_.fishnet.acquire time client.skill.key)
      .logIfSlow(100, logger)(_ => s"JsonView ${study.id} ${study.name}")
      .result

  private implicit val gameWrites = OWrites[(Game, Option[FEN])] {
    case (g, fen) => Json.obj(
      "id" -> g.id,
      "tags" -> PgnTags(gamePgnDump.tags(g, fen.map(_.value), none))
    )
  }

  private implicit val lightUserWrites = OWrites[LightUser] { u =>
    Json.obj("id" -> u.id, "name" -> u.name, "title" -> u.title).noNull
  }

  private[study] implicit val memberRoleWrites = Writes[StudyMember.Role] { r =>
    JsString(r.id)
  }
  private[study] implicit val memberWrites: Writes[StudyMember] = Writes[StudyMember] { m =>
    Json.obj(
      "user" -> lightUser(m.id),
      "role" -> m.role,
      "addedAt" -> m.addedAt)
  }

  private[study] implicit val membersWrites: Writes[StudyMembers] = Writes[StudyMembers] { m =>
    Json toJson m.members
  }

  private implicit val studyWrites = OWrites[Study] { s =>
    Json.obj(
      "id" -> s.id,
      "name" -> s.name,
      "members" -> s.members,
      "position" -> s.position,
      "ownerId" -> s.ownerId,
      "settings" -> s.settings,
      "visibility" -> s.visibility,
      "views" -> s.views.value,
      "createdAt" -> s.createdAt,
      "from" -> s.from,
      "isNew" -> s.createdAt.isAfter(DateTime.now minusSeconds 4).option(true)
    ).noNull
  }
}

object JsonView {

  case class JsData(study: JsObject, analysis: JsObject, chat: JsValue)

  private implicit val uciWrites: Writes[Uci] = Writes[Uci] { u =>
    JsString(u.uci)
  }
  private implicit val uciCharPairWrites: Writes[UciCharPair] = Writes[UciCharPair] { u =>
    JsString(u.toString)
  }
  private implicit val posReader: Reads[Pos] = Reads[Pos] { v =>
    (v.asOpt[String] flatMap Pos.posAt).fold[JsResult[Pos]](JsError(Nil))(JsSuccess(_))
  }
  private[study] implicit val pathWrites: Writes[Path] = Writes[Path] { p =>
    JsString(p.toString)
  }
{'fraction_non_alphanumeric': 0.14764123471170645, 'fraction_numerical': 0.0011648223645894002, 'mean_word_length': 2.8288740245261983, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 86, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '914976', 'n_tokens_mistral': 2463, 'n_tokens_neox': 2363, 'n_words': 464}