<commit_before>package org.col.assembly;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import com.google.common.base.Preconditions;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.col.api.model.ColUser;
import org.col.api.model.EditorialDecision;
import org.col.api.model.Name;
import org.col.api.model.Page;
import org.col.api.model.Sector;
import org.col.api.model.SectorImport;
import org.col.api.model.Taxon;
import org.col.api.util.ObjectUtils;
import org.col.api.vocab.Datasets;
import org.col.common.util.LoggingUtils;
import org.col.db.mapper.DecisionMapper;
import org.col.db.mapper.SectorImportMapper;
import org.col.db.mapper.SectorMapper;
import org.col.db.mapper.TaxonMapper;
import org.col.es.name.index.NameUsageIndexService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

abstract class SectorRunnable implements Runnable {
  private static final Logger LOG = LoggerFactory.getLogger(SectorRunnable.class);

  final int catalogueKey = Datasets.DRAFT_COL;
  protected final int datasetKey;
  protected final int sectorKey;
  protected Sector sector;
  final boolean validateSector;
  final SqlSessionFactory factory;
  final NameUsageIndexService indexService;
  // maps keyed on taxon ids from this sector
  final Map<String, EditorialDecision> decisions = new HashMap<>();
  List<Sector> childSectors;
  List<Taxon> foreignChildren;
  // map with foreign child id to original parent name
  Map<String, Name> foreignChildrenParents = new HashMap<>();
  private final Consumer<SectorRunnable> successCallback;
  private final BiConsumer<SectorRunnable, Exception> errorCallback;
  private final LocalDateTime created = LocalDateTime.now();
  final ColUser user;
  final SectorImport state = new SectorImport();
  
  SectorRunnable(int sectorKey, boolean validateSector, SqlSessionFactory factory, NameUsageIndexService indexService,
                      Consumer<SectorRunnable> successCallback,
                      BiConsumer<SectorRunnable, Exception> errorCallback, ColUser user) throws IllegalArgumentException {
    this.user = Preconditions.checkNotNull(user);
    this.validateSector = validateSector;
    this.factory = factory;
    this.sectorKey = sectorKey;
    try (SqlSession session = factory.openSession(true)) {
      // check for existance and datasetKey - we will load the real thing for processing only when we get executed!
      sector = loadSector(false);
      this.datasetKey = sector.getSubjectDatasetKey();
      // lookup next attempt
      List<SectorImport> imports = session.getMapper(SectorImportMapper.class).list(sectorKey, null, null,null, new Page(0,1));
      state.setAttempt(imports == null || imports.isEmpty() ? 1 : imports.get(0).getAttempt() + 1);
      state.setSectorKey(sectorKey);
      state.setDatasetKey(datasetKey);
      state.setType(getClass().getSimpleName());
      state.setState(SectorImport.State.WAITING);
    }
    this.indexService = indexService;
    this.successCallback = successCallback;
    this.errorCallback = errorCallback;
  }
  
  @Override
  public void run() {
    LoggingUtils.setSectorMDC(sectorKey, state.getAttempt(), getClass());
    try {
      state.setStarted(LocalDateTime.now());
      init();
      doWork();
      successCallback.accept(this);
      
    } catch (InterruptedException e) {
      LOG.warn("Interrupted {}", this, e);
      errorCallback.accept(this, e);
      state.setState(SectorImport.State.CANCELED);
      
    } catch (Exception e) {
      LOG.error("Error running {}", this, e);
      state.setError(ExceptionUtils.getRootCauseMessage(e));
      errorCallback.accept(this, e);
      state.setState(SectorImport.State.FAILED);
      
    } finally {
      LOG.info("Completed {}", this);
      state.setFinished(LocalDateTime.now());
      finalWork();
      LoggingUtils.removeSectorMDC();
    }
  }
  
  abstract void finalWork();
  
  void init() throws Exception {
    state.setState( SectorImport.State.PREPARING);
    // load latest version of the sector again to get the latest target ids
    sector = loadSector(validateSector);
    loadDecisions();
    loadForeignChildren();
    loadAttachedSectors();
    checkIfCancelled();
  }
  
  private Sector loadSector(boolean validate) {
    try (SqlSession session = factory.openSession(true)) {
      SectorMapper sm = session.getMapper(SectorMapper.class);
      Sector s = sm.get(sectorKey);
      if (s == null) {
        throw new IllegalArgumentException("Sector "+sectorKey+" does not exist");
      }
      
      if (validate) {
        // assert that target actually exists. Subject might be bad - not needed for deletes!
        TaxonMapper tm = session.getMapper(TaxonMapper.class);
        
        // check if target actually exists
        String msg = "Sector " + s.getKey() + " does have a non existing target " + s.getTarget() + " for dataset " + catalogueKey;
        try {
          ObjectUtils.checkNotNull(s.getTarget(), s + " does not have any target");
          ObjectUtils.checkNotNull(tm.get(s.getTargetAsDSID()), "Sector " + s.getKey() + " does have a non existing target id");
        } catch (PersistenceException e) {
          throw new IllegalArgumentException(msg, e);
        }
  
        // also validate the subject for syncs
        msg = "Sector " + s.getKey() + " does have a non existing subject " + s.getSubject() + " for dataset " + datasetKey;
        try {<commit_msg>Throw if sector merge is requested<commit_after>package org.col.assembly;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

import com.google.common.base.Preconditions;

import org.apache.commons.lang3.NotImplementedException;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.col.api.model.ColUser;
import org.col.api.model.EditorialDecision;
import org.col.api.model.Name;
import org.col.api.model.Page;
import org.col.api.model.Sector;
import org.col.api.model.SectorImport;
import org.col.api.model.Taxon;
import org.col.api.util.ObjectUtils;
import org.col.api.vocab.Datasets;
import org.col.common.util.LoggingUtils;
import org.col.db.mapper.DecisionMapper;
import org.col.db.mapper.SectorImportMapper;
import org.col.db.mapper.SectorMapper;
import org.col.db.mapper.TaxonMapper;
import org.col.es.name.index.NameUsageIndexService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

abstract class SectorRunnable implements Runnable {
  private static final Logger LOG = LoggerFactory.getLogger(SectorRunnable.class);

  final int catalogueKey = Datasets.DRAFT_COL;
  protected final int datasetKey;
  protected final int sectorKey;
  protected Sector sector;
  final boolean validateSector;
  final SqlSessionFactory factory;
  final NameUsageIndexService indexService;
  // maps keyed on taxon ids from this sector
  final Map<String, EditorialDecision> decisions = new HashMap<>();
  List<Sector> childSectors;
  List<Taxon> foreignChildren;
  // map with foreign child id to original parent name
  Map<String, Name> foreignChildrenParents = new HashMap<>();
  private final Consumer<SectorRunnable> successCallback;
  private final BiConsumer<SectorRunnable, Exception> errorCallback;
  private final LocalDateTime created = LocalDateTime.now();
  final ColUser user;
  final SectorImport state = new SectorImport();
  
  SectorRunnable(int sectorKey, boolean validateSector, SqlSessionFactory factory, NameUsageIndexService indexService,
                      Consumer<SectorRunnable> successCallback,
                      BiConsumer<SectorRunnable, Exception> errorCallback, ColUser user) throws IllegalArgumentException {
    this.user = Preconditions.checkNotNull(user);
    this.validateSector = validateSector;
    this.factory = factory;
    this.sectorKey = sectorKey;
    try (SqlSession session = factory.openSession(true)) {
      // check for existance and datasetKey - we will load the real thing for processing only when we get executed!
      sector = loadSector(false);
      this.datasetKey = sector.getSubjectDatasetKey();
      // lookup next attempt
      List<SectorImport> imports = session.getMapper(SectorImportMapper.class).list(sectorKey, null, null,null, new Page(0,1));
      state.setAttempt(imports == null || imports.isEmpty() ? 1 : imports.get(0).getAttempt() + 1);
      state.setSectorKey(sectorKey);
      state.setDatasetKey(datasetKey);
      state.setType(getClass().getSimpleName());
      state.setState(SectorImport.State.WAITING);
    }
    this.indexService = indexService;
    this.successCallback = successCallback;
    this.errorCallback = errorCallback;
  }
  
  @Override
  public void run() {
    LoggingUtils.setSectorMDC(sectorKey, state.getAttempt(), getClass());
    try {
      state.setStarted(LocalDateTime.now());
      init();
      doWork();
      successCallback.accept(this);
      
    } catch (InterruptedException e) {
      LOG.warn("Interrupted {}", this, e);
      errorCallback.accept(this, e);
      state.setState(SectorImport.State.CANCELED);
      
    } catch (Exception e) {
      LOG.error("Error running {}", this, e);
      state.setError(ExceptionUtils.getRootCauseMessage(e));
      errorCallback.accept(this, e);
      state.setState(SectorImport.State.FAILED);
      
    } finally {
      LOG.info("Completed {}", this);
      state.setFinished(LocalDateTime.now());
      finalWork();
      LoggingUtils.removeSectorMDC();
    }
  }
  
  abstract void finalWork();
  
  void init() throws Exception {
    state.setState( SectorImport.State.PREPARING);
    // load latest version of the sector again to get the latest target ids
    sector = loadSector(validateSector);
    if (sector.getMode() == Sector.Mode.MERGE) {
      //TODO: https://github.com/Sp2000/colplus-backend/issues/509
      throw new NotImplementedException("Sector merging not implemented yet");
    }
    loadDecisions();
    loadForeignChildren();
    loadAttachedSectors();
    checkIfCancelled();
  }
  
  private Sector loadSector(boolean validate) {
    try (SqlSession session = factory.openSession(true)) {
      SectorMapper sm = session.getMapper(SectorMapper.class);
      Sector s = sm.get(sectorKey);
      if (s == null) {
        throw new IllegalArgumentException("Sector "+sectorKey+" does not exist");
      }
      
      if (validate) {
        // assert that target actually exists. Subject might be bad - not needed for deletes!
        TaxonMapper tm = session.getMapper(TaxonMapper.class);
        
        // check if target actually exists
        String msg = "Sector " + s.getKey() + " does have a non existing target " + s.getTarget() + " for dataset " + catalogueKey;
        try {
          ObjectUtils.checkNotNull(s.getTarget(), s + " does not have any target");
          ObjectUtils.checkNotNull(tm.get(s.getTargetAsDSID()), "Sector " + s.getKey() + " does have a non existing target id");
        } catch (PersistenceException e) {
          throw new IllegalArgumentException(msg, e);
        }
  
        // also validate the subject for syncs
        msg = "Sector " + s.getKey() + " does have a non existing subject " + s.getSubject() + " for dataset " + datasetKey;
        try {
{'fraction_non_alphanumeric': 0.10551046561298591, 'fraction_numerical': 0.0020504058094831267, 'mean_word_length': 3.641554321966693, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 1, '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': '2402540', 'n_tokens_mistral': 3401, 'n_tokens_neox': 3373, 'n_words': 811}