All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.innovenso.townplanner.io.concepts.ConceptExcelIO.scala Maven / Gradle / Ivy

package com.innovenso.townplanner.io.concepts

import com.innovenso.townplanner.io.concepts.ExcelFields._
import com.innovenso.townplanner.model._
import com.innovenso.townplanner.model.concepts.properties.{
  API,
  ApiScope,
  ApiStyle,
  ArchitectureVerdict,
  AuthenticationType,
  Criticality,
  DDoSProtection,
  DataClassification,
  Description,
  RateLimiting
}
import com.innovenso.townplanner.model.language.Concept
import com.innovenso.townplanner.model.meta.{Key, SortKey}
import org.apache.poi.ss.usermodel.{Row, Sheet}
import org.apache.poi.xssf.usermodel.XSSFWorkbook

import java.util.UUID

trait ConceptExcelIO[ConceptType <: Concept] extends WorksheetIO {
  def conceptClass: Class[ConceptType]

  def sheetName: String

  def headerLabels: List[String]

  private def allHeaderLabels: List[String] =
    List(KEY) ::: headerLabels ::: List(SORT_KEY)

  def cellValues(concept: ConceptType): List[String]

  private def allCellValues(
      concept: ConceptType
  ): List[String] = List(concept.key.value) ::: cellValues(concept) ::: List(
    concept.sortKey.value.getOrElse("")
  )

  override def worksheet(
      townPlan: TownPlan,
      workbook: XSSFWorkbook
  ): Option[Sheet] = for {
    sheet <- prepareSheet(workbook, sheetName)
    header <- prepareHeader(
      sheet,
      allHeaderLabels
    )
    rows <- conceptRows(sheet, townPlan)
  } yield sheet

  private def conceptRows(sheet: Sheet, townPlan: TownPlan): Option[List[Row]] =
    if (townPlan.components(conceptClass).isEmpty) None
    else
      Some(
        townPlan
          .components(conceptClass)
          .zipWithIndex
          .flatMap(tuple => row(sheet, tuple._2 + 1, tuple._1))
      )

  private def row(
      sheet: Sheet,
      index: Int,
      concept: ConceptType
  ): Option[Row] =
    Option(sheet.getRow(index))
      .orElse(Some(sheet.createRow(index)))
      .flatMap(r => cells(r, concept))

  private def cells(row: Row, concept: ConceptType): Option[Row] = {
    allCellValues(concept).zipWithIndex.foreach(value =>
      cell(row, value._2, value._1)
    )
    Some(row)
  }

  private def inputWorksheet(workbook: XSSFWorkbook): Option[Sheet] =
    Option(workbook.getSheet(sheetName))

  private def concepts(
      worksheet: Option[Sheet],
      enterpriseArchitecture: EnterpriseArchitecture
  ): Unit =
    listOfRows(worksheet).foreach(row =>
      try {
        concept(values(worksheet, Some(row)), enterpriseArchitecture)
      } catch {
        case e: Throwable =>
          println(
            s"Could not import worksheet ${worksheet
                .map(_.getSheetName)
                .getOrElse("-")}, row ${row.getRowNum}: ${e.getMessage}"
          )
      }
    )

  def concept(
      props: ConceptProperties,
      enterpriseArchitecture: EnterpriseArchitecture
  ): Unit

  def values(worksheet: Option[Sheet], row: Option[Row]): ConceptProperties =
    ConceptProperties(
      allHeaderLabels
        .map(hl => (hl, columnIndex(worksheet, hl)))
        .map(tuple =>
          (tuple._1, getCellValue(worksheet, row, tuple._2).getOrElse(""))
        )
        .toMap
    )

  override def read(
      enterpriseArchitecture: EnterpriseArchitecture,
      workbook: XSSFWorkbook
  ): Unit =
    inputWorksheet(workbook)
      .foreach(sheet => concepts(Some(sheet), enterpriseArchitecture))
}

case class ConceptProperties(cellValues: Map[String, String]) {
  def apply(key: String): String = cellValues.getOrElse(key, "")

  def bool(key: String): Boolean = apply(key) match {
    case "yes" | "true" | "1" | "Yes" | "YES" | "True" | "TRUE" => true
    case _                                                      => false
  }

  def option(key: String): Option[String] = Option(apply(key)) match {
    case Some(string) if !string.isBlank => Some(string)
    case _                               => None
  }

  def key: Key = Key.fromString(apply(KEY))
  def sortKey: SortKey = Option(apply(SORT_KEY)) match {
    case Some(string) if !string.isBlank => SortKey(Some(string))
    case _                               => SortKey.next
  }

  def sourceKey: Key = Option(apply(SOURCE_KEY))
    .filter(_.trim.nonEmpty)
    .map(Key.fromString)
    .getOrElse(Key.random)
  def targetKey: Key = Option(apply(TARGET_KEY))
    .filter(_.trim.nonEmpty)
    .map(Key.fromString)
    .getOrElse(Key.random)

  def title: String = apply(TITLE)

  def descriptions: List[Description] =
    apply(DESCRIPTION).split("\n\n").map(Description).toList

  def architectureVerdict: ArchitectureVerdict = ArchitectureVerdict.fromString(
    apply(ARCHITECTURE_VERDICT),
    Option(apply(ARCHITECTURE_VERDICT_EXPLANATION))
  )

  def criticality: Criticality = Criticality.fromString(
    apply(CRITICALITY),
    Option(apply(CRITICALITY_CONSEQUENCES))
  )

  def dataClassification: DataClassification = DataClassification.fromString(
    apply(CLASSIFICATION),
    Option(apply(CLASSIFICATION_DESCRIPTION))
  )

  def api: Option[API] = Option(apply(API_STYLE))
    .filter(_.trim.nonEmpty)
    .map(apiStyle =>
      API(
        authentication = AuthenticationType
          .fromString(apply(API_AUTH_STYLE), option(API_AUTH_DESCRIPTION)),
        style =
          ApiStyle.fromString(apply(API_STYLE), option(API_STYLE_DESCRIPTION)),
        scope =
          ApiScope.fromString(apply(API_SCOPE), option(API_SCOPE_DESCRIPTION)),
        ddoSProtection = DDoSProtection.fromString(
          apply(DDOS_PROTECTION),
          option(DDOS_PROTECTION_DESCRIPTION)
        ),
        rateLimiting = RateLimiting
          .fromString(apply(RATE_LIMITING), option(RATE_LIMITING_DESCRIPTION))
      )
    )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy