<commit_before>package edu.gemini.catalog

import javax.swing.table.AbstractTableModel

import edu.gemini.ags.api.AgsMagnitude.MagnitudeTable
import edu.gemini.ags.api.{AgsRegistrar, AgsStrategy}
import edu.gemini.catalog.api.{UCAC4, CatalogName, CatalogQuery}
import edu.gemini.spModel.core.Target.SiderealTarget
import edu.gemini.spModel.core._
import edu.gemini.spModel.gemini.obscomp.SPSiteQuality.Conditions
import edu.gemini.spModel.obs.context.ObsContext
import jsky.app.ot.gemini.editor.targetComponent.GuidingFeedback.ProbeLimits

import scalaz._
import Scalaz._

/**
 * contains the Catalog Navigator UI model classes
 */
package object ui {

  /**
   * Locally describe an ags strategy including its limits and the query that would trigger
   */
  case class SupportedStrategy(strategy: AgsStrategy, limits: Option[ProbeLimits], query: List[CatalogQuery])

  /**
   * Describes the observation used to do a Guide Star Search
   */
  case class ObservationInfo(objectName: Option[String], instrumentName: Option[String], strategy: Option[AgsStrategy], validStrategies: List[SupportedStrategy], conditions: Option[Conditions], catalog: CatalogName) {
    def catalogQuery:List[CatalogQuery] = validStrategies.collect {
        case SupportedStrategy(s, _, query) if s == strategy => query
      }.flatten
  }

  object ObservationInfo {

    /**
     * Converts an AgsStrategy to a simpler description to be stored in the UI model
     */
    private def toSupportedStrategy(obsCtx: ObsContext, strategy: AgsStrategy, mt: MagnitudeTable):SupportedStrategy = {
      val pb = strategy.magnitudes(obsCtx, mt).map(k => ProbeLimits(strategy.probeBands, obsCtx, k._2)).headOption
      val queries = strategy.catalogQueries(obsCtx, mt)
      SupportedStrategy(strategy, pb.flatten, queries)
    }

    def apply(ctx: ObsContext, mt: MagnitudeTable):ObservationInfo = ObservationInfo(
      Option(ctx.getTargets.getBase).map(_.getTarget.getName),
      Option(ctx.getInstrument).map(_.getTitle),
      AgsRegistrar.currentStrategy(ctx),
      AgsRegistrar.validStrategies(ctx).map(toSupportedStrategy(ctx, _, mt)),
      ctx.getConditions.some,
      UCAC4)

  }

  /**
   * Represents a table column description
   * @tparam T The type of the column
   */
  sealed trait CatalogNavigatorColumn[T] {
    val title: String

    def lens: PLens[Target, T]

    def displayValue(t: T): String = t.toString

    def render(target: Target): String = ~lens.get(target).map(displayValue)
  }

  case class IdColumn(title: String) extends CatalogNavigatorColumn[String] {
    override val lens = Target.name
  }

  case class RAColumn(title: String) extends CatalogNavigatorColumn[RightAscension] {
    override val lens = Target.coords >=> Coordinates.ra.partial

    override def displayValue(t: RightAscension) = t.toAngle.formatHMS
  }

  case class DECColumn(title: String) extends CatalogNavigatorColumn[Declination] {
    override val lens = Target.coords >=> Coordinates.dec.partial

    override def displayValue(t: Declination) = t.formatDMS
  }

  case class DistanceColumn(base: Coordinates, title: String) extends CatalogNavigatorColumn[Angle] {
    val distance: Coordinates @> Angle = Lens(c => Store(r => c, Coordinates.difference(base, c).distance))

    override val lens = Target.coords >=> distance.partial

    override def displayValue(t: Angle) = f"${t.toArcmins}%.2f"
  }

  case class PMRAColumn(title: String) extends CatalogNavigatorColumn[RightAscensionAngularVelocity] {
    override val lens = Target.pm >=> ProperMotion.deltaRA.partial

    override def displayValue(t: RightAscensionAngularVelocity): String = f"${t.velocity.masPerYear}%.2f"
  }

  case class PMDecColumn(title: String) extends CatalogNavigatorColumn[DeclinationAngularVelocity] {
    override val lens = Target.pm >=> ProperMotion.deltaDec.partial

    override def displayValue(t: DeclinationAngularVelocity): String = f"${t.velocity.masPerYear}%.2f"
  }

  case class MagnitudeColumn(band: MagnitudeBand) extends CatalogNavigatorColumn[Magnitude] {
    override val title = band.name
    // Lens from list of magnitudes to the band's magnitude if present
    val bLens: List[Magnitude] @?> Magnitude = PLens(ml => ml.find(_.band === band).map(m => Store(b => sys.error("read-only lens"), m)))
    override val lens = Target.magnitudes >=> bLens

    override def displayValue(t: Magnitude): String = f"${t.value}%.2f"
  }

  def baseColumnNames(base: Coordinates): List[CatalogNavigatorColumn[_]] = List(IdColumn("Id"), RAColumn("RA"), DECColumn("Dec"), DistanceColumn(base, "Dist. [arcmin]"))
  val pmColumns: List[CatalogNavigatorColumn[_]] = List(PMRAColumn("µ RA"), PMDecColumn("µ Dec"))
  val magColumns = MagnitudeBand.all.map(MagnitudeColumn)

  /**
   * Data model for the main table of the catalog navigator
   */
  case class TargetsModel(base: Coordinates, targets: List[SiderealTarget]) extends AbstractTableModel {

    // Available columns from the list of targets
    val columns = {
      val bandsInTargets = targets.flatMap(_.magnitudes).map(_.band).distinct
      val hasPM = targets.exists(_.properMotion.isDefined)
      val pmCols = if (hasPM) pmColumns else Nil
      val magCols = magColumns.filter(m => bandsInTargets.contains(m.band))

      baseColumnNames(base) ::: pmCols ::: magCols
    }

    override def getRowCount = targets.length

    override def getColumnCount = columns.size

    override def getColumnName(column: Int): String =
      columns(column).title

    override def getValueAt(rowIndex: Int, columnIndex: Int) = targets.lift(rowIndex).flatMap { t =>
      columns.zipWithIndex.find(_._2 == columnIndex).map(_._1.render(t))
    }.orNull

  }
}<commit_msg>OCSADV-417 Preserve the type of each column's element #time 2h<commit_after>package edu.gemini.catalog

import javax.swing.table.AbstractTableModel

import edu.gemini.ags.api.AgsMagnitude.MagnitudeTable
import edu.gemini.ags.api.{AgsRegistrar, AgsStrategy}
import edu.gemini.catalog.api.{UCAC4, CatalogName, CatalogQuery}
import edu.gemini.spModel.core.Target.SiderealTarget
import edu.gemini.spModel.core._
import edu.gemini.spModel.gemini.obscomp.SPSiteQuality.Conditions
import edu.gemini.spModel.obs.context.ObsContext
import jsky.app.ot.gemini.editor.targetComponent.GuidingFeedback.ProbeLimits

import scalaz._
import Scalaz._

/**
 * contains the Catalog Navigator UI model classes
 */
package object ui {

  /**
   * Locally describe an ags strategy including its limits and the query that would trigger
   */
  case class SupportedStrategy(strategy: AgsStrategy, limits: Option[ProbeLimits], query: List[CatalogQuery])

  /**
   * Describes the observation used to do a Guide Star Search
   */
  case class ObservationInfo(objectName: Option[String], instrumentName: Option[String], strategy: Option[AgsStrategy], validStrategies: List[SupportedStrategy], conditions: Option[Conditions], catalog: CatalogName) {
    def catalogQuery:List[CatalogQuery] = validStrategies.collect {
        case SupportedStrategy(s, _, query) if s == strategy => query
      }.flatten
  }

  object ObservationInfo {

    /**
     * Converts an AgsStrategy to a simpler description to be stored in the UI model
     */
    private def toSupportedStrategy(obsCtx: ObsContext, strategy: AgsStrategy, mt: MagnitudeTable):SupportedStrategy = {
      val pb = strategy.magnitudes(obsCtx, mt).map(k => ProbeLimits(strategy.probeBands, obsCtx, k._2)).headOption
      val queries = strategy.catalogQueries(obsCtx, mt)
      SupportedStrategy(strategy, pb.flatten, queries)
    }

    def apply(ctx: ObsContext, mt: MagnitudeTable):ObservationInfo = ObservationInfo(
      Option(ctx.getTargets.getBase).map(_.getTarget.getName),
      Option(ctx.getInstrument).map(_.getTitle),
      AgsRegistrar.currentStrategy(ctx),
      AgsRegistrar.validStrategies(ctx).map(toSupportedStrategy(ctx, _, mt)),
      ctx.getConditions.some,
      UCAC4)

  }

  /**
   * Represents a table column description
   * @tparam T The type of the column
   */
  abstract class CatalogNavigatorColumn[T >: Null] {
    def title: String

    def lens: PLens[Target, T]

    def displayValue(t: T): String = t.toString

    // Extract the data from the target via the lens
    def render(target: Target): Option[T] = lens.get(target)

    // Indicates the class of the column
    def clazz:Class[_] = manifest.runtimeClass
  }

  case class IdColumn(title: String) extends CatalogNavigatorColumn[String] {
    override val lens = Target.name
  }

  case class RAColumn(title: String) extends CatalogNavigatorColumn[RightAscension] {
    override val lens = Target.coords >=> Coordinates.ra.partial

    override def displayValue(t: RightAscension) = t.toAngle.formatHMS
  }

  case class DECColumn(title: String) extends CatalogNavigatorColumn[Declination] {
    override val lens = Target.coords >=> Coordinates.dec.partial

    override def displayValue(t: Declination) = t.formatDMS
  }

  case class DistanceColumn(base: Coordinates, title: String) extends CatalogNavigatorColumn[Angle] {
    val distance: Coordinates @> Angle = Lens(c => Store(r => c, Coordinates.difference(base, c).distance))

    override val lens = Target.coords >=> distance.partial

    override def displayValue(t: Angle) = f"${t.toArcmins}%.2f"
  }

  case class PMRAColumn(title: String) extends CatalogNavigatorColumn[RightAscensionAngularVelocity] {
    override val lens = Target.pm >=> ProperMotion.deltaRA.partial

    override def displayValue(t: RightAscensionAngularVelocity): String = f"${t.velocity.masPerYear}%.2f"
  }

  case class PMDecColumn(title: String) extends CatalogNavigatorColumn[DeclinationAngularVelocity] {
    override val lens = Target.pm >=> ProperMotion.deltaDec.partial

    override def displayValue(t: DeclinationAngularVelocity): String = f"${t.velocity.masPerYear}%.2f"
  }

  case class MagnitudeColumn(band: MagnitudeBand) extends CatalogNavigatorColumn[Magnitude] {
    override val title = band.name
    // Lens from list of magnitudes to the band's magnitude if present
    val bLens: List[Magnitude] @?> Magnitude = PLens(ml => ml.find(_.band === band).map(m => Store(b => sys.error("read-only lens"), m)))
    override val lens = Target.magnitudes >=> bLens

    override def displayValue(t: Magnitude): String = f"${t.value}%.2f"
  }

  // Required to give limits to the existential type list
  type ColumnsList = List[CatalogNavigatorColumn[A] forSome { type A >: Null <: AnyRef}]

  def baseColumnNames(base: Coordinates): ColumnsList = List(IdColumn("Id"), RAColumn("RA"), DECColumn("Dec"), DistanceColumn(base, "Dist. [arcmin]"))
  val pmColumns: ColumnsList  = List(PMRAColumn("µ RA"), PMDecColumn("µ Dec"))
  val magColumns = MagnitudeBand.all.map(MagnitudeColumn)

  /**
   * Data model for the main table of the catalog navigator
   */
  case class TargetsModel(base: Coordinates, targets: List[SiderealTarget]) extends AbstractTableModel {
    // Available columns from the list of targets
    val columns:ColumnsList = {
      val bandsInTargets = targets.flatMap(_.magnitudes).map(_.band).distinct
      val hasPM = targets.exists(_.properMotion.isDefined)
      val pmCols = if (hasPM) pmColumns else Nil
      val magCols = magColumns.filter(m => bandsInTargets.contains(m.band))

      baseColumnNames(base) ::: pmCols ::: magCols
    }

    override def getRowCount = targets.length

    override def getColumnCount = columns.size

    override def getColumnName(column: Int): String =
      columns(column).title

    override def getValueAt(rowIndex: Int, columnIndex: Int):AnyRef =
      targets.lift(rowIndex).flatMap { t =>
        columns.lift(columnIndex) >>= {_.render(t)}
      }.orNull

    override def getColumnClass(columnIndex: Int): Class[_] = columns(columnIndex).clazz
  }
}
{'fraction_non_alphanumeric': 0.1077184054283291, 'fraction_numerical': 0.0016963528413910093, 'mean_word_length': 4.330470162748644, 'pattern_counts': {'":': 0, '<': 4, '<?xml version=': 0, '>': 53, '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': '17164', 'n_tokens_mistral': 3685, 'n_tokens_neox': 3542, 'n_words': 881}