<commit_before>package org.pharmgkb.pharmcat.reporter.model.result;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.annotation.Nonnull;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import org.pharmgkb.pharmcat.definition.PhenotypeMap;
import org.pharmgkb.pharmcat.definition.model.GenePhenotype;
import org.pharmgkb.pharmcat.haplotype.MatchData;
import org.pharmgkb.pharmcat.haplotype.NamedAlleleMatcher;
import org.pharmgkb.pharmcat.haplotype.model.GeneCall;
import org.pharmgkb.pharmcat.haplotype.model.Variant;
import org.pharmgkb.pharmcat.reporter.model.AstrolabeCall;
import org.pharmgkb.pharmcat.reporter.model.DrugLink;
import org.pharmgkb.pharmcat.reporter.model.MatchLogic;
import org.pharmgkb.pharmcat.reporter.model.PharmcatException;


/**
 * This class is used to help collect Gene-related data for later reporting
 */
public class GeneReport implements Comparable<GeneReport> {
  private static final String UNCALLED = "uncalled";

  private String m_gene;
  private Set<String> m_diplotypes = new TreeSet<>();
  private Set<String> m_haplotypes = new TreeSet<>();
  private Set<String> m_uncalledHaplotypes;
  private SortedSet<Variant> m_variants = new TreeSet<>();
  private List<PharmcatException> m_exceptList = new ArrayList<>();
  private MatchData m_matchData;
  private List<DrugLink> m_relatedDrugs = new ArrayList<>();
  private SortedSet<String> m_functions = new TreeSet<>();
  private Set<String> m_phenotypes = new TreeSet<>();
  private boolean m_astrolabeCall = false;

  /**
   * public constructor
   */
  public GeneReport(@Nonnull String geneSymbol) {
    m_gene = geneSymbol;
    addDip(UNCALLED);
  }

  /**
   * public constructor
   * @param call a {@link GeneCall} that has been made by the {@link NamedAlleleMatcher}
   */
  public void setCallData(@Nonnull GeneCall call, PhenotypeMap phenotypeMap) {
    m_gene = call.getGene();
    if (!call.getDiplotypes().isEmpty()) {
      m_diplotypes.clear();
      call.getDiplotypes().forEach(d -> addDip(d.getName()));
    }
    m_variants.addAll(call.getVariants());
    m_matchData = call.getMatchData();
    m_uncalledHaplotypes = call.getUncallableHaplotypes();
    if (call.getDiplotypes() != null && call.getDiplotypes().size() > 0) {
      call.getDiplotypes().forEach(d -> {
        addFunction(d.getFunction());
        GenePhenotype genePhenotype = phenotypeMap.lookup(m_gene);
        if (genePhenotype != null) {
          addPhenotype(genePhenotype.makePhenotype(d.getName()));
        } else {
          addPhenotype("N/A");
        }
      });
    }
  }

  public void setAstrolabeData(@Nonnull AstrolabeCall call, @Nonnull PhenotypeMap phenotypeMap) {
    m_astrolabeCall = true;
    m_gene = call.getGene();
    if (call.getDiplotypes() != null) {
      m_diplotypes.clear();
      call.getDiplotypes().forEach(d -> {
        addDip(d);
        addFunction(phenotypeMap.lookup(m_gene).makeFunction(d));
        addPhenotype(phenotypeMap.lookup(m_gene).makePhenotype(d));
      });
    }
  }

  /**
   * Diplotypes that have been called for this gene
   */
  public Set<String> getDips(){
    return m_diplotypes;
  }

  private void addDip(String dip) {
    String cleanDip = m_gene.equals("CYP2C19") ? dip.replaceAll("\\*4[AB]", "*4") : dip;

    m_diplotypes.add(cleanDip);

    if (cleanDip.contains("/")) {
      String[] haps = cleanDip.split("/");
      m_haplotypes.add(haps[0]);
      m_haplotypes.add(haps[1]);
    }
  }

  private void addFunction(String function) {
    m_functions.add(function);
  }

  private void addPhenotype(String phenotype) {
    m_phenotypes.add(phenotype);
  }

  /**
   * The gene symbol for this gene
   */
  public String getGene(){
    return m_gene;
  }

  /**
   * The list of exceptions that apply to this gene
   */
  public List<PharmcatException> getExceptionList(){
    return m_exceptList;
  }

  public void applyExceptions(Collection<PharmcatException> exceptions) {
    if (exceptions != null) {
      exceptions.forEach(this::addException);
    }
  }

  public void addException(PharmcatException except) {
    MatchLogic match = except.getMatches();

    if (match.getGene().equals(m_gene)) {
      boolean critHap = match.getHapsCalled().isEmpty() || match.getHapsCalled().stream().allMatch(h -> m_haplotypes.contains(h));
      boolean critUnmatchedHap = match.getHapsMissing().isEmpty() || match.getHapsMissing().stream().allMatch(h -> m_uncalledHaplotypes.contains(h));
      boolean critDip = match.getDips().isEmpty() || match.getDips().stream().allMatch(d -> m_diplotypes.contains(d));
      boolean critMissVariant = match.getVariantsMissing().isEmpty() ||
          match.getVariantsMissing().stream().allMatch(v -> m_variants.isEmpty() || m_variants.stream().noneMatch(a -> a.getRsid().equals(v)));

      if (critHap && critUnmatchedHap && critDip && critMissVariant) {<commit_msg>Add SLCO1B1-specific calling logic.<commit_after>package org.pharmgkb.pharmcat.reporter.model.result;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import javax.annotation.Nonnull;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import org.pharmgkb.pharmcat.UnexpectedStateException;
import org.pharmgkb.pharmcat.definition.PhenotypeMap;
import org.pharmgkb.pharmcat.definition.model.GenePhenotype;
import org.pharmgkb.pharmcat.haplotype.MatchData;
import org.pharmgkb.pharmcat.haplotype.NamedAlleleMatcher;
import org.pharmgkb.pharmcat.haplotype.model.GeneCall;
import org.pharmgkb.pharmcat.haplotype.model.Variant;
import org.pharmgkb.pharmcat.reporter.model.AstrolabeCall;
import org.pharmgkb.pharmcat.reporter.model.DrugLink;
import org.pharmgkb.pharmcat.reporter.model.MatchLogic;
import org.pharmgkb.pharmcat.reporter.model.PharmcatException;


/**
 * This class is used to help collect Gene-related data for later reporting
 */
public class GeneReport implements Comparable<GeneReport> {
  private static final String UNCALLED = "uncalled";

  private String m_gene;
  private Set<String> m_diplotypes = new TreeSet<>();
  private Set<String> m_haplotypes = new TreeSet<>();
  private Set<String> m_uncalledHaplotypes;
  private SortedSet<Variant> m_variants = new TreeSet<>();
  private List<PharmcatException> m_exceptList = new ArrayList<>();
  private MatchData m_matchData;
  private List<DrugLink> m_relatedDrugs = new ArrayList<>();
  private SortedSet<String> m_functions = new TreeSet<>();
  private Set<String> m_phenotypes = new TreeSet<>();
  private boolean m_astrolabeCall = false;

  /**
   * public constructor
   */
  public GeneReport(@Nonnull String geneSymbol) {
    m_gene = geneSymbol;
    addDip(UNCALLED);
  }

  /**
   * public constructor
   * @param call a {@link GeneCall} that has been made by the {@link NamedAlleleMatcher}
   */
  public void setCallData(@Nonnull GeneCall call, PhenotypeMap phenotypeMap) {
    m_gene = call.getGene();
    if (!call.getDiplotypes().isEmpty()) {
      m_diplotypes.clear();
      call.getDiplotypes().forEach(d -> addDip(d.getName()));
    }
    m_variants.addAll(call.getVariants());
    m_matchData = call.getMatchData();

    if (m_gene.equals("SLCO1B1")) {
      callSlco1b1(phenotypeMap.lookup(m_gene));
    }
    else {
      m_uncalledHaplotypes = call.getUncallableHaplotypes();

      if (call.getDiplotypes() != null && call.getDiplotypes().size() > 0) {
        call.getDiplotypes().forEach(d -> {
          addFunction(d.getFunction());
          GenePhenotype genePhenotype = phenotypeMap.lookup(m_gene);
          if (genePhenotype != null) {
            addPhenotype(genePhenotype.makePhenotype(d.getName()));
          } else {
            addPhenotype("N/A");
          }
        });
      }
    }
  }

  public void setAstrolabeData(@Nonnull AstrolabeCall call, @Nonnull PhenotypeMap phenotypeMap) {
    m_astrolabeCall = true;
    m_gene = call.getGene();
    if (call.getDiplotypes() != null) {
      m_diplotypes.clear();
      call.getDiplotypes().forEach(d -> {
        addDip(d);
        addFunction(phenotypeMap.lookup(m_gene).makeFunction(d));
        addPhenotype(phenotypeMap.lookup(m_gene).makePhenotype(d));
      });
    }
  }

  /**
   * Calls the allele and phenotype data specifically for SLCO1B1 which is a special case using only one variant.
   * @param genePhenotype {@link GenePhenotype} information for SLCO1B1
   */
  private void callSlco1b1(GenePhenotype genePhenotype) {
    Variant variant = m_variants.stream().filter(v -> v.getRsid() != null && v.getRsid().equals("rs4149056")).reduce((a,b) -> {
      throw new UnexpectedStateException("more than one variant found");
    }).orElse(null);

    m_diplotypes.clear();
    if (variant != null) {
      String dip = null;
      switch (variant.getVcfCall()) {
        case "T|T":
          dip = "*1A/*1A";
          break;
        case "T|C":
        case "C|T":
          dip = "*1A/*5";
          break;
        case "C|C":
          dip = "*5/*5";
          break;
      }
      addDip(dip);
      addFunction(genePhenotype.makeFunction(dip));
      addPhenotype(genePhenotype.makePhenotype(dip));
    } else {
      addDip(UNCALLED);
    }  }

  /**
   * Diplotypes that have been called for this gene
   */
  public Set<String> getDips(){
    return m_diplotypes;
  }

  private void addDip(String dip) {
    String cleanDip = m_gene.equals("CYP2C19") ? dip.replaceAll("\\*4[AB]", "*4") : dip;

    m_diplotypes.add(cleanDip);

    if (cleanDip.contains("/")) {
      String[] haps = cleanDip.split("/");
      m_haplotypes.add(haps[0]);
      m_haplotypes.add(haps[1]);
    }
  }

  private void addFunction(String function) {
    m_functions.add(function);
  }

  private void addPhenotype(String phenotype) {
    m_phenotypes.add(phenotype);
  }

  /**
   * The gene symbol for this gene
   */
  public String getGene(){
    return m_gene;
  }

  /**
   * The list of exceptions that apply to this gene
   */
  public List<PharmcatException> getExceptionList(){
    return m_exceptList;
  }

  public void applyExceptions(Collection<PharmcatException> exceptions) {
    if (exceptions != null) {
      exceptions.forEach(this::addException);
    }
  }

  private void addException(PharmcatException except) {
    MatchLogic match = except.getMatches();

    if (match.getGene().equals(m_gene)) {
      boolean critHap = match.getHapsCalled().isEmpty() || match.getHapsCalled().stream().allMatch(h -> m_haplotypes.contains(h));
      boolean critUnmatchedHap = match.getHapsMissing().isEmpty() || match.getHapsMissing().stream().allMatch(h -> m_uncalledHaplotypes.contains(h));
      boolean critDip = match.getDips().isEmpty() || match.getDips().stream().allMatch(d -> m_diplotypes.contains(d));
      boolean critMissVariant = match.getVariantsMissing().isEmpty() ||
          match.getVariantsMissing().stream().allMatch(v -> m_variants.isEmpty() || m_variants.stream().noneMatch(a -> a.getRsid().equals(v)));

      if (critHap && critUnmatchedHap && critDip && critMissVariant) {
{'fraction_non_alphanumeric': 0.13071953270200903, 'fraction_numerical': 0.003628639702628551, 'mean_word_length': 3.753891459823307, 'pattern_counts': {'":': 4, '<': 41, '<?xml version=': 0, '>': 61, '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': '2406671', 'n_tokens_mistral': 3840, 'n_tokens_neox': 3644, 'n_words': 687}