Yrkki/cv-generator-fe

View on GitHub
src/app/services/count-cache/count-cache.service.ts

Summary

Maintainability
A
0 mins
Test Coverage
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2018 Georgi Marinov
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import { Injectable } from '@angular/core';

import { ModelModel } from '../../model/model/model.model';

import { UiService } from '../../services/ui/ui.service';
import { ChartService } from '../../services/chart/chart.service';

import { TagCloudProcessorService } from '../../services/tag-cloud-processor/tag-cloud-processor.service';

import { Indexable } from '../../interfaces/indexable';
import { Language } from '../../interfaces/cv/language';
import { Project } from '../../interfaces/project/project';

/**
 * A count cache service.
 */
@Injectable({
  providedIn: 'root'
})
export class CountCacheService {
  /** Aggregation count cache. */
  /** Aggregation count cache getter. */
  public get countCache() { return this.model.countCache; }
  /** Aggregation count cache setter. */
  public set countCache(value) { this.model.countCache = value; }

  /** Frequencies cache. */
  /** Frequencies cache getter. */
  public get frequenciesCache() { return this.model.frequenciesCache; }
  /** Frequencies cache setter. */
  public set frequenciesCache(value) { this.model.frequenciesCache = value; }

  /** Project period decrypted. */
  public decryptedPeriod: Indexable = {};

  /** Filtered getter. */
  public get filtered() { return this.model.filtered; }

  /**
   * Constructs the count cache service.
   * ~constructor
   *
   * @param uiService The UI service injected dependency.
   * @param tagCloudProcessorService The tag cloud processor service injected dependency.
   * @param model The model injected dependency.
   * @param chartService The chart service injected dependency.
   */
  constructor(
    public readonly uiService: UiService,
    private readonly tagCloudProcessorService: TagCloudProcessorService,
    private readonly model: ModelModel,
    private readonly chartService: ChartService,
  ) {
  }

  /**
   * Gets the project period decrypted for a project
   *
   * @param project The project index
   */
  public getDecryptedProjectPeriod(project: Project): string {
    const period = 'Period';
    return this.decryptedPeriod[project[period]];
  }

  /**
   * One person team project indicator.
   *
   * @param project The project index
   */
  public getProjectIsOnePersonTeam(project: Project): boolean {
    const teamSize = 'Team size';
    return project[teamSize] === 1;
  }

  /** Calculates the count cache and refreshes project charts. */
  public calcCountCache(propertyNames: string[]) {
    this.calcCountCacheProper(propertyNames);

    if (propertyNames.length === 0 || propertyNames.includes('Project')) {
      this.chartService.refreshCharts();
    }
  }

  /** Calculates the count cache for the property types registered and refreshes the clients. */
  // eslint-disable-next-line max-lines-per-function
  private calcCountCacheProper(propertyNames: string[]) {
    // if (propertyNames.length === 0) {
    propertyNames = ['Project', 'Language', 'Accomplishment', 'Publication'];
    // }

    this.countCache = {};

    this.calcCountCacheProjectsFrequencies(propertyNames);
    if (propertyNames.includes('Language')) {
      this.calcFrequencies(this.filtered.Languages, 'Language');
    }
    if (propertyNames.includes('Accomplishment')) {
      this.calcFrequencies(this.filtered.Certifications, 'Certification');
      this.calcFrequencies(this.filtered.Courses, 'Name');
      this.calcFrequencies(this.filtered.Organizations, 'Organization');
      this.calcFrequencies(this.filtered.HonorsAndAwards, 'Honor and Award');
      this.calcFrequencies(this.filtered.Volunteering, 'Volunteering');
      this.calcFrequencies(this.filtered.InterestsAndHobbies, 'Interest and Hobby');
      this.calcFrequencies(this.filtered.Vacation, 'Vacation');
    }
    if (propertyNames.includes('Publication')) {
      this.calcFrequencies(this.filtered.Publications, 'Title');
    }
    if (propertyNames.includes('Project')) {
      this.calcCountCacheProjects();
    }
  }

  /** Preprocesses the project frequencies. */
  private calcCountCacheProjectsFrequencies(propertyNames: string[]) {
    if (propertyNames.includes('Project')) {
      for (const propertyName of [
        'Project',
        'Client', 'Country', 'Industry', 'Project type', 'System type',
        'Platform', 'Architecture', 'Languages and notations', 'IDEs and Tools', 'Methodology and practices',
        'Role', /* 'Responsibilities', */ 'Team size', 'Position', 'Reference'
      ]) {
        this.calcFrequencies(this.filtered.Projects, propertyName);
      }
      this.calcFrequencies(this.filtered.Projects, 'Responsibilities', undefined, true);
    }
  }

  /** Calculates the count cache for the projects. */
  private calcCountCacheProjects() {
    // calc sections start project and count cache
    let i = 0;
    let lastPeriod = '';
    const propertyName = 'New Period';
    for (const project of this.filtered.Projects) {
      const period = this.getDecryptedProjectPeriod(project);
      if (period === lastPeriod) {
        project[propertyName] = '';
      } else {
        project[propertyName] = period;

        this.countCache[lastPeriod] = i;
        lastPeriod = period;
        i = 0;
      }
      i++;
    }
    this.countCache[lastPeriod] = i;
  }

  /**
   * Checkes if the section toggle state is collapsed.
   *
   * @param propertyName The name of the property to process.
   *
   * @returns Whether the section toggle state is collapsed.
   */
  public checkToggleCollapsed(propertyName?: string): boolean {
    // if (this.persistenceService.getToggle(propertyName)['content-class'] === 'collapse') {
    //     this.countCache[propertyName] = 0;
    //     this.frequenciesCache[propertyName] = [];
    //     return true;
    // }

    return false;
  }

  /**
   * Calculates a splitter and then delegates to a service to calculate the frequency of occurrence of any value parts
   * in a collection objects' property based on that splitter character/string.
   *
   * @param collection The collection of objects to process.
   * @param propertyName The name of the property to process.
   * @param splitter The splitter character/string. Optional.
   * @param ai Whether to apply lexical analysis euristics when parsing each value encountered. Optional.
   *
   * @description
   * Also updates count and caches result.
   */
  private calcFrequencies(collection: any, propertyName: string, splitter: string = ', ', ai: boolean = false) {
    const frequenciesCacheKey = this.frequenciesCacheKey(collection, propertyName);
    const key = frequenciesCacheKey.key;
    propertyName = frequenciesCacheKey.propertyName;

    if (this.checkToggleCollapsed()) { return; }

    this.countCache[key] = 0;

    const entries = this.tagCloudProcessorService.calcFrequencies(collection, propertyName, splitter, ai);
    if ((typeof entries === 'undefined')) {
      return;
    }

    this.updateCount(key, entries.length);

    this.frequenciesCache[key] = entries;
  }

  /**
   * Calculates a frequencies cache key.
   *
   * @param collection The collection of objects to process.
   * @param propertyName The name of the property to process.
   *
   * @returns FrequenciesCacheKey calculated and modified propertyName.
   */
  private frequenciesCacheKey(collection: any, propertyName: string) {
    let key = propertyName;

    if (['Language', 'Certification', 'Organization', 'Honor and Award', 'Volunteering',
      'Interest and Hobby', 'Vacation', 'Project'].includes(propertyName)) {
      if (propertyName === 'Language') {
        collection.forEach((_: Language) => {
          _.Name = _.Language;
          _.Strength = _.Share;
        });
      } else if (propertyName === 'Project') {
        collection.forEach((_: Project) => {
          _.Name = _['Project name'];
          _.Strength = Number(this.getProjectIsOnePersonTeam(_));
        });
      }
      key = propertyName;
      propertyName = 'Name';
    }

    return { key, propertyName };
  }

  /**
   * Updates an entity's count.
   *
   * @param propertyName The name of the property to process.
   * @param count The new count.
   */
  private updateCount(propertyName: string, count: number) {
    if (propertyName === '' || typeof propertyName === 'undefined') {
      return;
    }

    if (typeof this.countCache[propertyName] !== 'number') {
      this.countCache[propertyName] = 0;
    }

    this.countCache[propertyName] += count;

    const entities = this.model.entities;

    if (!entities?.[propertyName]) {
      return;
    }

    const parentEntity = entities[propertyName].parent;

    this.updateCount(parentEntity, count);
  }
}