<commit_before><?php
/**
 * Kanjis Peer.
 *
 * Methods:
 *  getKanjiByUCS()
 *  getKanjiByHeisig()
 *  getKanjiByCharacter()
 *  getKeyword()
 *  getDisplayKeyword()
 *  getFlashcardData()
 *  isHeisigIndexed()
 *
 * Helpers:
 *  joinLeft($select)       Adds LEFT JOIN USING(ucs_id) to the select.
 * 
 * 
 * @author  <NAME>
 */

class KanjisPeer extends coreDatabaseTable
{
  protected
    $tableName = 'kanjis',
    $columns   = [];  // timestamp columns must be declared for insert/update/replace

  /**
   * This function must be copied in each peer class.
   */
  public static function getInstance()
  {
    return coreDatabaseTable::_getInstance(__CLASS__);
  }

  /**
   * Get character information, given the UCS-2 code value.
   *
   * Datatype conversions:
   *    ucs_id     =>   (int)
   *
   * @param   int    $ucsId   UCS-2 code value.
   *
   * @return  object  Kanjis table row data (plus 'framenum'), or false
   */
  public static function getKanjiByUCS($ucsId)
  {
    // FIXME   remove the validation code (validate higher up!! + cohesion)
    assert(ctype_digit($ucsId) && rtkIndex::isExtendedIndex($ucsId));

    if (!BaseValidators::validateInteger($ucsId))
    {
      return false;
    }

    self::getInstance()->select()->where('ucs_id = ?', $ucsId)->query();

    if (false !== ($o = self::$db->fetchObject()))
    {
      // set "framenum" to the selected Heisig index, otherwise UCS
      $heisigNr = rtkIndex::getIndexForChar($o->kanji);
      $o->framenum = $heisigNr !== false ? $heisigNr : $ucsId;

      // normalize some values for cleaner code downhill
      $o->ucs_id = (int)$o->ucs_id;
      $o->strokecount = (int)$o->strokecount;
    }

    return $o;
  }

  /**
   * Return kanji data for given Heisig index number (aka "frame number").
   *
   * @param   string   $heisigNum   A Heisig index number (in the future, may be alphanumeric eg "123A")
   *
   * @return  object   Kanji rowdata as object, or FALSE
   */
  public static function getKanjiByHeisig($heisigNum)
  {
    assert((int)$heisigNum < 0x3000);

    self::getInstance()->select()->where('framenum = ?', $heisigNum)->query();

    return self::$db->fetchObject();
  }

  /**
   * Return kanji data for given kanji as utf8 character.
   * 
   * @param   String   $utf8    Kanji character in utf8
   * @return  Object   Kanji rowdata as object, or FALSE
   */
  public static function getKanjiByCharacter($utf8)
  {
    if (empty($utf8))
    {
      return false;
    }
    self::getInstance()->select()->where('kanji = ?', $utf8)->query();
    return self::$db->fetchObject();
  }

  /**
   * Returns flashcard data for the flashcard reviews (both SRS and non-SRS).
   *
   * This is a uiFlashcardReview callback, $ucsId must be sanitized!
   *
   * Options:
   * 
   *   yomi          (API ONLY) true to include example words with pronunciations
   *   api_mode      (API ONLY) true to return data according to API /review/fetch
   *   
   * @param  int     $ucsId     UCS-2 code value.
   * @param  object  $options   Options for the flashcard format (optional)
   * @return mixed   Object with flashcard data, or null
   */
  public static function getFlashcardData($ucsId, $options = null)
  {
    if (false === ($cardData = self::getKanjiByUCS($ucsId))) {
      return null;
    }

    $userId = sfContext::getInstance()->getUser()->getUserId();

    // make sure id is a Number in returned JSON
    $cardData->id = (int)$cardData->ucs_id;
    unset($cardData->ucs_id);

    sfProjectConfiguration::getActive()->loadHelpers(['Tag', 'Url', 'Links']);

    // not needed by client, reduce JSON response
    unset($cardData->onyomi);
    unset($cardData->lessonnum);
    unset($cardData->idx_olded);
    unset($cardData->idx_newed);

    // API ONLY (Kanji Ryokucha) : return On/Kun example words
    if (isset($options->yomi)) {
      // v_on, v_kun
      rtkLabs::getSampleWords($cardData->id, $cardData, isset($options->api_mode));
    }

    // retrieve user's vocab picks, plus highlighted readings
    if (!isset($options->api_mode)) {
      // VocabPickArray
      $cardData->vocab = rtkLabs::getFormattedVocabPicks($userId, $cardData->id);
    }

    // get custom keyword
    $custom_keyword = CustkeywordsPeer::getCustomKeyword($userId, $ucsId);
    $keyword = null !== $custom_keyword ? $custom_keyword : $cardData->keyword;
  
    if (!isset($options->api_mode)) {
      $cardData->keyword = link_to_keyword($keyword, $cardData->kanji, ['title' => 'Go to the Study page', 'target' => '_blank']);
    }
    else {
      $cardData->keyword = $keyword;
    }

    // API ONLY (apps) : api doesn't return the kanji as a character
    if (isset($option->api_mode)) { 
      unset($cardData->kanji);
    }

    // the data goes straight to the client via JSON
    return $cardData;
  }

  /**
   * Checks if a character has a known Heisig index (hardcoded sequence 0 and
   * sequence 1 = old/new or trad/simpl indexes).
   *
   * @return  bool<commit_msg>(KanjisPeer): fix typo, do not return `kanji` prop in card data for API<commit_after><?php
/**
 * Kanjis Peer.
 *
 * Methods:
 *  getKanjiByUCS()
 *  getKanjiByHeisig()
 *  getKanjiByCharacter()
 *  getKeyword()
 *  getDisplayKeyword()
 *  getFlashcardData()
 *  isHeisigIndexed()
 *
 * Helpers:
 *  joinLeft($select)       Adds LEFT JOIN USING(ucs_id) to the select.
 * 
 * 
 * @author  <NAME>
 */

class KanjisPeer extends coreDatabaseTable
{
  protected
    $tableName = 'kanjis',
    $columns   = [];  // timestamp columns must be declared for insert/update/replace

  /**
   * This function must be copied in each peer class.
   */
  public static function getInstance()
  {
    return coreDatabaseTable::_getInstance(__CLASS__);
  }

  /**
   * Get character information, given the UCS-2 code value.
   *
   * Datatype conversions:
   *    ucs_id     =>   (int)
   *
   * @param   int    $ucsId   UCS-2 code value.
   *
   * @return  object  Kanjis table row data (plus 'framenum'), or false
   */
  public static function getKanjiByUCS($ucsId)
  {
    // FIXME   remove the validation code (validate higher up!! + cohesion)
    assert(ctype_digit($ucsId) && rtkIndex::isExtendedIndex($ucsId));

    if (!BaseValidators::validateInteger($ucsId))
    {
      return false;
    }

    self::getInstance()->select()->where('ucs_id = ?', $ucsId)->query();

    if (false !== ($o = self::$db->fetchObject()))
    {
      // set "framenum" to the selected Heisig index, otherwise UCS
      $heisigNr = rtkIndex::getIndexForChar($o->kanji);
      $o->framenum = $heisigNr !== false ? $heisigNr : $ucsId;

      // normalize some values for cleaner code downhill
      $o->ucs_id = (int)$o->ucs_id;
      $o->strokecount = (int)$o->strokecount;
    }

    return $o;
  }

  /**
   * Return kanji data for given Heisig index number (aka "frame number").
   *
   * @param   string   $heisigNum   A Heisig index number (in the future, may be alphanumeric eg "123A")
   *
   * @return  object   Kanji rowdata as object, or FALSE
   */
  public static function getKanjiByHeisig($heisigNum)
  {
    assert((int)$heisigNum < 0x3000);

    self::getInstance()->select()->where('framenum = ?', $heisigNum)->query();

    return self::$db->fetchObject();
  }

  /**
   * Return kanji data for given kanji as utf8 character.
   * 
   * @param   String   $utf8    Kanji character in utf8
   * @return  Object   Kanji rowdata as object, or FALSE
   */
  public static function getKanjiByCharacter($utf8)
  {
    if (empty($utf8))
    {
      return false;
    }
    self::getInstance()->select()->where('kanji = ?', $utf8)->query();
    return self::$db->fetchObject();
  }

  /**
   * Returns flashcard data for the flashcard reviews (both SRS and non-SRS).
   *
   * This is a uiFlashcardReview callback, $ucsId must be sanitized!
   *
   * Options:
   * 
   *   yomi          (API ONLY) true to include example words with pronunciations
   *   api_mode      (API ONLY) true to return data according to API /review/fetch
   *   
   * @param  int     $ucsId     UCS-2 code value.
   * @param  object  $options   Options for the flashcard format (optional)
   * @return mixed   Object with flashcard data, or null
   */
  public static function getFlashcardData($ucsId, $options = null)
  {
    if (false === ($cardData = self::getKanjiByUCS($ucsId))) {
      return null;
    }

    $userId = sfContext::getInstance()->getUser()->getUserId();

    // make sure id is a Number in returned JSON
    $cardData->id = (int)$cardData->ucs_id;
    unset($cardData->ucs_id);

    sfProjectConfiguration::getActive()->loadHelpers(['Tag', 'Url', 'Links']);

    // not needed by client, reduce JSON response
    unset($cardData->onyomi);
    unset($cardData->lessonnum);
    unset($cardData->idx_olded);
    unset($cardData->idx_newed);

    // API ONLY (Kanji Ryokucha) : return On/Kun example words
    if (isset($options->yomi)) {
      // v_on, v_kun
      rtkLabs::getSampleWords($cardData->id, $cardData, isset($options->api_mode));
    }

    // retrieve user's vocab picks, plus highlighted readings
    if (!isset($options->api_mode)) {
      // VocabPickArray
      $cardData->vocab = rtkLabs::getFormattedVocabPicks($userId, $cardData->id);
    }

    // get custom keyword
    $custom_keyword = CustkeywordsPeer::getCustomKeyword($userId, $ucsId);
    $keyword = null !== $custom_keyword ? $custom_keyword : $cardData->keyword;
  
    if (!isset($options->api_mode)) {
      $cardData->keyword = link_to_keyword($keyword, $cardData->kanji, ['title' => 'Go to the Study page', 'target' => '_blank']);
    }
    else {
      $cardData->keyword = $keyword;
    }

    // API ONLY (apps) : api doesn't return the kanji as a character
    if (isset($options->api_mode)) { 
      unset($cardData->kanji);
    }

    // the data goes straight to the client via JSON
    return $cardData;
  }

  /**
   * Checks if a character has a known Heisig index (hardcoded sequence 0 and
   * sequence 1 = old/new or trad/simpl indexes).
   *
   * @return  bool
{'fraction_non_alphanumeric': 0.1379586124162751, 'fraction_numerical': 0.0037988603418974308, 'mean_word_length': 2.924676343664182, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 93, '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': '4597378', 'n_tokens_mistral': 3386, 'n_tokens_neox': 3285, 'n_words': 851}