<commit_before>package io.mediachain.rpc.operations

import java.util.UUID

import cats.data.Xor
import gremlin.scala._
import io.mediachain.Types._
import io.mediachain.core.GraphError.CanonicalNotFound
import io.mediachain.util.JsonUtils
import org.json4s._
import io.mediachain.rpc.TypeConversions._
import io.mediachain.rpc.{Types => RPCTypes}
import io.mediachain.rpc.Services._


object CanonicalQueries {
  import io.mediachain.Traversals, Traversals._, Traversals.Implicits._
  import org.json4s.JsonDSL._
  implicit val formats = DefaultFormats

  val PAGE_SIZE = 20

  def blobToJObject(metadataBlob: MetadataBlob): JObject =
    JsonUtils.jsonObjectForHashable(metadataBlob) ~
      ("multiHash" -> metadataBlob.multiHash.base58)


  def vertexToMetadataBlob(vertex: Vertex): Option[MetadataBlob] =
    vertex.label match {
      case "ImageBlob" => Some(vertex.toCC[ImageBlob])
      case "Person" => Some(vertex.toCC[Person])
      case "RawMetadataBlob" => Some(vertex.toCC[RawMetadataBlob])
      case _ => None
    }

  def canonicalWithRootRevision(graph: Graph, canonical: Canonical, withRaw: Boolean = false)
  : Option[CanonicalWithRootRevision] = {
    // FIXME - use decent graph traversal
    val gs = graph.V ~> canonicalsWithID(canonical.canonicalID)
    val rootBlobOpt: Option[(MetadataBlob, Option[RawMetadataBlob], Option[Person])] =
      gs.out(DescribedBy).headOption.flatMap { v: Vertex =>
        val raw = if(withRaw) {
          v.toPipeline.flatMap(_ >> findRawMetadataXor).toOption
        } else None
        val author = v.label match {
          case "ImageBlob" => v.toPipeline.toOption.flatMap(Traversals.getAuthor(_).out(DescribedBy).toCC[Person].headOption())
          case _ => None
        }
        v.label match {
          case "ImageBlob" => Some((v.toCC[ImageBlob], raw, author))
          case "Person" => Some((v.toCC[Person], raw, author))
          case _ => None
        }
      }

    rootBlobOpt
      .map { blobWithRelated =>
        val (blob: MetadataBlob, raw: Option[RawMetadataBlob], author: Option[Person]) = blobWithRelated
        val rpcBlob = metadataBlobToRPC(blob)

        CanonicalWithRootRevision(rawMetadata = raw.map(_.toRPC), author = author.map(_.toRPC))
          .withCanonicalID(canonical.canonicalID)
          .withRootRevision(rpcBlob)
      }
  }
  
  def canonicalToBlobObject(graph: Graph, canonical: Canonical, withRaw: Boolean = false)
  : Option[JObject] = {
    // FIXME: this is a pretty sad n+1 query
    val gs = graph.V ~> canonicalsWithID(canonical.canonicalID)
    val rootBlobOpt: Option[(String, MetadataBlob, Option[RawMetadataBlob], Option[Person])] =
      gs.out(DescribedBy).headOption.flatMap { v: Vertex =>
        val raw = if(withRaw) {
          v.toPipeline.flatMap(_ >> findRawMetadataXor).toOption
        } else None
        val author = v.label match {
          case "ImageBlob" => v.toPipeline.toOption.flatMap(Traversals.getAuthor(_).out(DescribedBy).toCC[Person].headOption())
          case _ => None
        }
        v.label match {
          case "ImageBlob" => Some((v.label, v.toCC[ImageBlob], raw, author))
          case "Person" => Some((v.label, v.toCC[Person], raw, author))
          case _ => None
        }
      }

    rootBlobOpt
      .map { labelWithBlob =>
        val (label: String, blob: MetadataBlob, raw: Option[RawMetadataBlob], author: Option[Person]) = labelWithBlob
        val blobObject = blobToJObject(blob)

        ("canonicalID" -> canonical.canonicalID) ~
          ("artefact" -> (("type" -> label) ~ blobObject ~ ("author" -> author.map(blobToJObject)))) ~
          ("raw" -> raw.map(blobToJObject))
      }
  }

  def listCanonicals(page: Int)(graph: Graph): List[JObject] = {
    val first = page * PAGE_SIZE
    val last = first + PAGE_SIZE

    val canonicals = graph.V.hasLabel[Canonical]
      .range(first, last).toCC[Canonical].toList

    canonicals.flatMap(canonicalToBlobObject(graph, _))
  }

  def canonicalWithID(canonicalID: UUID, withRaw: Boolean = false)(graph: Graph): Option[JObject] = {
    (graph.V ~> canonicalsWithUUID(canonicalID))
      .toCC[Canonical]
      .headOption
      .flatMap(canonicalToBlobObject(graph, _, withRaw))
  }

  def historyForCanonical(canonicalID: UUID)(graph: Graph): Option[JObject] = {
    val treeXor = graph.V ~> canonicalsWithUUID(canonicalID) >> findSubtreeXor

    val author = StepLabel[Vertex]("author")
    val raw = StepLabel[Vertex]("raw")
    val blob = StepLabel[Vertex]("blob")

    for {
      tree <- treeXor.toOption
      canonicalGS = tree.V ~> canonicalsWithUUID(canonicalID)
      canonical <- canonicalGS.toCC[Canonical].headOption

      revisions = (tree.V ~> describingOrModifyingBlobs(canonical)).toList
      revisionBlobs = revisions.flatMap(vertexToMetadataBlob)
    } yield {
      val revisionsJ = revisionBlobs.map(blobToJObject)
      ("canonicalID" -> canonical.canonicalID) ~
        ("revisions" -> revisionsJ)
    }
  }


  def worksForPersonWithCanonicalID(canonicalID: UUID)(graph: Graph)
  : Option[JObject] = {
    val responseXor = for {
      canonicalV <- graph.V ~> canonicalsWithUUID(canonicalID) >> headXor

      canonical = canonicalV.toCC[Canonical]

      canonicalJobject <- Xor.fromOption(
        canonicalToBlobObject(graph, canonical),
        CanonicalNotFound())

      canonicalGS <- canonicalV.toPipeline
      worksCanonicals <- canonicalGS >> findWorksXor
      worksJobjects = worksCanonicals.flatMap(canonicalToBlobObject(graph, _))
    } yield {
      canonicalJobject ~ ("works" -> worksJobjects)
    }

    responseXor.toOption
  }
}<commit_msg>return rpc types from remaining CanonicalQueries<commit_after>package io.mediachain.rpc.operations

import java.util.UUID

import cats.data.Xor
import gremlin.scala._
import io.mediachain.Types._
import io.mediachain.core.GraphError.CanonicalNotFound
import io.mediachain.rpc.TypeConversions._
import io.mediachain.rpc.Services._
import io.mediachain.Traversals, Traversals._, Traversals.Implicits._


object CanonicalQueries {

  val PAGE_SIZE = 20

  // TODO: Move this to io.mediachain.Types, use vals instead of string literals
  def vertexToMetadataBlob(vertex: Vertex): Option[MetadataBlob] =
    vertex.label match {
      case "ImageBlob" => Some(vertex.toCC[ImageBlob])
      case "Person" => Some(vertex.toCC[Person])
      case "RawMetadataBlob" => Some(vertex.toCC[RawMetadataBlob])
      case _ => None
    }

  def canonicalWithRootRevision(graph: Graph, canonical: Canonical, withRaw: Boolean = false)
  : Option[CanonicalWithRootRevision] = {
    // FIXME - use decent graph traversal
    val gs = graph.V ~> canonicalsWithID(canonical.canonicalID)
    val rootBlobOpt: Option[(MetadataBlob, Option[RawMetadataBlob], Option[Person])] =
      gs.out(DescribedBy).headOption.flatMap { v: Vertex =>
        val raw = if(withRaw) {
          v.toPipeline.flatMap(_ >> findRawMetadataXor).toOption
        } else None
        val author = v.label match {
          case "ImageBlob" => v.toPipeline.toOption.flatMap(Traversals.getAuthor(_).out(DescribedBy).toCC[Person].headOption())
          case _ => None
        }
        v.label match {
          case "ImageBlob" => Some((v.toCC[ImageBlob], raw, author))
          case "Person" => Some((v.toCC[Person], raw, author))
          case _ => None
        }
      }

    rootBlobOpt
      .map { blobWithRelated =>
        val (blob: MetadataBlob, raw: Option[RawMetadataBlob], author: Option[Person]) = blobWithRelated
        val rpcBlob = metadataBlobToRPC(blob)

        CanonicalWithRootRevision(rawMetadata = raw.map(_.toRPC), author = author.map(_.toRPC))
          .withCanonicalID(canonical.canonicalID)
          .withRootRevision(rpcBlob)
      }
  }


  def listCanonicals(page: Int)(graph: Graph)
  : CanonicalList = {
    val first = page * PAGE_SIZE
    val last = first + PAGE_SIZE

    val canonicals = graph.V.hasLabel[Canonical]
      .range(first, last).toCC[Canonical].toList

    CanonicalList(canonicals.flatMap(canonicalWithRootRevision(graph, _)))
  }

  def canonicalWithID(canonicalID: UUID, withRaw: Boolean = false)(graph: Graph)
  : Option[CanonicalWithRootRevision] = {
    (graph.V ~> canonicalsWithUUID(canonicalID))
      .toCC[Canonical]
      .headOption
      .flatMap(canonicalWithRootRevision(graph, _, withRaw))
  }

  def historyForCanonical(canonicalID: UUID)(graph: Graph)
  : Option[CanonicalWithHistory] = {
    val treeXor = graph.V ~> canonicalsWithUUID(canonicalID) >> findSubtreeXor

    for {
      tree <- treeXor.toOption
      canonicalGS = tree.V ~> canonicalsWithUUID(canonicalID)
      canonical <- canonicalGS.toCC[Canonical].headOption

      revisions = (tree.V ~> describingOrModifyingBlobs(canonical)).toList
      revisionBlobs = revisions.flatMap(vertexToMetadataBlob)
    } yield {
      val revisions = revisionBlobs.map(metadataBlobToRPC)
      CanonicalWithHistory()
        .withCanonicalID(canonical.canonicalID)
        .withRevisions(revisions)
    }
  }


  def worksForPersonWithCanonicalID(canonicalID: UUID)(graph: Graph)
  : Option[WorksForAuthor] = {
    val responseXor = for {
      canonicalV <- graph.V ~> canonicalsWithUUID(canonicalID) >> headXor

      canonical = canonicalV.toCC[Canonical]

      canonicalRPC <- Xor.fromOption(
        canonicalWithRootRevision(graph, canonical),
        CanonicalNotFound())

      canonicalGS <- canonicalV.toPipeline
      worksCanonicals <- canonicalGS >> findWorksXor
      worksWithRootRev = worksCanonicals.flatMap(canonicalWithRootRevision(graph, _))
    } yield {
      WorksForAuthor()
        .withAuthor(canonicalRPC)
        .withWorks(worksWithRootRev)
    }

    responseXor.toOption
  }
}
{'fraction_non_alphanumeric': 0.11421215242018537, 'fraction_numerical': 0.0009268795056642636, 'mean_word_length': 3.321762349799733, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 73, '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': '5452903', 'n_tokens_mistral': 3072, 'n_tokens_neox': 2955, 'n_words': 558}