<commit_before>package io.digitallibrary.bookapi.service

import com.typesafe.scalalogging.LazyLogging
import io.digitallibrary.bookapi.model._
import io.digitallibrary.bookapi.model.api.{ValidationException, ValidationMessage}
import io.digitallibrary.bookapi.repository._
import scalikejdbc.{AutoSession, DBSession}

import scala.util.{Failure, Success, Try}

trait ImportService {
  this: TransactionHandler
    with LicenseRepository
    with PublisherRepository
    with BookRepository
    with CategoryRepository
    with ValidationService
    with TranslationRepository
    with PersonRepository
    with ContributorRepository
    with ChapterRepository
    with ConverterService
    with InTranslationRepository =>

  val importService: ImportService

  class ImportService extends LazyLogging {

    def importBook(book: api.internal.Book): Try[api.internal.TranslationId] = {
      book.externalId.flatMap(translationRepository.withExternalId) match {
        case None => addBook(book)
        case Some(existingTranslation) => updateBook(book, existingTranslation)
      }
    }

    private def addBook(book: api.internal.Book): Try[api.internal.TranslationId] = {
      inTransaction { implicit session =>
        for {
          license <- validLicense(book)
          publisher <- validPublisher(book)
          persistedPublisher <- persistPublisher(publisher)
          persistedBook <- persistBook(book, license, persistedPublisher)
          persistedTranslation <- persistTranslation(persistedBook, book)
          _ <- persistChapters(book, persistedTranslation)
        } yield api.internal.TranslationId(persistedTranslation.id.get)
      }
    }

    private def updateBook(book: api.internal.Book, existingTranslation: domain.Translation): Try[api.internal.TranslationId] = {
      if (inTranslationRepository.forOriginalId(book.id).nonEmpty) {
        Failure(new RuntimeException(s"Book with id ${book.id} is currently being translated. Cannot update at the moment."))
      } else {
        bookRepository.withId(existingTranslation.bookId) match {
          case None => Failure(new api.NotFoundException(s"The book with id ${existingTranslation.bookId} was not found. Cannot update."))
          case Some(originalBook) => inTransaction { implicit session =>
            for {
              license <- validLicense(book)
              publisher <- validPublisher(book)
              persistedPublisher <- persistPublisher(publisher)
              persistedBook <- persistBookUpdate(originalBook, book, license, persistedPublisher)
              persistedTranslation <- persistTranslationUpdate(persistedBook, existingTranslation, book)
              _ <- persistChapterUpdates(book, persistedTranslation)
            } yield api.internal.TranslationId(persistedTranslation.id.get)

          }
        }
      }
    }

    def importBookAsTranslation(newTranslation: api.internal.Book, bookId: Long): Try[api.internal.TranslationId] = {
      bookRepository.withId(bookId) match {
        case None => Failure(new api.NotFoundException(s"The book with id $bookId was not found. Cannot update."))
        case Some(book) => {
          newTranslation.externalId.flatMap(translationRepository.withExternalId) match {
            case None => addBookAsTranslation(newTranslation, book)
            case Some(existingTranslation) => updateBookAsTranslation(newTranslation, existingTranslation, book)
          }
        }
      }
    }

    def addBookAsTranslation(newTranslation: api.internal.Book, book: domain.Book): Try[api.internal.TranslationId] = {
      inTransaction { implicit session =>
        for {
          persistedTranslation <- persistTranslation(book, newTranslation)
          _ <- persistChapters(newTranslation, persistedTranslation)
        } yield api.internal.TranslationId(persistedTranslation.id.get)
      }
    }

    def updateBookAsTranslation(newTranslation: api.internal.Book, existingTranslation: domain.Translation, book: domain.Book): Try[api.internal.TranslationId] = {
      inTransaction { implicit session =>
        for {
          persistedTranslation <- persistTranslationUpdate(book, existingTranslation, newTranslation)
          _ <- persistChapterUpdates(newTranslation, existingTranslation)
        } yield api.internal.TranslationId(persistedTranslation.id.get)
      }
    }

    private def persistChapters(book: api.internal.Book, translation: domain.Translation)(implicit session: DBSession = AutoSession): Try[Seq[domain.Chapter]] = {
      Try {
        book.chapters.map(chapter => {
          chapterRepository.add(converterService.toDomainChapter(chapter, translation.id.get))
        })
      }
    }

    def persistChapterUpdates(book: api.internal.Book, translation: domain.Translation)(implicit session: DBSession = AutoSession): Try[Seq[domain.Chapter]] = {
      Try {
        book.chapters.map(toUpdate => {
          chapterRepository.forTranslationWithSeqNo(translation.id.get, toUpdate.seqNo) match {
            case Some(chapter) => chapterRepository.updateChapter(converterService.mergeChapter(chapter, toUpdate))
            case None => chapterRepository.add(converterService.toDomainChapter(toUpdate, translation.id.get))
          }
        })
      }
    }

    private def persistTranslation(persistedBook: domain.Book, newBook: api.internal.Book)(implicit session: DBSession = AutoSession): Try[domain.Translation] = {
      for {
        validCategories <- validCategories(newBook)
        domainTranslation <- Success(converterService.toDomainTranslation(newBook, persistedBook, validCategories))
        persistedTranslation <- Try(translationRepository.add(domainTranslation))<commit_msg>#253: Abort if book is in translation GlobalDigitalLibraryio/issues#253<commit_after>package io.digitallibrary.bookapi.service

import com.typesafe.scalalogging.LazyLogging
import io.digitallibrary.bookapi.model._
import io.digitallibrary.bookapi.model.api.{ValidationException, ValidationMessage}
import io.digitallibrary.bookapi.repository._
import scalikejdbc.{AutoSession, DBSession}

import scala.util.{Failure, Success, Try}

trait ImportService {
  this: TransactionHandler
    with LicenseRepository
    with PublisherRepository
    with BookRepository
    with CategoryRepository
    with ValidationService
    with TranslationRepository
    with PersonRepository
    with ContributorRepository
    with ChapterRepository
    with ConverterService
    with InTranslationRepository =>

  val importService: ImportService

  class ImportService extends LazyLogging {

    def importBook(book: api.internal.Book): Try[api.internal.TranslationId] = {
      book.externalId.flatMap(translationRepository.withExternalId) match {
        case None => addBook(book)
        case Some(existingTranslation) => updateBook(book, existingTranslation)
      }
    }

    private def addBook(book: api.internal.Book): Try[api.internal.TranslationId] = {
      inTransaction { implicit session =>
        for {
          license <- validLicense(book)
          publisher <- validPublisher(book)
          persistedPublisher <- persistPublisher(publisher)
          persistedBook <- persistBook(book, license, persistedPublisher)
          persistedTranslation <- persistTranslation(persistedBook, book)
          _ <- persistChapters(book, persistedTranslation)
        } yield api.internal.TranslationId(persistedTranslation.id.get)
      }
    }

    private def updateBook(book: api.internal.Book, existingTranslation: domain.Translation): Try[api.internal.TranslationId] = {
      if (inTranslationRepository.forOriginalId(book.id).nonEmpty) {
        Failure(new RuntimeException(s"Book with id ${book.id} is currently being translated. Cannot update at the moment."))
      } else {
        bookRepository.withId(existingTranslation.bookId) match {
          case None => Failure(new api.NotFoundException(s"The book with id ${existingTranslation.bookId} was not found. Cannot update."))
          case Some(originalBook) => inTransaction { implicit session =>
            for {
              license <- validLicense(book)
              publisher <- validPublisher(book)
              persistedPublisher <- persistPublisher(publisher)
              persistedBook <- persistBookUpdate(originalBook, book, license, persistedPublisher)
              persistedTranslation <- persistTranslationUpdate(persistedBook, existingTranslation, book)
              _ <- persistChapterUpdates(book, persistedTranslation)
            } yield api.internal.TranslationId(persistedTranslation.id.get)

          }
        }
      }
    }

    def importBookAsTranslation(newTranslation: api.internal.Book, bookId: Long): Try[api.internal.TranslationId] = {
      bookRepository.withId(bookId) match {
        case None => Failure(new api.NotFoundException(s"The book with id $bookId was not found. Cannot update."))
        case Some(book) => {
          newTranslation.externalId.flatMap(translationRepository.withExternalId) match {
            case None => addBookAsTranslation(newTranslation, book)
            case Some(existingTranslation) => updateBookAsTranslation(newTranslation, existingTranslation, book)
          }
        }
      }
    }

    def addBookAsTranslation(newTranslation: api.internal.Book, book: domain.Book): Try[api.internal.TranslationId] = {
      inTransaction { implicit session =>
        for {
          persistedTranslation <- persistTranslation(book, newTranslation)
          _ <- persistChapters(newTranslation, persistedTranslation)
        } yield api.internal.TranslationId(persistedTranslation.id.get)
      }
    }

    def updateBookAsTranslation(newTranslation: api.internal.Book, existingTranslation: domain.Translation, book: domain.Book): Try[api.internal.TranslationId] = {
      if(inTranslationRepository.forOriginalId(existingTranslation.bookId).exists(_.fromLanguage == existingTranslation.language)) {
        Failure(new RuntimeException(s"Book with id ${book.id} is currently being translated. Cannot update at the moment."))
      } else {
        inTransaction { implicit session =>
          for {
            persistedTranslation <- persistTranslationUpdate(book, existingTranslation, newTranslation)
            _ <- persistChapterUpdates(newTranslation, existingTranslation)
          } yield api.internal.TranslationId(persistedTranslation.id.get)
        }
      }
    }

    private def persistChapters(book: api.internal.Book, translation: domain.Translation)(implicit session: DBSession = AutoSession): Try[Seq[domain.Chapter]] = {
      Try {
        book.chapters.map(chapter => {
          chapterRepository.add(converterService.toDomainChapter(chapter, translation.id.get))
        })
      }
    }

    def persistChapterUpdates(book: api.internal.Book, translation: domain.Translation)(implicit session: DBSession = AutoSession): Try[Seq[domain.Chapter]] = {
      Try {
        book.chapters.map(toUpdate => {
          chapterRepository.forTranslationWithSeqNo(translation.id.get, toUpdate.seqNo) match {
            case Some(chapter) => chapterRepository.updateChapter(converterService.mergeChapter(chapter, toUpdate))
            case None => chapterRepository.add(converterService.toDomainChapter(toUpdate, translation.id.get))
          }
        })
      }
    }

    private def persistTranslation(persistedBook: domain.Book, newBook: api.internal.Book)(implicit session: DBSession = AutoSession): Try[domain.Translation] = {
      for {
        validCategories <- validCategories(newBook)
        domainTranslation <- Success(converterService.toDomainTranslation(newBook, persistedBook, validCategories))
        persistedTranslation <- Try(translationRepository.add(domainTranslation))
{'fraction_non_alphanumeric': 0.08961702127659575, 'fraction_numerical': 0.0005106382978723404, 'mean_word_length': 3.4731633041492196, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 37, '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': '2654135', 'n_tokens_mistral': 3073, 'n_tokens_neox': 2875, 'n_words': 590}