<commit_before>import { captureException } from 'common/errorLogger';
import SPELLS from 'common/SPELLS';
import { EventType } from 'parser/core/Events';
import EventsNormalizer from 'parser/core/EventsNormalizer';
import Abilities from 'parser/core/modules/Abilities';
import Buffs from 'parser/core/modules/Buffs';

import ApplyBuff from './ApplyBuff';

const debug = false;

/**
 * This normalizer attempts to track all potentially relevant casts that
 * happened before the pull. It then fabricates cast events in order to help
 * other modules (like cast efficiency) be more accurate.
 *
 * Additionally, because there is currently no ability info on whether or not a
 * spell is instant or has a cast time, we cannot (yet) accurately fabricate
 * the right type of event (begincast + cast vs only a cast).
 *
 * @property {Abilities} abilities
 * @property {Buffs} buffs
 * @property {ApplyBuff} applyBuff
 */
class PrePullCooldowns extends EventsNormalizer {
  /*
   * Abilities is an analyzer, which means that accessing it from a
   * normalizer like this one is not ideal design. For now, to avoid having to
   * rewrite Abilities (a core module heavily coupled with pretty much every
   * specialization), just be aware that using any Ability properties that
   * require event data (like .cooldown if it's a func, etc.) will *not*
   * produce intended results. In this normalizer I only access parts of the
   * spellbook with static values.
   */
  static dependencies = {
    abilities: Abilities,
    buffs: Buffs,
    applyBuff: ApplyBuff, // we need fabricated events for untracked prepull applied buffs
  };

  /**
   * Rather than constantly querying Abilities, here we simplify into two
   * new arrays that we can modify without messing with the spellbook
   */
  getApplicableSpells() {
    const buffSpells = [];
    const damageSpells = [];
    const addBuff = (buff, buffId) => {
      if (!buff.triggeredBySpellId) {
        // This normalizer is to fabricate prepull cast events that can be detected from buffs (and damage). If a buff isn't triggered by a spell, then there's nothing to fabricate.
        return;
      }

      buffSpells.push({
        castId: buff.triggeredBySpellId,
        buffId,
      });
    };

    this.buffs.activeBuffs.forEach((buff) => {
      if (buff.spellId instanceof Array) {
        // Add each buff separate to make usage easier
        buff.spellId.forEach((spellId) => {
          addBuff(buff, spellId);
        });
      } else {
        addBuff(buff, buff.spellId);
      }
    });

    this.abilities.activeAbilities.forEach((ability) => {
      if (ability.damageSpellIds) {
        damageSpells.push({
          castId: ability.spell,
          damageIds: ability.damageSpellIds,
        });
      }
    });

    return {
      buffSpells,
      damageSpells,
    };
  }

  normalize(events) {
    const { buffSpells, damageSpells } = this.getApplicableSpells();
    const prepullCasts = [];
    let precastClassResources = null;

    // When the fight started. Used for instants.
    const fightStartTimestamp = this.owner.fight.start_time;
    // When the player first cast something. Used for everything else (this is intended to optimally estimate channeled spells).
    const firstEventIndex = this.getFightStartIndex(events);
    const firstTimestamp = events[firstEventIndex].timestamp;
    const playerId = this.owner.playerId;

    for (let i = 0; i < events.length; i += 1) {
      const event = events[i];
      const sourceId = event.sourceID;

      if (event.type === EventType.ApplyBuff) {
        // We rely on the ApplyBuff normalizer to set the prepull property
        if (!event.prepull) {
          continue;
        }

        for (let i = 0; i < buffSpells.length; i += 1) {
          if (buffSpells[i].buffId === event.ability.guid) {
            debug && console.debug(`Detected a precast buff cooldown: ${event.ability.name}`);
            if (
              buffSpells[i].castId instanceof Array &&
              buffSpells[i].castId.includes(event.ability.guid)
            ) {
              //try to find corresponding cast, otherwise pass list of casts
              prepullCasts.push(this.constructor._fabricateCastEvent(event));
            } else {
              prepullCasts.push(this.constructor._fabricateCastEvent(event, buffSpells[i].castId));
            }
            buffSpells.splice(i, 1);
            break;
          }
        }
        continue;
      }

      if (sourceId !== playerId) {
        continue;
      }

      if (event.type === EventType.Cast) {
        /**
         * This will copy the first resource information to all precast events.
         * It's not pretty or 100% accurate, but it prevents errors on analyzers
         * that require resource information and usually wont be too far off.
         *
         * In the future, a better way to handle this would be to make sure any
         * analyzers that need the resource information check for .prepull, or
         * we gain access to spell data that lets us build the resource
         * information more accurately.
         */
        if (precastClassResources === null && event.classResources) {
          debug && console.debug('Setting prepull class resources (but stripping costs) to:', event.classResources);
          precastClassResources = event.classResources;
          precastClassResources.map(classResource => {
            const noCostClassResource = Object.assign({}, classResource);
            noCostClassResource.cost = undefined;
            return noCostClassResource;
          });
        }

        // If a cast is found for a damage spell, remove it from the search
        for (let i = 0; i < damageSpells.length; i += 1) {
          if (damageSpells[i].castId === event.ability.guid) {<commit_msg>Fixed issue<commit_after>import { captureException } from 'common/errorLogger';
import SPELLS from 'common/SPELLS';
import { EventType } from 'parser/core/Events';
import EventsNormalizer from 'parser/core/EventsNormalizer';
import Abilities from 'parser/core/modules/Abilities';
import Buffs from 'parser/core/modules/Buffs';

import ApplyBuff from './ApplyBuff';

const debug = false;

/**
 * This normalizer attempts to track all potentially relevant casts that
 * happened before the pull. It then fabricates cast events in order to help
 * other modules (like cast efficiency) be more accurate.
 *
 * Additionally, because there is currently no ability info on whether or not a
 * spell is instant or has a cast time, we cannot (yet) accurately fabricate
 * the right type of event (begincast + cast vs only a cast).
 *
 * @property {Abilities} abilities
 * @property {Buffs} buffs
 * @property {ApplyBuff} applyBuff
 */
class PrePullCooldowns extends EventsNormalizer {
  /*
   * Abilities is an analyzer, which means that accessing it from a
   * normalizer like this one is not ideal design. For now, to avoid having to
   * rewrite Abilities (a core module heavily coupled with pretty much every
   * specialization), just be aware that using any Ability properties that
   * require event data (like .cooldown if it's a func, etc.) will *not*
   * produce intended results. In this normalizer I only access parts of the
   * spellbook with static values.
   */
  static dependencies = {
    abilities: Abilities,
    buffs: Buffs,
    applyBuff: ApplyBuff, // we need fabricated events for untracked prepull applied buffs
  };

  /**
   * Rather than constantly querying Abilities, here we simplify into two
   * new arrays that we can modify without messing with the spellbook
   */
  getApplicableSpells() {
    const buffSpells = [];
    const damageSpells = [];
    const addBuff = (buff, buffId) => {
      if (!buff.triggeredBySpellId) {
        // This normalizer is to fabricate prepull cast events that can be detected from buffs (and damage). If a buff isn't triggered by a spell, then there's nothing to fabricate.
        return;
      }

      buffSpells.push({
        castId: buff.triggeredBySpellId,
        buffId,
      });
    };

    this.buffs.activeBuffs.forEach((buff) => {
      if (buff.spellId instanceof Array) {
        // Add each buff separate to make usage easier
        buff.spellId.forEach((spellId) => {
          addBuff(buff, spellId);
        });
      } else {
        addBuff(buff, buff.spellId);
      }
    });

    this.abilities.activeAbilities.forEach((ability) => {
      if (ability.damageSpellIds) {
        damageSpells.push({
          castId: ability.spell,
          damageIds: ability.damageSpellIds,
        });
      }
    });

    return {
      buffSpells,
      damageSpells,
    };
  }

  normalize(events) {
    const { buffSpells, damageSpells } = this.getApplicableSpells();
    const prepullCasts = [];
    let precastClassResources = null;

    // When the fight started. Used for instants.
    const fightStartTimestamp = this.owner.fight.start_time;
    // When the player first cast something. Used for everything else (this is intended to optimally estimate channeled spells).
    const firstEventIndex = this.getFightStartIndex(events);
    const firstTimestamp = events[firstEventIndex].timestamp;
    const playerId = this.owner.playerId;

    for (let i = 0; i < events.length; i += 1) {
      const event = events[i];
      const sourceId = event.sourceID;

      if (event.type === EventType.ApplyBuff) {
        // We rely on the ApplyBuff normalizer to set the prepull property
        if (!event.prepull) {
          continue;
        }

        for (let i = 0; i < buffSpells.length; i += 1) {
          if (buffSpells[i].buffId === event.ability.guid) {
            debug && console.debug(`Detected a precast buff cooldown: ${event.ability.name}`);
            if (
              buffSpells[i].castId instanceof Array &&
              buffSpells[i].castId.includes(event.ability.guid)
            ) {
              //try to find corresponding cast, otherwise pass list of casts
              prepullCasts.push(this.constructor._fabricateCastEvent(event));
            } else {
              prepullCasts.push(this.constructor._fabricateCastEvent(event, buffSpells[i].castId));
            }
            buffSpells.splice(i, 1);
            break;
          }
        }
        continue;
      }

      if (sourceId !== playerId) {
        continue;
      }

      if (event.type === EventType.Cast) {
        /**
         * This will copy the first resource information to all precast events.
         * It's not pretty or 100% accurate, but it prevents errors on analyzers
         * that require resource information and usually wont be too far off.
         *
         * In the future, a better way to handle this would be to make sure any
         * analyzers that need the resource information check for .prepull, or
         * we gain access to spell data that lets us build the resource
         * information more accurately.
         */
        if (precastClassResources === null && event.classResources) {
          debug && console.debug('Setting prepull class resources (but stripping costs) to:', event.classResources);
          precastClassResources = event.classResources;
          // make a new class resources with costs stripped - have to deep copy to avoid messing with the original cast event
          precastClassResources = precastClassResources.map(classResource => {
            const noCostClassResource = Object.assign({}, classResource);
            noCostClassResource.cost = undefined;
            return noCostClassResource;
          });
        }

        // If a cast is found for a damage spell, remove it from the search
        for (let i = 0; i < damageSpells.length; i += 1) {
          if (damageSpells[i].castId === event.ability.guid) {
{'fraction_non_alphanumeric': 0.08956743002544529, 'fraction_numerical': 0.0016963528413910093, 'mean_word_length': 2.5461654135338345, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 13, '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': '3948460', 'n_tokens_mistral': 3206, 'n_tokens_neox': 3145, 'n_words': 1122}