<commit_before>import * as _ from 'underscore';
import { DimItem } from './item-types';
import { t } from 'i18next';

// step node names we'll hide, we'll leave "* Chroma" for now though, since we don't otherwise indicate Chroma
const FILTER_NODE_NAMES = [
  'Upgrade Defense',
  'Ascend',
  'Infuse',
  'Increase Intellect',
  'Increase Discipline',
  'Increase Strength',
  'Twist Fate',
  'The Life Exotic',
  'Reforge Artifact',
  'Reforge Shell',
  'Deactivate Chroma',
  'Kinetic Damage',
  'Solar Damage',
  'Arc Damage',
  'Void Damage'
];

function capitalizeFirstLetter(str: string) {
  if (!str || str.length === 0) {
    return '';
  }
  return str.charAt(0).toUpperCase() + str.slice(1);
}

function downloadCsv(filename, csv) {
  filename = `${filename}.csv`;
  const pom = document.createElement('a');
  pom.setAttribute('href', `data:text/csv;charset=utf-8,${encodeURIComponent(csv)}`);
  pom.setAttribute('download', filename);
  document.body.appendChild(pom);
  pom.click();
  document.body.removeChild(pom);
}

function buildNodeString(nodes) {
  let data = '';
  nodes.forEach((node) => {
    if (_.contains(FILTER_NODE_NAMES, node.name)) {
      return;
    }
    data += node.name;
    if (node.activated) {
      data += '*';
    }
    data += ',';
  });

  return data;
}

function downloadArmor(items, nameMap) {
  const header =
    'Name,Tag,Tier,Type,Equippable,Light,Owner,% Leveled,Locked,Equipped,Year,DTR Rating,# of Reviews,% Quality,% IntQ,% DiscQ,% StrQ,Int,Disc,Str,Notes,Perks\n';
  let data = '';
  items.forEach((item) => {
    data += `"${item.name}",`;
    data += `${item.dimInfo.tag || ''},`;
    data += `${item.tier},`;
    data += `${item.typeName},`;
    let equippable = item.classTypeName;
    if (!equippable || equippable === 'unknown') {
      equippable =
        !equippable || equippable === 'unknown' ? 'Any' : capitalizeFirstLetter(equippable);
    }
    data += `${equippable},`;
    data += `${item.primStat.value},`;
    data += `${nameMap[item.owner]},`;
    data += `${(item.percentComplete * 100).toFixed(0)},`;
    data += `${item.locked},`;
    data += `${item.equipped},`;
    data += `${item.year},`;
    if (item.dtrRating && item.dtrRating.overallScore) {
      data += `${item.dtrRating.overallScore},${item.dtrRating.ratingCount},`;
    } else {
      data += 'N/A,N/A,';
    }
    data += item.quality ? `${item.quality.min},` : '0,';
    const stats: { [name: string]: { value: number; pct: number } } = {};
    if (item.stats) {
      item.stats.forEach((stat) => {
        let pct = 0;
        if (stat.scaled && stat.scaled.min) {
          pct = Math.round((100 * stat.scaled.min) / stat.split);
        }
        stats[stat.name] = {
          value: stat.value,
          pct
        };
      });
    }
    data += stats.Intellect ? `${stats.Intellect.pct},` : '0,';
    data += stats.Discipline ? `${stats.Discipline.pct},` : '0,';
    data += stats.Strength ? `${stats.Strength.pct},` : '0,';
    data += stats.Intellect ? `${stats.Intellect.value},` : '0,';
    data += stats.Discipline ? `${stats.Discipline.value},` : '0,';
    data += stats.Strength ? `${stats.Strength.value},` : '0,';

    data += cleanNotes(item);

    // if DB is out of date this can be null, can't hurt to be careful
    if (item.talentGrid) {
      data += buildNodeString(item.talentGrid.nodes);
    }
    data += '\n';
  });
  downloadCsv('destinyArmor', header + data);
}

function downloadWeapons(guns, nameMap) {
  const header =
    'Name,Tag,Tier,Type,Light,Dmg,Owner,% Leveled,Locked,Equipped,Year,DTR Rating,# of Reviews,' +
    'AA,Impact,Range,Stability,ROF,Reload,Mag,Equip,' +
    'Notes,Nodes\n';
  let data = '';
  guns.forEach((gun) => {
    data += `"${gun.name}",`;
    data += `${gun.dimInfo.tag || ''},`;
    data += `${gun.tier},`;
    data += `${gun.typeName},`;
    data += `${gun.primStat.value},`;
    if (gun.dmg) {
      data += `${capitalizeFirstLetter(gun.dmg)},`;
    } else {
      data += 'Kinetic,';
    }
    data += `${nameMap[gun.owner]},`;
    data += `${(gun.percentComplete * 100).toFixed(0)},`;
    data += `${gun.locked},`;
    data += `${gun.equipped},`;
    data += `${gun.year},`;
    if (gun.dtrRating && gun.dtrRating.overallScore) {
      data += `${gun.dtrRating.overallScore},${gun.dtrRating.ratingCount},`;
    } else {
      data += 'N/A,N/A,';
    }
    const stats = {
      aa: 0,
      impact: 0,
      range: 0,
      stability: 0,
      rof: 0,
      reload: 0,
      magazine: 0,
      equipSpeed: 0
    };
    gun.stats.forEach((stat) => {
      switch (stat.statHash) {
        case 1345609583: // Aim Assist
          stats.aa = stat.value;
          break;
        case 4043523819: // Impact
          stats.impact = stat.value;
          break;
        case 1240592695: // Range
          stats.range = stat.value;
          break;
        case 155624089: // Stability
          stats.stability = stat.value;
          break;
        case 4284893193: // Rate of fire
          stats.rof = stat.value;
          break;
        case 4188031367: // Reload
          stats.reload = stat.value;
          break;
        case 3871231066: // Magazine
        case 925767036: // Energy
          stats.magazine = stat.value;
          break;
        case 943549884: // Equip Speed
          stats.equipSpeed = stat.value;
          break;
      }
    });
    data += `${stats.aa},`;
    data += `${stats.impact},`;
    data += `${stats.range},`;
    data += `${stats.stability},`;
    data += `${stats.rof},`;
    data += `${stats.reload},`;
    data += `${stats.magazine},`;
    data += `${stats.equipSpeed},`;

    data += cleanNotes(gun);

    // haven't seen this null yet, but can't hurt to check since we saw it on armor above
    if (gun.talentGrid) {
      data += buildNodeString(gun.talentGrid.nodes);
    }<commit_msg>Here, have some sockets exported to your spreadsheet.<commit_after>import * as _ from 'underscore';
import { DimItem, DimSockets } from './item-types';
import { t } from 'i18next';

// step node names we'll hide, we'll leave "* Chroma" for now though, since we don't otherwise indicate Chroma
const FILTER_NODE_NAMES = [
  'Upgrade Defense',
  'Ascend',
  'Infuse',
  'Increase Intellect',
  'Increase Discipline',
  'Increase Strength',
  'Twist Fate',
  'The Life Exotic',
  'Reforge Artifact',
  'Reforge Shell',
  'Deactivate Chroma',
  'Kinetic Damage',
  'Solar Damage',
  'Arc Damage',
  'Void Damage'
];

function capitalizeFirstLetter(str: string) {
  if (!str || str.length === 0) {
    return '';
  }
  return str.charAt(0).toUpperCase() + str.slice(1);
}

function downloadCsv(filename, csv) {
  filename = `${filename}.csv`;
  const pom = document.createElement('a');
  pom.setAttribute('href', `data:text/csv;charset=utf-8,${encodeURIComponent(csv)}`);
  pom.setAttribute('download', filename);
  document.body.appendChild(pom);
  pom.click();
  document.body.removeChild(pom);
}

function buildSocketString(sockets: DimSockets): string {
  const socketItems = sockets.sockets.map((s) =>
    s.plugOptions.map(
      (p) =>
        s.plug && s.plug.plugItem.hash && p.plugItem.hash === s.plug.plugItem.hash
          ? `${p.plugItem.displayProperties.name}*`
          : p.plugItem.displayProperties.name
    )
  );

  return socketItems.join(',');
}

function buildNodeString(nodes) {
  let data = '';
  nodes.forEach((node) => {
    if (_.contains(FILTER_NODE_NAMES, node.name)) {
      return;
    }
    data += node.name;
    if (node.activated) {
      data += '*';
    }
    data += ',';
  });

  return data;
}

function downloadArmor(items, nameMap) {
  const header =
    'Name,Tag,Tier,Type,Equippable,Light,Owner,% Leveled,Locked,Equipped,Year,DTR Rating,# of Reviews,% Quality,% IntQ,% DiscQ,% StrQ,Int,Disc,Str,Notes,Perks\n';
  let data = '';
  items.forEach((item) => {
    data += `"${item.name}",`;
    data += `${item.dimInfo.tag || ''},`;
    data += `${item.tier},`;
    data += `${item.typeName},`;
    let equippable = item.classTypeName;
    if (!equippable || equippable === 'unknown') {
      equippable =
        !equippable || equippable === 'unknown' ? 'Any' : capitalizeFirstLetter(equippable);
    }
    data += `${equippable},`;
    data += `${item.primStat.value},`;
    data += `${nameMap[item.owner]},`;
    data += `${(item.percentComplete * 100).toFixed(0)},`;
    data += `${item.locked},`;
    data += `${item.equipped},`;
    data += `${item.year},`;
    if (item.dtrRating && item.dtrRating.overallScore) {
      data += `${item.dtrRating.overallScore},${item.dtrRating.ratingCount},`;
    } else {
      data += 'N/A,N/A,';
    }
    data += item.quality ? `${item.quality.min},` : '0,';
    const stats: { [name: string]: { value: number; pct: number } } = {};
    if (item.stats) {
      item.stats.forEach((stat) => {
        let pct = 0;
        if (stat.scaled && stat.scaled.min) {
          pct = Math.round((100 * stat.scaled.min) / stat.split);
        }
        stats[stat.name] = {
          value: stat.value,
          pct
        };
      });
    }
    data += stats.Intellect ? `${stats.Intellect.pct},` : '0,';
    data += stats.Discipline ? `${stats.Discipline.pct},` : '0,';
    data += stats.Strength ? `${stats.Strength.pct},` : '0,';
    data += stats.Intellect ? `${stats.Intellect.value},` : '0,';
    data += stats.Discipline ? `${stats.Discipline.value},` : '0,';
    data += stats.Strength ? `${stats.Strength.value},` : '0,';

    data += cleanNotes(item);

    console.log(item);

    // if DB is out of date this can be null, can't hurt to be careful
    if (item.talentGrid) {
      data += buildNodeString(item.talentGrid.nodes);
    } else if (item.sockets) {
      data += buildSocketString(item.sockets);
    }
    data += '\n';
  });
  downloadCsv('destinyArmor', header + data);
}

function downloadWeapons(guns, nameMap) {
  const header =
    'Name,Tag,Tier,Type,Light,Dmg,Owner,% Leveled,Locked,Equipped,Year,DTR Rating,# of Reviews,' +
    'AA,Impact,Range,Stability,ROF,Reload,Mag,Equip,' +
    'Notes,Nodes\n';
  let data = '';
  guns.forEach((gun) => {
    data += `"${gun.name}",`;
    data += `${gun.dimInfo.tag || ''},`;
    data += `${gun.tier},`;
    data += `${gun.typeName},`;
    data += `${gun.primStat.value},`;
    if (gun.dmg) {
      data += `${capitalizeFirstLetter(gun.dmg)},`;
    } else {
      data += 'Kinetic,';
    }
    data += `${nameMap[gun.owner]},`;
    data += `${(gun.percentComplete * 100).toFixed(0)},`;
    data += `${gun.locked},`;
    data += `${gun.equipped},`;
    data += `${gun.year},`;
    if (gun.dtrRating && gun.dtrRating.overallScore) {
      data += `${gun.dtrRating.overallScore},${gun.dtrRating.ratingCount},`;
    } else {
      data += 'N/A,N/A,';
    }
    const stats = {
      aa: 0,
      impact: 0,
      range: 0,
      stability: 0,
      rof: 0,
      reload: 0,
      magazine: 0,
      equipSpeed: 0
    };
    gun.stats.forEach((stat) => {
      switch (stat.statHash) {
        case 1345609583: // Aim Assist
          stats.aa = stat.value;
          break;
        case 4043523819: // Impact
          stats.impact = stat.value;
          break;
        case 1240592695: // Range
          stats.range = stat.value;
          break;
        case 155624089: // Stability
          stats.stability = stat.value;
          break;
        case 4284893193: // Rate of fire
          stats.rof = stat.value;
          break;
        case 4188031367: // Reload
          stats.reload = stat.value;
          break;
        case 3871231066: // Magazine
        case 925767036: // Energy
          stats.magazine = stat.value;
          break;
        case 943549884: // Equip Speed
          stats.equipSpeed = stat.value;
          break;
      }
    });
    data += `${stats.aa},`;
    data += `${stats.impact},`;
    data += `${stats.range},`;
    data += `${stats.stability},`;
    data += `${stats.rof},`;
    data += `${stats.reload},`;
    data += `${stats.magazine},`;
    data += `${stats.equipSpeed},`;

    data += cleanNotes(gun);

    console.log(gun);

    // haven't seen this null yet, but can't hurt to check since we saw it on armor above
    if (gun.talentGrid) {
      data += buildNodeString(gun.talentGrid.nodes);
    } else if (gun.sockets) {
      data += buildSocketString(gun.sockets);
    }
{'fraction_non_alphanumeric': 0.18843404808317088, 'fraction_numerical': 0.01949317738791423, 'mean_word_length': 2.648296296296296, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2546936', 'n_tokens_mistral': 4529, 'n_tokens_neox': 4329, 'n_words': 855}