Cloud-CV/EvalAI

View on GitHub
frontend_v2/src/app/components/challenge/challengesettings/challengesettings.component.ts

Summary

Maintainability
F
3 wks
Test Coverage
import { Component, OnInit, OnDestroy, QueryList, ViewChildren } from '@angular/core';
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { MatChipInputEvent } from '@angular/material/chips';
import { Router } from '@angular/router';
import { NGXLogger } from 'ngx-logger';
import * as moment from 'moment';

import { ChallengeService } from '../../../services/challenge.service';
import { EndpointsService } from '../../../services/endpoints.service';
import { ApiService } from '../../../services/api.service';
import { GlobalService } from '../../../services/global.service';

import { SelectphaseComponent } from '../../utility/selectphase/selectphase.component';

@Component({
  selector: 'app-challengesettings',
  templateUrl: './challengesettings.component.html',
  styleUrls: ['./challengesettings.component.scss'],
})
export class ChallengesettingsComponent implements OnInit, OnDestroy {
  /**
   * Phase select card components
   */
  @ViewChildren('phaseselect')
  components: QueryList<SelectphaseComponent>;

  /**
   * Challenge object
   */
  challenge: any;

  /**
   * Leaderboard object
   */
  leaderboard: any;

  /**
   * Id of the currently selected leaderboard
   */
   selectedLeaderboardId: any = null;

  /**
   * Is challenge host
   */
  isChallengeHost = false;

  /**
   * To call the API inside modal for editing the challenge details
   */
  apiCall: any;

  /**
   * Challenge phase list
   */
  phases = [];

  /**
   * Phase split list
   */
  phaseSplits = [];

  /**
   * Challenge phases filtered
   */
  filteredPhases = [];

  /**
   * Phase splits filtered
   */
  filteredPhaseSplits = [];

  /**
   * Currently selected phase
   */
  selectedPhase: any = null;

  /**
   * Phase selection type (radio button or select box)
   */
  phaseSelectionType = 'selectBox';

  /**
   * Select box list type
   */
  phaseSelectionListType = 'phase';

  /**
   * If the submission is public
   */
  isSubmissionPublic = false;

  /**
   * If the phase is public
   */
  isPhasePublic = false;

  /**
   * If leaderboard is public
   */
  isLeaderboardPublic = false;

  /**
   * Currently selected phase split
   */
  selectedPhaseSplit: any = null;

  /**
   * Phase selection type (radio button or select box)
   */
  phaseLeaderboardSelectionType = 'selectBox';

  /**
   * Select box list type
   */
  phaseLeaderboardSelectionListType = 'settingsPhaseSplit';

  /**
   * If leaderboard of phase split is public
   * 1 -> private
   * 3 -> public
   */
  isPhaseSplitLeaderboardPublic = 1;

  /**
   * Leaderboard precision value
   */
  leaderboardPrecisionValue = 2;

  /**
   * Set leaderboard precision value
   */
  setLeaderboardPrecisionValue = '1.2-2';

  /**
   * If leaderboard precision value is equal to 0
   */
  minusDisabled = false;

  /**
   * If leaderboard precision value is equal to 20
   */
  plusDisabled = false;

  /**
   * store worker logs
   */
  workerLogs = [];

  /**
   * An interval for fetching the leaderboard data in every 5 seconds
   */
  pollingInterval: any;

  /**
   * Participants banned emails ids
   */
  bannedEmailIds: string[];

  /**
   * Former participants banned emails ids
   */
  formerBannedEmailIds: string[];

  /**
   * Email validation for the banned email ids
   */
  isValidationError = false;

  /**
   * Email error message
   */
  message: string;

  /**
   * phase visibility state and it's icon
   */
  phaseVisibility = {
    state: 'Private',
    icon: 'fa fa- text-darken-1',
  };

  /**
   * submission visibility state and it's icon
   */
  submissionVisibility = {
    state: 'Private',
    icon: 'fa fa-toggle-off grey-text text-darken-1',
  };

  /**
   * phase visibility state and it's icon
   */
  leaderboardVisibility = {
    state: 'Private',
    icon: 'fa fa-toggle-off grey-text text-darken-1',
  };

  /**
   * publish challenge state and it's icon
   */
  publishChallenge = {
    state: 'Not Published',
    icon: 'fa fa-eye-slash red-text',
  };

  /**
   * Separator key codes
   */
  readonly separatorKeysCodes: number[] = [ENTER, COMMA];

  /**
   * Banned email ids chips property
   */
  visible = true;
  selectable = true;
  removable = true;
  addOnBlur = true;

  /**
   * Input to edit the banned participants emails
   */
  isBannedEmailInputVisible: boolean;

  constructor(
    private challengeService: ChallengeService,
    private globalService: GlobalService,
    private apiService: ApiService,
    private endpointsService: EndpointsService,
    private router: Router,
    private logger: NGXLogger
  ) {}

  ngOnInit() {
    this.challengeService.currentChallenge.subscribe((challenge) => {
      this.challenge = challenge;
    });

    this.challengeService.isChallengeHost.subscribe((status) => {
      this.isChallengeHost = status;
    });

    this.challengeService.currentChallengePublishState.subscribe((publishChallenge) => {
      this.publishChallenge.state = publishChallenge.state;
      this.publishChallenge.icon = publishChallenge.icon;
    });

    if (!this.challenge['remote_evaluation']) {
      this.fetchWorkerLogs();
      this.startLoadingLogs();
    }

    this.challengeService.currentPhases.subscribe((phases) => {
      this.phases = phases;
      for (let i = 0; i < this.phases.length; i++) {
        if (this.phases[i].is_public === false) {
          this.phases[i].showPrivate = true;
        } else {
          this.phases[i].showPrivate = false;
        }
      }
      this.filteredPhases = this.phases;
    });

    this.challengeService.currentPhaseSplit.subscribe((phaseSplits) => {
      this.phaseSplits = phaseSplits;
      for (let i = 0; i < this.phaseSplits.length; i++) {
        if (this.phaseSplits[i].visibility !== 3) {
          this.phaseSplits[i].showPrivate = true;
        } else {
          this.phaseSplits[i].showPrivate = false;
        }
      }
      this.filteredPhaseSplits = this.phaseSplits;
    });
  }

  // Edit Challenge Details ->

  /**
   * Edit challenge title function
   */
  editChallengeTitle() {
    const SELF = this;

    SELF.apiCall = (params) => {
      const BODY = JSON.stringify(params);
      SELF.apiService
        .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY)
        .subscribe(
          (data) => {
            SELF.challenge.title = data.title;
            SELF.globalService.showToast('success', 'The challenge title is  successfully updated!', 5);
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
          },
          () => {}
        );
    };

    const PARAMS = {
      title: 'Edit Challenge Title',
      content: '',
      confirm: 'Submit',
      deny: 'Cancel',
      form: [
        {
          name: 'editChallengeTitle',
          isRequired: true,
          label: 'title',
          placeholder: 'Challenge Title',
          type: 'text',
          value: this.challenge.title,
        },
      ],
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }

  /**
   * Delete challenge
   */
  deleteChallenge() {
    const SELF = this;
    const redirectTo = '/dashboard';

    SELF.apiCall = () => {
      const BODY = JSON.stringify({});
      SELF.apiService.postUrl(SELF.endpointsService.deleteChallengeURL(SELF.challenge.id), BODY).subscribe(
        (data) => {
          SELF.router.navigate([redirectTo]);
          SELF.globalService.showToast('success', 'The Challenge is successfully deleted!', 5);
        },
        (err) => {
          SELF.globalService.handleApiError(err, true);
          SELF.globalService.showToast('error', err);
        },
        () => {}
      );
    };

    const PARAMS = {
      title: 'Delete Challenge',
      content: '',
      confirm: 'I understand consequences, delete the challenge',
      deny: 'Cancel',
      form: [
        {
          name: 'challegenDeleteInput',
          isRequired: true,
          label: '',
          placeholder: 'Please type in the name of the challenge to confirm',
          type: 'text',
          value: '',
        },
      ],
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }

  /**
   * Edit challenge image function
   */
  editChallengeImage() {
    const SELF = this;
    SELF.apiCall = (params) => {
      const FORM_DATA: FormData = new FormData();
      FORM_DATA.append('image', params['image']);
      SELF.apiService
        .patchFileUrl(
          SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id),
          FORM_DATA
        )
        .subscribe(
          (data) => {
            SELF.challenge.image = data.image;
            SELF.globalService.showToast('success', 'The Challenge image successfully updated!', 5);
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
          },
          () => {}
        );
    };

    /**
     * Parameters of the modal
     */
    const PARAMS = {
      title: 'Edit Challenge Image',
      content: '',
      confirm: 'Submit',
      deny: 'Cancel',
      form: [
        {
          name: 'Challenge Image',
          isRequired: true,
          label: 'image',
          placeholder: '',
          type: 'file',
          value: '',
        },
      ],
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }

  /**
   * Edit challenge overview with file function
   */
  editChallengeOverviewUpload() {
    const SELF = this;
    SELF.apiCall = (params) => {
      const FORM_DATA: FormData = new FormData();
      FORM_DATA.append('overview_file', params['overview_file']);
      SELF.apiService
        .patchFileUrl(
          SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id),
          FORM_DATA
        )
        .subscribe(
          (data) => {
            SELF.challenge.description = data.description;
            SELF.globalService.showToast('success', 'Challenge description updated successfully!', 5);
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
          },
          () => this.logger.info('EDIT-CHALLENGE-DESCRIPTION-FINISHED')
        );
    };

    /**
     * Parameters of the modal
     */
    const PARAMS = {
      title: 'Edit Challenge Overview',
      content: '',
      confirm: 'Submit',
      deny: 'Cancel',
      form: [
        {
          name: 'Challenge Overview',
          isRequired: true,
          label: 'overview_file',
          placeholder: '',
          type: 'file',
          value: '',
        },
      ],
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }
  
  /**
   * Edit challenge terms and conditions with file function
   */
  editChallengeTermsAndConditionsUpload() {
    const SELF = this;
    SELF.apiCall = (params) => {
      const FORM_DATA: FormData = new FormData();
      FORM_DATA.append('terms_and_conditions_file', params['terms_and_conditions_file']);
      SELF.apiService
        .patchFileUrl(
          SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id),
          FORM_DATA
        )
        .subscribe(
          (data) => {
            SELF.challenge.terms_and_conditions = data.terms_and_conditions;
            SELF.globalService.showToast('success', 'Terms and conditions updated successfully!', 5);
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
          },
          () => this.logger.info('EDIT-TERMS-AND-CONDITIONS-FINISHED')
        );
    };
    
    /**
     * Parameters of the modal
     */
    const PARAMS = {
      title: 'Edit Terms and Conditions',
      content: '',
      confirm: 'Submit',
      deny: 'Cancel',
      form: [
        {
          name: 'Challenge Terms and Descriptions',
          isRequired: true,
          label: 'terms_and_conditions_file',
          placeholder: '',
          type: 'file',
          value: '',
        },
      ],
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }

  /**
   * Edit challenge evaluation criteria with file function
   */ 
  editEvaluationCriteriaUpload() {
    const SELF = this;
    SELF.apiCall = (params) => {
      const FORM_DATA: FormData = new FormData();
      FORM_DATA.append('evaluation_criteria_file', params['evaluation_criteria_file']);
      SELF.apiService
        .patchFileUrl(
          SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id),
          FORM_DATA
        )
        .subscribe(
          (data) => {
            SELF.challenge.evaluation_details = data.evaluation_details;
            SELF.globalService.showToast('success', 'Evaluation details updated successfully!', 5);
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
          },
          () => this.logger.info('EDIT-CHALLENGE-EVALUATION-DETAILS-FINISHED')
        );
    };

    /**
     * Parameters of the modal
     */
    const PARAMS = {
      title: 'Edit Evaluation Criteria',
      content: '',
      confirm: 'Submit',
      deny: 'Cancel',
      form: [
        {
          name: 'Edit Evaluation Criteria',
          isRequired: true,
          label: 'evaluation_criteria_file',
          placeholder: '',
          type: 'file',
          value: '',
        },
      ],
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
 
  }

  /**
   * Edit phase details criteria with file function
   */ 
  editPhaseDetailsUpload() {
    const SELF = this;
    SELF.apiCall = (params) => {
      const FORM_DATA: FormData = new FormData();
      FORM_DATA.append('phase_description_file', params['phase_description_file']);
      SELF.apiService
        .patchFileUrl(
          SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.challenge.id, SELF.selectedPhase['id']),
          FORM_DATA
        )
        .subscribe(
          (data) => {
            for (const attrname of Object.keys(data)) {
              SELF.selectedPhase[attrname] = data[attrname];
            }
            SELF.globalService.showToast('success', 'Challenge phase description updated successfully!');
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
          },
          () => this.logger.info('PHASE-DESCRIPTION-UPDATE-FINISHED')
        );
    };
    
    /**
     * Parameters of the modal
     */
    const PARAMS = {
      title: 'Edit Phase Description',
      content: '',
      confirm: 'Submit',
      deny: 'Cancel',
      form: [
        {
          name: 'Phase Description',
          isRequired: true,
          label: 'phase_description_file',
          placeholder: '',
          type: 'file',
          value: '',
        },
      ],
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }

  /**
   * Edit challenge overview function
   */
  editChallengeOverview() {
    const SELF = this;

    SELF.apiCall = (params) => {
      const BODY = JSON.stringify(params);
      SELF.apiService
        .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY)
        .subscribe(
          (data) => {
            SELF.challenge.description = data.description;
            SELF.globalService.showToast('success', 'The description is successfully updated!', 5);
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
          },
          () => this.logger.info('EDIT-CHALLENGE-DESCRIPTION-FINISHED')
        );
    };

    const PARAMS = {
      title: 'Edit Challenge Description',
      label: 'description',
      isEditorRequired: true,
      editorContent: this.challenge.description,
      confirm: 'Submit',
      deny: 'Cancel',
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }

  /**
   * Edit terms and conditions of the challenge
   */
  editTermsAndConditions() {
    const SELF = this;
    SELF.apiCall = (params) => {
      const BODY = JSON.stringify(params);
      SELF.apiService
        .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY)
        .subscribe(
          (data) => {
            SELF.challenge.terms_and_conditions = data.terms_and_conditions;
            this.updateView();
            SELF.globalService.showToast('success', 'The terms and conditions are successfully updated!', 5);
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
          },
          () => this.logger.info('EDIT-TERMS-AND-CONDITIONS-FINISHED')
        );
    };

    /**
     * Parameters of the modal
     */
    const PARAMS = {
      title: 'Edit Terms And Conditions',
      label: 'terms_and_conditions',
      isEditorRequired: true,
      editorContent: this.challenge.terms_and_conditions,
      confirm: 'Submit',
      deny: 'Cancel',
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }

  /**
   * Edit challenge start and end date function
   */
  challengeDateDialog() {
    const SELF = this;
    SELF.apiCall = (params) => {
      if (new Date(params.start_date).valueOf() < new Date(params.end_date).valueOf()) {
        const BODY = JSON.stringify({
          start_date: new Date(params.start_date).toISOString(),
          end_date: new Date(params.end_date).toISOString(),
        });
        SELF.apiService
          .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY)
          .subscribe(
            (data) => {
              SELF.challenge.start_date = data.start_date;
              SELF.challenge.end_date = data.end_date;
              SELF.globalService.showToast('success', 'The Challenge start and end date successfully updated!', 5);
            },
            (err) => {
              SELF.globalService.handleApiError(err, true);
              SELF.globalService.showToast('error', err);
            },
            () => {}
          );
      } else {
        SELF.globalService.showToast('error', 'The challenge start date cannot be same or greater than end date.', 5);
      }
    };
    const PARAMS = {
      title: 'Edit Challenge Start and End Date',
      content: '',
      confirm: 'Confirm',
      deny: 'Cancel',
      form: [
        {
          isRequired: false,
          label: 'start_date',
          placeholder: 'Start Date and Time',
          type: 'text',
          value: moment(this.challenge.start_date).format('MM-DD-YYYY hh:mm a'),
        },
        {
          isRequired: false,
          label: 'end_date',
          placeholder: 'End Date and Time',
          type: 'text',
          value: moment(this.challenge.end_date).format('MM-DD-YYYY hh:mm a'),
        },
      ],
      isButtonDisabled: true,
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }

  /**
   * Publish challenge click function
   */
  togglePublishChallengeState() {
    const SELF = this;
    let toggleChallengePublishState, isPublished;
    if (this.publishChallenge.state === 'Published') {
      toggleChallengePublishState = 'private';
      isPublished = false;
    } else {
      toggleChallengePublishState = 'public';
      isPublished = true;
    }
    SELF.apiCall = () => {
      const BODY = JSON.stringify({
        published: isPublished,
      });
      SELF.apiService
        .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY)
        .subscribe(
          (data) => {
            if (isPublished) {
              this.publishChallenge.state = 'Published';
              this.publishChallenge.icon = 'fa fa-eye green-text';
            } else {
              this.publishChallenge.state = 'Not Published';
              this.publishChallenge.icon = 'fa fa-eye-slash red-text';
            }
            SELF.globalService.showToast(
              'success',
              'The challenge was successfully made ' + toggleChallengePublishState,
              5
            );
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
          },
          () => this.logger.info('PUBLISH-CHALLENGE-UPDATE-FINISHED')
        );
    };

    const PARAMS = {
      title: 'Make this challenge ' + toggleChallengePublishState + '?',
      content: '',
      confirm: "Yes, I'm sure",
      deny: 'No',
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showConfirm(PARAMS);
  }

  /**
   * Close participation function
   */
  stopParticipation(event) {
    event.preventDefault();
    const participationState = this.challenge['is_registration_open'] ? 'Close' : 'Open';
    const closeParticipationMsg = 'Participation is closed successfully';
    const openParticipationMsg = 'Participation is opened successfully';

    this.apiCall = () => {
      if (this.isChallengeHost && this.challenge['id'] !== null) {
        const BODY = JSON.stringify({
          is_registration_open: !this.challenge['is_registration_open'],
        });
        this.apiService
          .patchUrl(this.endpointsService.editChallengeDetailsURL(this.challenge.creator.id, this.challenge.id), BODY)
          .subscribe(
            () => {
              this.challenge['is_registration_open'] = !this.challenge['is_registration_open'];
              if (this.challenge['is_registration_open']) {
                this.globalService.showToast('success', openParticipationMsg, 5);
              } else {
                this.globalService.showToast('success', closeParticipationMsg, 5);
              }
            },
            (err) => {
              this.globalService.handleApiError(err, true);
              this.globalService.showToast('error', err);
            },
            () => {}
          );
      }
    };

    const PARAMS = {
      title: participationState + ' participation in the challenge?',
      content: '',
      confirm: "Yes, I'm sure",
      deny: 'No',
      confirmCallback: this.apiCall,
    };
    this.globalService.showConfirm(PARAMS);
  }

  // Banned Email Ids ->

  updateView() {
    this.bannedEmailIds = this.challenge.banned_email_ids || [];
    this.formerBannedEmailIds = this.bannedEmailIds.concat(); // Creating deep copy
  }

  /**
   * Add banned email chip
   * @param event current event
   */
  add(event: MatChipInputEvent): void {
    const SELF = this;
    const input = event.input;
    const value = event.value;
    SELF.isValidationError = false;
    SELF.message = '';

    // Add our fruit
    if ((value || '').trim()) {
      if (!SELF.validateEmail(value.trim())) {
        SELF.isValidationError = true;
        SELF.message = 'Please enter a valid email!';
      } else {
        SELF.bannedEmailIds.push(value.trim());
      }
    }

    // Reset the input value
    if (input && !SELF.isValidationError) {
      input.value = '';
    }
  }

  /**
   * Remove banned email chip
   * @param email Banned email id
   */
  remove(email): void {
    const SELF = this;
    const index = SELF.bannedEmailIds.indexOf(email);

    if (index >= 0) {
      SELF.bannedEmailIds.splice(index, 1);
    }

    // updating the banned Email Ids list
    SELF.updateBannedEmailList();
  }

  validateEmail(email) {
    if (email === '') {
      return true;
    }
    const regex = /^\s*[\w\-\+_]+(\.[\w\-\+_]+)*\@[\w\-\+_]+\.[\w\-\+_]+(\.[\w\-\+_]+)*\s*$/;
    return String(email).search(regex) !== -1;
  }

  reflectChange() {
    if (this.bannedEmailIds.toString() === this.formerBannedEmailIds.toString()) {
      this.globalService.showToast('error', 'No change to reflect!');
    } else if (this.isValidationError) {
      this.globalService.showToast('error', 'Please enter a valid email!');
    } else {
      this.updateBannedEmailList();
    }
  }

  updateBannedEmailList() {
    const SELF = this;
    const BODY = JSON.stringify({
      banned_email_ids: SELF.bannedEmailIds,
    });
    SELF.apiService
      .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY)
      .subscribe(
        (data) => {
          SELF.challenge.banned_email_ids = data.banned_email_ids;
          SELF.isBannedEmailInputVisible = false;
          SELF.globalService.showToast('success', 'Banned participant emails are successfully updated!', 5);
          this.formerBannedEmailIds = this.bannedEmailIds.concat(); // Creating deep copy
        },
        (err) => {
          SELF.globalService.handleApiError(err, true);
          SELF.globalService.showToast('error', err);
        },
        () => {}
      );
  }

  // Edit Phase Details ->

  /**
   * Called when a phase is selected (from child component)
   */
  phaseSelected() {
    const SELF = this;
    return (phase) => {
      SELF.selectedPhase = phase;
      SELF.isPhasePublic = SELF.selectedPhase['is_public'];
      SELF.isSubmissionPublic = SELF.selectedPhase['is_submission_public'];
      SELF.isLeaderboardPublic = SELF.selectedPhase['leaderboard_public'];
      if (SELF.isPhasePublic) {
        SELF.phaseVisibility.state = 'Public';
        SELF.phaseVisibility.icon = 'fa fa-toggle-on green-text';
      } else {
        SELF.phaseVisibility.state = 'Private';
        SELF.phaseVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1';
      }
      if (SELF.isSubmissionPublic) {
        SELF.submissionVisibility.state = 'Public';
        SELF.submissionVisibility.icon = 'fa fa-toggle-on green-text';
      } else {
        SELF.submissionVisibility.state = 'Private';
        SELF.submissionVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1';
      }
    };
  }

  /**
   * Edit Phase Details function
   */
  editPhaseDetails() {
    const SELF = this;
    SELF.apiCall = (params) => {
      const FORM_DATA: FormData = new FormData();
      for (const key in params) {
        if (params[key]) {
          FORM_DATA.append(key, params[key]);
        }
      }
      SELF.apiService
        .patchFileUrl(
          SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.challenge.id, SELF.selectedPhase['id']),
          FORM_DATA
        )
        .subscribe(
          (data) => {
            for (const attrname of Object.keys(data)) {
              SELF.selectedPhase[attrname] = data[attrname];
            }
            SELF.globalService.showToast('success', 'The challenge phase details are successfully updated!');
          },
          (err) => {
            SELF.globalService.showToast('error', err);
          },
          () => {
            this.logger.info('PHASE-UPDATE-FINISHED');
          }
        );
    };

    const PARAMS = {
      title: 'Edit Challenge Phase Details',
      name: SELF.selectedPhase['name'],
      label: 'description',
      description: SELF.selectedPhase['description'],
      startDate: SELF.selectedPhase['start_date'],
      endDate: SELF.selectedPhase['end_date'],
      maxSubmissionsPerDay: SELF.selectedPhase['max_submissions_per_day'],
      maxSubmissionsPerMonth: SELF.selectedPhase['max_submissions_per_month'],
      maxSubmissions: SELF.selectedPhase['max_submissions'],
      maxConcurrentSubmissionsAllowed: SELF.selectedPhase['max_concurrent_submissions_allowed'],
      allowedSubmissionFileTypes: SELF.selectedPhase['allowed_submission_file_types'],
      confirm: 'Submit',
      deny: 'Cancel',
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showEditPhaseModal(PARAMS);
  }

  /**
   * Phase Visibility toggle function
   */
  togglePhaseVisibility() {
    const SELF = this;
    let togglePhaseVisibilityState, isPublic;
    if (SELF.phaseVisibility.state === 'Public') {
      togglePhaseVisibilityState = 'private';
      isPublic = false;
      SELF.phaseVisibility.state = 'Private';
      SELF.phaseVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1';
    } else {
      togglePhaseVisibilityState = 'public';
      isPublic = true;
      SELF.phaseVisibility.state = 'Public';
      SELF.phaseVisibility.icon = 'fa fa-toggle-on green-text';
    }
    const BODY: FormData = new FormData();
    BODY.append('is_public', isPublic);
    SELF.apiService
      .patchFileUrl(
        SELF.endpointsService.updateChallengePhaseDetailsURL(SELF.selectedPhase['challenge'], SELF.selectedPhase['id']),
        BODY
      )
      .subscribe(
        (data) => {
          SELF.selectedPhase['is_public'] = data.is_public;
          SELF.selectedPhase['showPrivate'] = !data.is_public;
          SELF.challengeService.changePhaseSelected(true);
          if (!SELF.selectedPhase['is_public']) {
            for (let i = 0; i < SELF.phaseSplits.length; i++) {
              if (
                SELF.phaseSplits[i]['challenge_phase_name'] === SELF.selectedPhase['name'] &&
                SELF.phaseSplits[i]['visibility'] === 3
              ) {
                const visibility: any = 1;
                const FORM: FormData = new FormData();
                FORM.append('visibility', visibility);
                SELF.apiService
                  .patchFileUrl(SELF.endpointsService.particularChallengePhaseSplitUrl(SELF.phaseSplits[i]['id']), FORM)
                  .subscribe(
                    (dataPhaseSplit) => {
                      SELF.phaseSplits[i]['visibility'] = dataPhaseSplit.visibility;
                      SELF.challengeService.changePhaseSplitSelected(true);
                      SELF.phaseSplits[i]['showPrivate'] = true;
                      if (SELF.selectedPhaseSplit === SELF.phaseSplits[i]) {
                        SELF.leaderboardVisibility.state = 'Private';
                        SELF.leaderboardVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1';
                      }
                    },
                    (err) => {
                      SELF.globalService.handleApiError(err, true);
                      SELF.globalService.showToast('error', err);
                    },
                    () => this.logger.info('Change leaderboard visibility after phase is updated')
                  );
              }
            }
          }

          SELF.globalService.showToast('success', 'The phase was successfully made ' + togglePhaseVisibilityState, 5);
        },
        (err) => {
          SELF.globalService.handleApiError(err, true);
          SELF.globalService.showToast('error', err);
          if (isPublic) {
            SELF.phaseVisibility.state = 'Private';
            SELF.phaseVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1';
          } else {
            SELF.phaseVisibility.state = 'Public';
            SELF.phaseVisibility.icon = 'fa fa-toggle-on green-text';
          }
        },
        () => this.logger.info('PHASE-VISIBILITY-UPDATE-FINISHED')
      );
  }

  /**
   * Submission Visibility toggle function
   */
  toggleSubmissionVisibility() {
    const SELF = this;
    if (SELF.isLeaderboardPublic === true) {
      let toggleSubmissionVisibilityState, isSubmissionPublic;
      if (SELF.submissionVisibility.state === 'Public') {
        toggleSubmissionVisibilityState = 'private';
        isSubmissionPublic = false;
        SELF.submissionVisibility.state = 'Private';
        SELF.submissionVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1';
      } else {
        toggleSubmissionVisibilityState = 'public';
        isSubmissionPublic = true;
        SELF.submissionVisibility.state = 'Public';
        SELF.submissionVisibility.icon = 'fa fa-toggle-on green-text';
      }
      const BODY: FormData = new FormData();
      BODY.append('is_submission_public', isSubmissionPublic);
      SELF.apiService
        .patchFileUrl(
          SELF.endpointsService.updateChallengePhaseDetailsURL(
            SELF.selectedPhase['challenge'],
            SELF.selectedPhase['id']
          ),
          BODY
        )
        .subscribe(
          (data) => {
            SELF.selectedPhase['is_submission_public'] = data.is_submission_public;
            SELF.globalService.showToast(
              'success',
              'The submissions were successfully made ' + toggleSubmissionVisibilityState,
              5
            );
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
            if (isSubmissionPublic) {
              SELF.submissionVisibility.state = 'Private';
              SELF.submissionVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1';
            } else {
              SELF.submissionVisibility.state = 'Public';
              SELF.submissionVisibility.icon = 'fa fa-toggle-on green-text';
            }
          },
          () => this.logger.info('SUBMISSION-VISIBILITY-UPDATE-FINISHED')
        );
    } else {
      SELF.globalService.showToast('error', 'Leaderboard is private, please make the leaderboard public');
    }
  }

  showCliInstructions() {
    const SELF = this;
    /**
     * Parameters of the modal
     */
    const PARAMS = {
      title: 'Instructions',
      label: 'evaluation_details',
      isCliInstructions: true,
    };
    SELF.globalService.showModal(PARAMS);
  }

  /**
   * Edit test annotations of the phase
   */
  editTestAnnotations() {
    const SELF = this;
    SELF.apiCall = (params) => {
      const FORM_DATA: FormData = new FormData();
      FORM_DATA.append('test_annotation', params['test_annotation']);
      SELF.apiService
        .patchFileUrl(
          SELF.endpointsService.updateChallengePhaseDetailsURL(
            SELF.selectedPhase['challenge'],
            SELF.selectedPhase['id']
          ),
          FORM_DATA
        )
        .subscribe(
          (data) => {
            SELF.globalService.showToast('success', 'The test annotations are successfully updated!');
          },
          (err) => {
            SELF.globalService.showToast('error', err);
          },
          () => this.logger.info('EDIT-TEST-ANNOTATION-FINISHED')
        );
    };

    /**
     * Parameters of the modal
     */
    const PARAMS = {
      title: 'Edit Test Annotations',
      confirm: 'Submit',
      deny: 'Cancel',
      form: [
        {
          name: 'testAnnotation',
          isRequired: true,
          label: 'test_annotation',
          placeholder: '',
          type: 'file',
          value: '',
        },
      ],
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }

  // Edit Leaderboard Details ->

  /**
   * This is called when a phase split is selected (from child components)
   */
  phaseSplitSelected() {
    const SELF = this;
    return (phaseSplit) => {
      SELF.selectedPhaseSplit = phaseSplit;
      SELF.isPhaseSplitLeaderboardPublic = SELF.selectedPhaseSplit['visibility'];
      if (SELF.isPhaseSplitLeaderboardPublic === 3) {
        SELF.leaderboardVisibility.state = 'Public';
        SELF.leaderboardVisibility.icon = 'fa fa-toggle-on green-text';
      } else {
        SELF.leaderboardVisibility.state = 'Private';
        SELF.leaderboardVisibility.icon = 'fa fa fa-toggle-off grey-text text-darken-1';
      }
      const API_PATH = SELF.endpointsService.particularChallengePhaseSplitUrl(this.selectedPhaseSplit['id']);
      SELF.apiService.getUrl(API_PATH).subscribe(
        (data) => {
          SELF.leaderboardPrecisionValue = data.leaderboard_decimal_precision;
          SELF.setLeaderboardPrecisionValue = `1.${SELF.leaderboardPrecisionValue}-${SELF.leaderboardPrecisionValue}`;
          SELF.selectedLeaderboardId = data.leaderboard;
        },
        (err) => {
          SELF.globalService.handleApiError(err);
        },
        () => {
          SELF.apiService
            .getUrl(
              this.endpointsService.getOrUpdateLeaderboardSchemaURL(SELF.selectedLeaderboardId)
            )
          .subscribe(
            (data) => {
              SELF.leaderboard = data;
            },
            (err) => {
                SELF.globalService.showToast('error', "Could not fetch leaderboard schema");
            },
            () => {}
          );
        }
      );
    };
  }

  /**
   * Leadeboard Visibility toggle function
   */
  toggleLeaderboardVisibility() {
    const SELF = this;
    let toggleLeaderboardVisibilityState, visibility, phaseIsPublic;
    for (let i = 0; i < SELF.filteredPhases.length; i++) {
      if (SELF.filteredPhases[i]['name'] === SELF.selectedPhaseSplit['challenge_phase_name']) {
        phaseIsPublic = SELF.filteredPhases[i]['is_public'];
      }
    }
    if (phaseIsPublic) {
      if (SELF.leaderboardVisibility.state === 'Public') {
        toggleLeaderboardVisibilityState = 'private';
        visibility = 1;
        SELF.leaderboardVisibility.state = 'Private';
        SELF.leaderboardVisibility.icon = 'fa fa fa-toggle-off grey-text text-darken-1';
      } else {
        toggleLeaderboardVisibilityState = 'public';
        visibility = 3;
        SELF.leaderboardVisibility.state = 'Public';
        SELF.leaderboardVisibility.icon = 'fa fa-toggle-on green-text';
      }
      const BODY: FormData = new FormData();
      BODY.append('visibility', visibility);
      SELF.apiService
        .patchFileUrl(SELF.endpointsService.particularChallengePhaseSplitUrl(SELF.selectedPhaseSplit['id']), BODY)
        .subscribe(
          (data) => {
            SELF.selectedPhaseSplit['visibility'] = data.visibility;
            SELF.challengeService.changePhaseSplitSelected(true);
            if (visibility === 3) {
              SELF.selectedPhaseSplit['showPrivate'] = false;
              SELF.leaderboardVisibility.state = 'Public';
              SELF.leaderboardVisibility.icon = 'fa fa-toggle-on green-text';
            } else {
              SELF.selectedPhaseSplit['showPrivate'] = true;
              SELF.leaderboardVisibility.state = 'Private';
              SELF.leaderboardVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1';
            }
            SELF.globalService.showToast(
              'success',
              'The phase split was successfully made ' + toggleLeaderboardVisibilityState,
              5
            );
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
            if (visibility === 3) {
              SELF.leaderboardVisibility.state = 'Private';
              SELF.leaderboardVisibility.icon = 'fa fa-toggle-off grey-text text-darken-1';
            } else {
              SELF.leaderboardVisibility.state = 'Public';
              SELF.leaderboardVisibility.icon = 'fa fa-toggle-on green-text';
            }
          },
          () => this.logger.info('LEADERBOARD-VISIBILITY-UPDATE-FINISHED')
        );
    } else {
      SELF.globalService.showToast('error', 'The phase is private, please make the phase public');
    }
  }

  /**
   * Update leaderboard decimal precision value
   * @param updatedLeaderboardPrecisionValue new leaderboard precision value
   */
  updateLeaderboardDecimalPrecision(updatedLeaderboardPrecisionValue) {
    const API_PATH = this.endpointsService.particularChallengePhaseSplitUrl(this.selectedPhaseSplit['id']);
    const SELF = this;
    SELF.leaderboardPrecisionValue = updatedLeaderboardPrecisionValue;
    SELF.setLeaderboardPrecisionValue = '1.' + SELF.leaderboardPrecisionValue + '-' + SELF.leaderboardPrecisionValue;
    const BODY = JSON.stringify({
      leaderboard_decimal_precision: SELF.leaderboardPrecisionValue,
    });
    SELF.apiService.patchUrl(API_PATH, BODY).subscribe(
      (data) => {
        this.minusDisabled = SELF.leaderboardPrecisionValue === 0 ? true : false;
        this.plusDisabled = SELF.leaderboardPrecisionValue === 20 ? true : false;
      },
      (err) => {
        SELF.globalService.handleApiError(err, true);
      },
      () => this.logger.info('EDIT-LEADERBOARD-PRECISION-VALUE-FINISHED')
    );
  }

  // Edit Evaluation Script and Criteria ->

  /**
   * Edit evaluation criteria of the challenge
   */
  editEvaluationCriteria() {
    const SELF = this;
    SELF.apiCall = (params) => {
      const BODY = JSON.stringify(params);
      SELF.apiService
        .patchUrl(SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id), BODY)
        .subscribe(
          (data) => {
            SELF.challenge.evaluation_details = data.evaluation_details;
            this.updateView();
            SELF.globalService.showToast('success', 'The evaluation details is successfully updated!', 5);
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
          },
          () => this.logger.info('EDIT-CHALLENGE-EVALUATION-DETAILS-FINISHED')
        );
    };

    /**
     * Parameters of the modal
     */
    const PARAMS = {
      title: 'Edit Evaluation Details',
      label: 'evaluation_details',
      isEditorRequired: true,
      editorContent: this.challenge.evaluation_details,
      confirm: 'Submit',
      deny: 'Cancel',
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }

  /**
   * Edit evaluation script of the challenge
   */
  editEvaluationScript() {
    const SELF = this;
    SELF.apiCall = (params) => {
      const FORM_DATA: FormData = new FormData();
      FORM_DATA.append('evaluation_script', params['evaluation_script']);
      SELF.apiService
        .patchFileUrl(
          SELF.endpointsService.editChallengeDetailsURL(SELF.challenge.creator.id, SELF.challenge.id),
          FORM_DATA
        )
        .subscribe(
          (data) => {
            SELF.globalService.showToast('success', 'The evaluation script is successfully updated!');
          },
          (err) => {
            SELF.globalService.showToast('error', err);
          },
          () => this.logger.info('EDIT-EVALUATION-SCRIPT-FINISHED')
        );
    };

    /**
     * Parameters of the modal
     */
    const PARAMS = {
      title: 'Edit Evaluation Script',
      confirm: 'Submit',
      deny: 'Cancel',
      form: [
        {
          name: 'evaluationScript',
          isRequired: true,
          label: 'evaluation_script',
          placeholder: '',
          type: 'file',
          value: '',
        },
      ],
      confirmCallback: SELF.apiCall,
    };
    SELF.globalService.showModal(PARAMS);
  }

  // Worker logs ->

  editLeaderboardSchema() {
    const SELF = this;
    SELF.apiCall = (params) => {
      let currentLeaderboard = this.leaderboard;
      this.logger.info(params);
      currentLeaderboard.schema = params.schema;
      const BODY = JSON.stringify(currentLeaderboard);
      this.logger.info(BODY);

      SELF.apiService
        .patchUrl(SELF.endpointsService.getOrUpdateLeaderboardSchemaURL(SELF.leaderboard.id), BODY)
        .subscribe(
          (data) => {
            SELF.leaderboard = data;
            SELF.globalService.showToast('success', 'Schema Succesfully Updated!', 5);
          },
          (err) => {
            SELF.globalService.handleApiError(err, true);
            SELF.globalService.showToast('error', err);
          },
          () => this.logger.info('EDIT-LEADERBOARD-SCHEMA-FINISHED')
        );
    };

    const PARAMS = {
      title: 'Edit Leaderboard Schema',
      content: '',
      confirm: 'Confirm',
      deny: 'Cancel',
      form: [
        {
          isRequired: false,
          label: 'schema',
          placeholder: 'schema',
          type: 'text',
          value: JSON.stringify(this.leaderboard.schema),
        },
      ],
      isButtonDisabled: true,
      confirmCallback: SELF.apiCall,
    };

    SELF.globalService.showModal(PARAMS);
  
  }

  /**
   * API call to manage the worker from UI.
   * Response data will be like: {action: "Success" or "Failure", error: <String to include only if action is Failure.>}
   */
  manageWorker(action) {
    const SELF = this;
    const API_PATH = SELF.endpointsService.manageWorkerURL(SELF.challenge['id'], action);
    const BODY = JSON.stringify('');
    SELF.apiService.putUrl(API_PATH, BODY).subscribe(
      (data) => {
        if (data[action] === 'Success') {
          SELF.globalService.showToast('success', 'Worker(s) ' + action + 'ed succesfully.', 5);
        } else {
          SELF.globalService.showToast('error', data['error'], 5);
        }
      },
      (err) => {
        SELF.globalService.handleApiError(err, true);
      },
      () => {}
    );
  }

  // Get the logs from worker if submissions are failing.
  fetchWorkerLogs() {
    if (this.challenge['id']) {
      const API_PATH = this.endpointsService.getLogsURL(this.challenge['id']);
      const SELF = this;
      SELF.apiService.getUrl(API_PATH, true, false).subscribe(
        (data) => {
          SELF.workerLogs = [];
          for (let i = 0; i < data.logs.length; i++) {
            SELF.workerLogs.push(data.logs[i]);
          }
        },
        (err) => {
          SELF.globalService.handleApiError(err);
        },
        () => {}
      );
    }
  }

  // Get the logs from worker if submissions are failing at an interval of 5sec.
  startLoadingLogs() {
    const SELF = this;
    SELF.pollingInterval = setInterval(function () {
      SELF.fetchWorkerLogs();
    }, 5000);
  }

  /**
   * Component on destroyed.
   */
  ngOnDestroy() {
    clearInterval(this.pollingInterval);
  }
}