<commit_before><?php
/*
  be.chiro.civi.kavo - support for the KAVO-API.
  Copyright (C) 2017  <NAME>

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as
  published by the Free Software Foundation, either version 3 of the
  License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Data access to CiviCRM participants, validation and mapping to KAVO participants.
 */
class CRM_Kavo_Worker_ParticipantWorker extends CRM_Kavo_Worker {

  /**
   * CRM_Kavo_Worker_ParticipantWorker constructor.
   */
  public function __construct() {
    parent::__construct('Participant');
  }

  /**
   * Returns the required fields for a new entity in the kavo tool.
   *
   * @return array
   */
  public function getKavoRequiredKeys() {
    return [
      'kavo_id',
      'course_id',
    ];
  }

  /**
   * Returns the relevant fields for CiviCRM to return.
   *
   * @return array
   */
  protected function getCiviRelevantKeys() {
    return [
      'contact_id',
      'event_id',
    ];
  }

  /**
   * Returns the required chained calls to retrieve the entity from CiviCRM.
   *
   * @return array
   */
  protected function getCiviChainedRequests() {
    return [
      'api.Contact.getsingle' => [
        'id' => '$value.contact_id',
        'return' => [CRM_Kavo_Field::KAVO_ID()],
      ],
      'api.Event.getsingle' => [
        'id' => '$value.event_id',
        'return' => [
          CRM_Kavo_Field::COURSE_ID(),
          CRM_Kavo_Field::ACKNOWLEDGEMENT_TYPE(),
          'start_date',
        ],
      ],
    ];
  }

  /**
   * Map the CiviCRM entity to the corresponding KAVO-entity.
   *
   * @param array $civiEntity
   * @return array
   */
  public function mapToKavo(array $civiEntity) {
    return [
      'kavo_id' => $civiEntity['api.Contact.getsingle'][CRM_Kavo_Field::KAVO_ID()],
      'course_id' => $civiEntity['api.Event.getsingle'][CRM_Kavo_Field::COURSE_ID()],
    ];
  }

  /**
   * Checks whether the given CiviCRM-participant can be used to create a KAVO
   * participant.
   *
   * @param array $civiEntity a CiviCRM participant.
   * @return \CRM_Kavo_ValidationResult
   * @throws \Exception
   */
  public function validateKavo(array $civiEntity) {
    // TODO: split function
    $result = parent::validateKavo($civiEntity);
    $contact = $civiEntity['api.Contact.getsingle'];
    $event = $civiEntity['api.Event.getsingle'];

    if ($civiEntity['role_id'] != CRM_Kavo_Role::ATTENDEE()) {
      // We only care about attendees atm.
      return $result;
    }

    if (empty($event[CRM_Kavo_Field::ACKNOWLEDGEMENT_TYPE()])) {
      // We don't care if there is no acknowledgement type.
      return $result;
    }

    if (empty($contact[CRM_Kavo_Field::KAVO_ID()])) {
      $result->addStatus(CRM_Kavo_Error::REQUIRED_FIELDS_MISSING);
      $result->addMessage('You need a KAVO-ID to subscribe to this course.');
      $result->extra['missing'][] = 'kavo_id';
    }

    // Age check.

    // We need to verify the age that the participant will have in the calendar
    // year of the course, see the specs, #10.
    $ageRestriction = [
      'animator' => 16,
      'hoofdanimator' => 18,
      'instructeur' => 19,
    ];
    $yearOfBirth = $this->extractYear($contact['birth_date']);
    $yearOfEvent = $this->extractYear($event['start_date']);
    if (empty($yearOfBirth)) {
      $result->addStatus(CRM_Kavo_Error::REQUIRED_FIELDS_MISSING);
      $result->addMessage('Birth date is required to subscribe.');
      $result->extra['missing'][] = 'birth_date';
    }
    if (empty($yearOfEvent)) {
      $result->addStatus(CRM_Kavo_Error::REQUIRED_FIELDS_MISSING);
      $result->addMessage('Event date missing.');
      $result->extra['missing'][] = 'start_date';
    }

    if (!empty($yearOfBirth) && !empty($yearOfEvent)) {
      $age = $yearOfEvent - $yearOfBirth;
      $minAge = $ageRestriction[$event[CRM_Kavo_Field::ACKNOWLEDGEMENT_TYPE()]];
      if ($age < $minAge) {
        $result->addStatus(CRM_Kavo_Error::PARTICIPANT_TOO_YOUNG);
        $result->addMessage("Participant too young; born after " . ($yearOfEvent - $minAge) . ".\n");
        $result->extra['ageLimit'] = $minAge;
      }
    }

    $result->extra['missing'] = array_unique($result->extra['missing']);

    if (!empty($result->status)) {
      // If we have errors here, we return. Because the checks below can
      // fail if e.g. the contact doesn't have a KAVO-id.
      return $result;
    }

    // For the moment we use the same criteria for hoofdanimator and
    // instructeur, which might not be 100% correct. But it is unclear
    // for everybody anyway ;-)
    switch ($event[CRM_Kavo_Field::ACKNOWLEDGEMENT_TYPE()]) {
      case "hoofdanimator":
      case "instructeur":
        // if you are a certified animator, everything's fine.
        if (!$this->isCertified($civiEntity, 'animator')) {
          // Otherwise we assume by default that you have the required
          // experience.
          $experience = 1;
          // But you can override this by implementing hook_kavo_experience.
          CRM_Utils_Hook::singleton()->invoke(3, $experience,
            $contact['id'], DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $event['start_date']),
            CRM_Utils_Hook::$_nullObject, CRM_Utils_Hook::$_nullObject,
            CRM_Utils_Hook::$_nullObject, 'kavo_experience');
          if (!$experience) {
            $result->addStatus(CRM_Kavo_Error::PARTICIPANT_LACKS_EXPERIENCE);
            $result->addMessage("A year of experience as leader is required.\n");
          }
        }
        break;
    }<commit_msg>Closes #25 - Don't add 'missing' if nothing is missing.<commit_after><?php
/*
  be.chiro.civi.kavo - support for the KAVO-API.
  Copyright (C) 2017  Chirojeugd-Vlaanderen vzw

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU Affero General Public License as
  published by the Free Software Foundation, either version 3 of the
  License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU Affero General Public License for more details.

  You should have received a copy of the GNU Affero General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * Data access to CiviCRM participants, validation and mapping to KAVO participants.
 */
class CRM_Kavo_Worker_ParticipantWorker extends CRM_Kavo_Worker {

  /**
   * CRM_Kavo_Worker_ParticipantWorker constructor.
   */
  public function __construct() {
    parent::__construct('Participant');
  }

  /**
   * Returns the required fields for a new entity in the kavo tool.
   *
   * @return array
   */
  public function getKavoRequiredKeys() {
    return [
      'kavo_id',
      'course_id',
    ];
  }

  /**
   * Returns the relevant fields for CiviCRM to return.
   *
   * @return array
   */
  protected function getCiviRelevantKeys() {
    return [
      'contact_id',
      'event_id',
    ];
  }

  /**
   * Returns the required chained calls to retrieve the entity from CiviCRM.
   *
   * @return array
   */
  protected function getCiviChainedRequests() {
    return [
      'api.Contact.getsingle' => [
        'id' => '$value.contact_id',
        'return' => [CRM_Kavo_Field::KAVO_ID()],
      ],
      'api.Event.getsingle' => [
        'id' => '$value.event_id',
        'return' => [
          CRM_Kavo_Field::COURSE_ID(),
          CRM_Kavo_Field::ACKNOWLEDGEMENT_TYPE(),
          'start_date',
        ],
      ],
    ];
  }

  /**
   * Map the CiviCRM entity to the corresponding KAVO-entity.
   *
   * @param array $civiEntity
   * @return array
   */
  public function mapToKavo(array $civiEntity) {
    return [
      'kavo_id' => $civiEntity['api.Contact.getsingle'][CRM_Kavo_Field::KAVO_ID()],
      'course_id' => $civiEntity['api.Event.getsingle'][CRM_Kavo_Field::COURSE_ID()],
    ];
  }

  /**
   * Checks whether the given CiviCRM-participant can be used to create a KAVO
   * participant.
   *
   * @param array $civiEntity a CiviCRM participant.
   * @return \CRM_Kavo_ValidationResult
   * @throws \Exception
   */
  public function validateKavo(array $civiEntity) {
    // TODO: split function
    $result = parent::validateKavo($civiEntity);
    $contact = $civiEntity['api.Contact.getsingle'];
    $event = $civiEntity['api.Event.getsingle'];

    if ($civiEntity['role_id'] != CRM_Kavo_Role::ATTENDEE()) {
      // We only care about attendees atm.
      return $result;
    }

    if (empty($event[CRM_Kavo_Field::ACKNOWLEDGEMENT_TYPE()])) {
      // We don't care if there is no acknowledgement type.
      return $result;
    }

    if (empty($contact[CRM_Kavo_Field::KAVO_ID()])) {
      $result->addStatus(CRM_Kavo_Error::REQUIRED_FIELDS_MISSING);
      $result->addMessage('You need a KAVO-ID to subscribe to this course.');
      $result->extra['missing'][] = 'kavo_id';
    }

    // Age check.

    // We need to verify the age that the participant will have in the calendar
    // year of the course, see the specs, #10.
    $ageRestriction = [
      'animator' => 16,
      'hoofdanimator' => 18,
      'instructeur' => 19,
    ];
    $yearOfBirth = $this->extractYear($contact['birth_date']);
    $yearOfEvent = $this->extractYear($event['start_date']);
    if (empty($yearOfBirth)) {
      $result->addStatus(CRM_Kavo_Error::REQUIRED_FIELDS_MISSING);
      $result->addMessage('Birth date is required to subscribe.');
      $result->extra['missing'][] = 'birth_date';
    }
    if (empty($yearOfEvent)) {
      $result->addStatus(CRM_Kavo_Error::REQUIRED_FIELDS_MISSING);
      $result->addMessage('Event date missing.');
      $result->extra['missing'][] = 'start_date';
    }

    if (!empty($yearOfBirth) && !empty($yearOfEvent)) {
      $age = $yearOfEvent - $yearOfBirth;
      $minAge = $ageRestriction[$event[CRM_Kavo_Field::ACKNOWLEDGEMENT_TYPE()]];
      if ($age < $minAge) {
        $result->addStatus(CRM_Kavo_Error::PARTICIPANT_TOO_YOUNG);
        $result->addMessage("Participant too young; born after " . ($yearOfEvent - $minAge) . ".\n");
        $result->extra['ageLimit'] = $minAge;
      }
    }

    if (!empty($result->extra['missing'])) {
      $result->extra['missing'] = array_unique($result->extra['missing']);
    }

    if (!empty($result->status)) {
      // If we have errors here, we return. Because the checks below can
      // fail if e.g. the contact doesn't have a KAVO-id.
      return $result;
    }

    // For the moment we use the same criteria for hoofdanimator and
    // instructeur, which might not be 100% correct. But it is unclear
    // for everybody anyway ;-)
    switch ($event[CRM_Kavo_Field::ACKNOWLEDGEMENT_TYPE()]) {
      case "hoofdanimator":
      case "instructeur":
        // if you are a certified animator, everything's fine.
        if (!$this->isCertified($civiEntity, 'animator')) {
          // Otherwise we assume by default that you have the required
          // experience.
          $experience = 1;
          // But you can override this by implementing hook_kavo_experience.
          CRM_Utils_Hook::singleton()->invoke(3, $experience,
            $contact['id'], DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $event['start_date']),
            CRM_Utils_Hook::$_nullObject, CRM_Utils_Hook::$_nullObject,
            CRM_Utils_Hook::$_nullObject, 'kavo_experience');
          if (!$experience) {
            $result->addStatus(CRM_Kavo_Error::PARTICIPANT_LACKS_EXPERIENCE);
            $result->addMessage("A year of experience as leader is required.\n");
          }
        }
        break;
    }
{'fraction_non_alphanumeric': 0.12888707037643207, 'fraction_numerical': 0.003109656301145663, 'mean_word_length': 2.920757138273981, 'pattern_counts': {'":': 4, '<': 10, '<?xml version=': 0, '>': 71, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '4875535', 'n_tokens_mistral': 3992, 'n_tokens_neox': 3864, 'n_words': 975}