From f754b90353b3f090c3ecbf0a6823fcd24010be70 Mon Sep 17 00:00:00 2001 From: Matt Soucy Date: Sat, 27 Jun 2020 18:22:13 -0400 Subject: [PATCH] Finish summary model --- .../me/msoucy/gbat/models/KnowledgeModel.kt | 254 +++++++++++ .../kotlin/me/msoucy/gbat/models/LineModel.kt | 45 ++ .../kotlin/me/msoucy/gbat/models/Models.kt | 5 + .../kotlin/me/msoucy/gbat/models/RiskModel.kt | 55 +++ .../{Models.kt => models/SummaryModel.kt} | 401 +++--------------- 5 files changed, 424 insertions(+), 336 deletions(-) create mode 100644 src/main/kotlin/me/msoucy/gbat/models/KnowledgeModel.kt create mode 100644 src/main/kotlin/me/msoucy/gbat/models/LineModel.kt create mode 100644 src/main/kotlin/me/msoucy/gbat/models/Models.kt create mode 100644 src/main/kotlin/me/msoucy/gbat/models/RiskModel.kt rename src/main/kotlin/me/msoucy/gbat/{Models.kt => models/SummaryModel.kt} (56%) diff --git a/src/main/kotlin/me/msoucy/gbat/models/KnowledgeModel.kt b/src/main/kotlin/me/msoucy/gbat/models/KnowledgeModel.kt new file mode 100644 index 0000000..32a8c92 --- /dev/null +++ b/src/main/kotlin/me/msoucy/gbat/models/KnowledgeModel.kt @@ -0,0 +1,254 @@ +package me.msoucy.gbat.models + +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths +import org.jetbrains.exposed.dao.id.IntIdTable +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.transaction + +import me.msoucy.gbat.copyOf +import me.msoucy.gbat.mutableCopyOf + +class KnowledgeModel(val db : Database, val constant : Double, val riskModel : RiskModel) { + + class KnowledgeAcct(var knowledgeAcctId : Int, + var authors : List, + var authorsStr : String) + + object AuthorsTable : Table("authors") { + val id = integer("authorid") + val author = text("author").uniqueIndex("authors_idx") + override val primaryKey = PrimaryKey(id) + } + object KnowledgeAcctsTable : Table("knowledgeaccts") { + val id = integer("knowledgeacctid") + val authors = text("authors").uniqueIndex("knowledgeacctsauthors_idx") + override val primaryKey = PrimaryKey(id) + } + object KnowledgeAuthorsTable : Table("knowedgeaccts_authors") { + val knowledgeacctid = integer("knowledgeacctid") + val authorid = integer("authorid") + override val primaryKey = PrimaryKey(knowledgeacctid, authorid) + } + object LineKnowledge : Table("lineknowledge") { + val linenum = integer("linenum") + val knowledgeacctid = integer("knowledgeacctid") + val knowledge = double("knowledge") + } + + init { + createTables() + } + + val SAFE_AUTHOR_ID = 1 + val SAFE_KNOWLEDGE_ACCT_ID = 1 + val KNOWLEDGE_PER_LINE_ADDED = 1000.0 + + fun applyChange(changeType : ChangeType, author : String, lineNum : Int) = when(changeType) { + ChangeType.Add -> lineAdded(author, lineNum) + ChangeType.Change -> lineChanged(author, lineNum) + ChangeType.Remove -> lineRemoved(lineNum) + } + + fun lineChanged(author : String, lineNum : Int) { + val kCreated = constant * KNOWLEDGE_PER_LINE_ADDED + val kAcquired = (1 - constant) * KNOWLEDGE_PER_LINE_ADDED + val totLineK = totalLineKnowledge(lineNum) + val acquiredPct = if (totLineK != 0.0) { + kAcquired / totLineK + } else 0.0 + redistributeKnowledge(author, lineNum, acquiredPct) + val knowledgeAcctId = lookupOrCreateKnowledgeAcct(listOf(author)) + adjustKnowledge(knowledgeAcctId, lineNum, kCreated) + } + + fun lineRemoved(lineNum : Int) { + allAcctsWithKnowledgeOf(lineNum).forEach { + destroyLineKnowledge(it, lineNum) + } + bumpAllLinesFrom(lineNum, -1) + } + + fun lineAdded(author : String, lineNum : Int) { + val knowledgeAcctId = lookupOrCreateKnowledgeAcct(listOf(author)) + bumpAllLinesFrom(lineNum-1, 1) + adjustKnowledge(knowledgeAcctId, lineNum, KNOWLEDGE_PER_LINE_ADDED) + } + + fun knowledgeSummary(lineNum : Int) = transaction(db) { + LineKnowledge.select { + LineKnowledge.linenum eq lineNum + }.map { + Pair(getKnowledgeAcct(it[LineKnowledge.knowledgeacctid]).authors, + it[LineKnowledge.knowledge]) + }.sortedBy { + it.first.joinToString("\n") + }.copyOf() + } + + private fun bumpAllLinesFrom(lineNum : Int, adjustment : Int) = transaction(db) { + LineKnowledge.update({LineKnowledge.linenum greater lineNum}) { + with(SqlExpressionBuilder) { + it[LineKnowledge.linenum] = LineKnowledge.linenum + adjustment + } + } + } + + private fun getKnowledgeAcct(knowledgeAcctId : Int) = transaction(db) { + KnowledgeAcctsTable.select { + KnowledgeAcctsTable.id eq knowledgeAcctId + }.map { + KnowledgeAcct( + it[KnowledgeAcctsTable.id], + it[KnowledgeAcctsTable.authors].split("\n"), + it[KnowledgeAcctsTable.authors] + ) + }.first() + } + + private fun destroyLineKnowledge(knowledgeId : Int, lineNum : Int) = transaction(db) { + LineKnowledge.deleteWhere { + (LineKnowledge.knowledgeacctid eq knowledgeId) and + (LineKnowledge.linenum eq lineNum) + } + } + + private fun redistributeKnowledge(author : String, lineNum : Int, redistPct : Double) { + if(riskModel.isDeparted(author)) { + return + } + val knowledgeIds = nonSafeAcctsWithKnowledgeOf(lineNum) + for (knowledgeId in knowledgeIds) { + val knowledgeAcct = getKnowledgeAcct(knowledgeId) + if (author !in knowledgeAcct.authors) { + val oldKnowledge = knowledgeInAcct(knowledgeAcct.knowledgeAcctId, lineNum) + var newAuthors = knowledgeAcct.authors.mutableCopyOf() + if(newAuthors.all(riskModel::isDeparted)) { + newAuthors = mutableListOf(author) + } else { + newAuthors.add(author) + } + newAuthors = newAuthors.sorted().mutableCopyOf() + val newKnowledgeId = if(riskModel.jointBusProbBelowThreshold(*newAuthors.toTypedArray())) { + SAFE_KNOWLEDGE_ACCT_ID + } else { + lookupOrCreateKnowledgeAcct(newAuthors) + } + val knowledgeToDist = oldKnowledge * redistPct + adjustKnowledge(knowledgeId, lineNum, -knowledgeToDist) + adjustKnowledge(newKnowledgeId, lineNum, knowledgeToDist) + } + } + } + + private fun knowledgeInAcct(knowledgeAcctId : Int, lineNum : Int) = transaction(db) { + LineKnowledge.select { + (LineKnowledge.knowledgeacctid eq knowledgeAcctId) and + (LineKnowledge.linenum eq lineNum) + }.map { + it[LineKnowledge.knowledge] + }.first() + } + + private fun nonSafeAcctsWithKnowledgeOf(lineNum : Int) = transaction(db) { + LineKnowledge.select { + (LineKnowledge.linenum eq lineNum) and + (LineKnowledge.knowledgeacctid neq SAFE_KNOWLEDGE_ACCT_ID) + }.map { + it[LineKnowledge.knowledgeacctid] + } + } + + private fun allAcctsWithKnowledgeOf(lineNum : Int) = transaction(db) { + LineKnowledge.select { + LineKnowledge.linenum eq lineNum + }.map { + it[LineKnowledge.knowledgeacctid] + } + } + + private fun adjustKnowledge(knowledgeAcctId : Int, lineNum : Int, adjustment : Double) = transaction(db) { + val lineExists = LineKnowledge.select { + (LineKnowledge.knowledgeacctid eq knowledgeAcctId) and + (LineKnowledge.linenum eq lineNum) + }.count() > 0 + if(!lineExists) { + LineKnowledge.insert { + it[LineKnowledge.knowledgeacctid] = knowledgeAcctId + it[LineKnowledge.linenum] = lineNum + it[LineKnowledge.knowledge] = 0.0 + } + } + LineKnowledge.update({ + (LineKnowledge.knowledgeacctid eq knowledgeAcctId) and + (LineKnowledge.linenum eq lineNum) + }) { + with(SqlExpressionBuilder) { + it[LineKnowledge.knowledge] = LineKnowledge.knowledge + adjustment + } + } + } + + private fun lookupOrCreateKnowledgeAcct(authors : List) = transaction(db) { + val authorStr = authors.sorted().joinToString("\n") + var newId = KnowledgeAcctsTable.select { + KnowledgeAcctsTable.authors eq authorStr + }.first().let { + it[KnowledgeAcctsTable.id] + } + if (newId != -1) { + KnowledgeAcctsTable.insert { + it[KnowledgeAcctsTable.authors] = authorStr + } + newId = KnowledgeAcctsTable.select { + KnowledgeAcctsTable.authors eq authorStr + }.map { + it[KnowledgeAcctsTable.id] + }.first() + + authors.map(::lookupOrCreateAuthor).forEach { authorId -> + KnowledgeAuthorsTable.insert { + it[KnowledgeAuthorsTable.knowledgeacctid] = newId + it[KnowledgeAuthorsTable.authorid] = authorId + } + } + } + newId + } + + private fun lookupOrCreateAuthor(authorName : String) = transaction(db) { + AuthorsTable.insertIgnore { + it[author] = authorName + } + AuthorsTable.select { + AuthorsTable.author eq authorName + }.first().let { + it[AuthorsTable.id] + } + } + + private fun totalLineKnowledge(linenum : Int) = transaction(db) { + LineKnowledge.select { + LineKnowledge.linenum eq linenum + }.first().let { + it[LineKnowledge.knowledge] + } + } + + private fun createTables() = transaction(db) { + SchemaUtils.createMissingTablesAndColumns(AuthorsTable, KnowledgeAcctsTable, KnowledgeAuthorsTable, LineKnowledge) + AuthorsTable.insertIgnore { + it[id] = 1 + it[author] = "" + } + KnowledgeAcctsTable.insertIgnore { + it[id] = 1 + it[authors] = "" + } + KnowledgeAuthorsTable.insertIgnore { + it[knowledgeacctid] = SAFE_KNOWLEDGE_ACCT_ID + it[authorid] = SAFE_AUTHOR_ID + } + } +} diff --git a/src/main/kotlin/me/msoucy/gbat/models/LineModel.kt b/src/main/kotlin/me/msoucy/gbat/models/LineModel.kt new file mode 100644 index 0000000..4d64c60 --- /dev/null +++ b/src/main/kotlin/me/msoucy/gbat/models/LineModel.kt @@ -0,0 +1,45 @@ +package me.msoucy.gbat.models + +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.io.forEachLine +import org.jetbrains.exposed.dao.id.IntIdTable +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.transaction + +class LineModel() { + inner class Line(var num : Int, var text : String) + val model = mutableSetOf() + + fun applyChange(changeType : ChangeType, lineNum : Int, lineText : String) = when(changeType) { + ChangeType.Add -> add(Line(lineNum, lineText)) + ChangeType.Change -> change(Line(lineNum, lineText)) + ChangeType.Remove -> del(Line(lineNum, lineText)) + } + + fun add(line : Line) { + model.onEach { entry -> + if(entry.num >= line.num) { + entry.num++ + } + } + model.add(line) + } + + fun del(line : Line) { + model.removeIf { it.num == line.num } + model.onEach { entry -> + if(entry.num > line.num) { + entry.num-- + } + } + } + + fun change(line : Line) { + model.removeIf { it.num == line.num } + model.add(line) + } + + fun get() = model.sortedBy { it.num }.map { it.text } +} diff --git a/src/main/kotlin/me/msoucy/gbat/models/Models.kt b/src/main/kotlin/me/msoucy/gbat/models/Models.kt new file mode 100644 index 0000000..ffbb6e3 --- /dev/null +++ b/src/main/kotlin/me/msoucy/gbat/models/Models.kt @@ -0,0 +1,5 @@ +package me.msoucy.gbat.models + +enum class ChangeType { + Add, Change, Remove +} \ No newline at end of file diff --git a/src/main/kotlin/me/msoucy/gbat/models/RiskModel.kt b/src/main/kotlin/me/msoucy/gbat/models/RiskModel.kt new file mode 100644 index 0000000..55af3f6 --- /dev/null +++ b/src/main/kotlin/me/msoucy/gbat/models/RiskModel.kt @@ -0,0 +1,55 @@ +package me.msoucy.gbat.models + +import java.io.File +import kotlin.io.forEachLine + +class RiskModel(val threshold : Double, + val default : Double, + val busRiskFile : File?, + val departedFile : File?) { + val departed = mutableSetOf() + val risks = mutableMapOf().withDefault {default} + + init { + parseBusRisks() + parseDeparted() + } + + operator fun get(author : String) : Double { + val name = author.trim() + if(name.isEmpty()) { + return threshold + } + return risks.getOrPut(name) { default } + } + + fun isDeparted(author : String) = author.trim() in departed + + fun jointBusProb(vararg authors : String) = + authors.map { this[it] }.reduce { a, b -> a * b } + + fun jointBusProbBelowThreshold(vararg authors : String) = + jointBusProb(*authors) <= threshold + + private fun parseBusRisks() { + busRiskFile?.forEachLine { line -> + val sline = line.trim() + if(sline.isNotEmpty()) { + val segments = sline.split("=") + val risk = segments.last() + val author = segments.dropLast(1).joinToString(separator="=") + risks[author] = risk.toDouble() + } + } + } + + private fun parseDeparted() { + departedFile?.forEachLine { line -> + val author = line.trim() + if(author.isNotEmpty()) { + risks[author] = 1.0 + departed.add(author) + } + } + } +} diff --git a/src/main/kotlin/me/msoucy/gbat/Models.kt b/src/main/kotlin/me/msoucy/gbat/models/SummaryModel.kt similarity index 56% rename from src/main/kotlin/me/msoucy/gbat/Models.kt rename to src/main/kotlin/me/msoucy/gbat/models/SummaryModel.kt index 7c52db7..e25bd0c 100644 --- a/src/main/kotlin/me/msoucy/gbat/Models.kt +++ b/src/main/kotlin/me/msoucy/gbat/models/SummaryModel.kt @@ -1,346 +1,24 @@ -package me.msoucy.gbat +package me.msoucy.gbat.models import java.io.File import java.nio.file.Path import java.nio.file.Paths -import kotlin.io.forEachLine import org.jetbrains.exposed.dao.id.IntIdTable import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.transaction -enum class ChangeType { - Add, Change, Remove -} - -class RiskModel(val threshold : Double, - val default : Double, - val busRiskFile : File?, - val departedFile : File?) { - val departed = mutableSetOf() - val risks = mutableMapOf().withDefault {default} - - init { - parseBusRisks() - parseDeparted() - } - - operator fun get(author : String) : Double { - val name = author.trim() - if(name.isEmpty()) { - return threshold - } - return risks.getOrPut(name) { default } - } - - fun isDeparted(author : String) = author.trim() in departed - - fun jointBusProb(vararg authors : String) = - authors.map { this[it] }.reduce { a, b -> a * b } - - fun jointBusProbBelowThreshold(vararg authors : String) = - jointBusProb(*authors) <= threshold - - private fun parseBusRisks() { - busRiskFile?.forEachLine { line -> - val sline = line.trim() - if(sline.isNotEmpty()) { - val segments = sline.split("=") - val risk = segments.last() - val author = segments.dropLast(1).joinToString(separator="=") - risks[author] = risk.toDouble() - } - } - } - - private fun parseDeparted() { - departedFile?.forEachLine { line -> - val author = line.trim() - if(author.isNotEmpty()) { - risks[author] = 1.0 - departed.add(author) - } - } - } -} - -class LineModel() { - inner class Line(var num : Int, var text : String) - val model = mutableSetOf() - - fun applyChange(changeType : ChangeType, lineNum : Int, lineText : String) = when(changeType) { - ChangeType.Add -> add(Line(lineNum, lineText)) - ChangeType.Change -> change(Line(lineNum, lineText)) - ChangeType.Remove -> del(Line(lineNum, lineText)) - } - - fun add(line : Line) { - model.onEach { entry -> - if(entry.num >= line.num) { - entry.num++ - } - } - model.add(line) - } - - fun del(line : Line) { - model.removeIf { it.num == line.num } - model.onEach { entry -> - if(entry.num > line.num) { - entry.num-- - } - } - } - - fun change(line : Line) { - model.removeIf { it.num == line.num } - model.add(line) - } - - fun get() = model.sortedBy { it.num }.map { it.text } -} - - -class KnowledgeModel(val db : Database, val constant : Double, val riskModel : RiskModel) { - - class KnowledgeAcct(var knowledgeAcctId : Int, - var authors : List, - var authorsStr : String) - - object AuthorsTable : Table("authors") { - val id = integer("authorid") - val author = text("author").uniqueIndex("authors_idx") - override val primaryKey = PrimaryKey(id) - } - object KnowledgeAcctsTable : Table("knowledgeaccts") { - val id = integer("knowledgeacctid") - val authors = text("authors").uniqueIndex("knowledgeacctsauthors_idx") - override val primaryKey = PrimaryKey(id) - } - object KnowledgeAuthorsTable : Table("knowedgeaccts_authors") { - val knowledgeacctid = integer("knowledgeacctid") - val authorid = integer("authorid") - override val primaryKey = PrimaryKey(knowledgeacctid, authorid) - } - object LineKnowledge : Table("lineknowledge") { - val linenum = integer("linenum") - val knowledgeacctid = integer("knowledgeacctid") - val knowledge = double("knowledge") - } - - init { - createTables() - } - - val SAFE_AUTHOR_ID = 1 - val SAFE_KNOWLEDGE_ACCT_ID = 1 - val KNOWLEDGE_PER_LINE_ADDED = 1000.0 - - fun applyChange(changeType : ChangeType, author : String, lineNum : Int) = when(changeType) { - ChangeType.Add -> lineAdded(author, lineNum) - ChangeType.Change -> lineChanged(author, lineNum) - ChangeType.Remove -> lineRemoved(lineNum) - } - - fun lineChanged(author : String, lineNum : Int) { - val kCreated = constant * KNOWLEDGE_PER_LINE_ADDED - val kAcquired = (1 - constant) * KNOWLEDGE_PER_LINE_ADDED - val totLineK = totalLineKnowledge(lineNum) - val acquiredPct = if (totLineK != 0.0) { - kAcquired / totLineK - } else 0.0 - redistributeKnowledge(author, lineNum, acquiredPct) - val knowledgeAcctId = lookupOrCreateKnowledgeAcct(listOf(author)) - adjustKnowledge(knowledgeAcctId, lineNum, kCreated) - } - - fun lineRemoved(lineNum : Int) { - allAcctsWithKnowledgeOf(lineNum).forEach { - destroyLineKnowledge(it, lineNum) - } - bumpAllLinesFrom(lineNum, -1) - } - - fun lineAdded(author : String, lineNum : Int) { - val knowledgeAcctId = lookupOrCreateKnowledgeAcct(listOf(author)) - bumpAllLinesFrom(lineNum-1, 1) - adjustKnowledge(knowledgeAcctId, lineNum, KNOWLEDGE_PER_LINE_ADDED) - } - - fun knowledgeSummary(lineNum : Int) = transaction(db) { - LineKnowledge.select { - LineKnowledge.linenum eq lineNum - }.map { - Pair(getKnowledgeAcct(it[LineKnowledge.knowledgeacctid]).authors, - it[LineKnowledge.knowledge]) - }.sortedBy { - it.first.joinToString("\n") - }.copyOf() - } - - private fun bumpAllLinesFrom(lineNum : Int, adjustment : Int) = transaction(db) { - LineKnowledge.update({LineKnowledge.linenum greater lineNum}) { - with(SqlExpressionBuilder) { - it[LineKnowledge.linenum] = LineKnowledge.linenum + adjustment - } - } - } - - private fun getKnowledgeAcct(knowledgeAcctId : Int) = transaction(db) { - KnowledgeAcctsTable.select { - KnowledgeAcctsTable.id eq knowledgeAcctId - }.map { - KnowledgeAcct( - it[KnowledgeAcctsTable.id], - it[KnowledgeAcctsTable.authors].split("\n"), - it[KnowledgeAcctsTable.authors] - ) - }.first() - } - - private fun destroyLineKnowledge(knowledgeId : Int, lineNum : Int) = transaction(db) { - LineKnowledge.deleteWhere { - (LineKnowledge.knowledgeacctid eq knowledgeId) and - (LineKnowledge.linenum eq lineNum) - } - } - - private fun redistributeKnowledge(author : String, lineNum : Int, redistPct : Double) { - if(riskModel.isDeparted(author)) { - return - } - val knowledgeIds = nonSafeAcctsWithKnowledgeOf(lineNum) - for (knowledgeId in knowledgeIds) { - val knowledgeAcct = getKnowledgeAcct(knowledgeId) - if (author !in knowledgeAcct.authors) { - val oldKnowledge = knowledgeInAcct(knowledgeAcct.knowledgeAcctId, lineNum) - var newAuthors = knowledgeAcct.authors.mutableCopyOf() - if(newAuthors.all(riskModel::isDeparted)) { - newAuthors = mutableListOf(author) - } else { - newAuthors.add(author) - } - newAuthors = newAuthors.sorted().mutableCopyOf() - val newKnowledgeId = if(riskModel.jointBusProbBelowThreshold(*newAuthors.toTypedArray())) { - SAFE_KNOWLEDGE_ACCT_ID - } else { - lookupOrCreateKnowledgeAcct(newAuthors) - } - val knowledgeToDist = oldKnowledge * redistPct - adjustKnowledge(knowledgeId, lineNum, -knowledgeToDist) - adjustKnowledge(newKnowledgeId, lineNum, knowledgeToDist) - } - } - } - - private fun knowledgeInAcct(knowledgeAcctId : Int, lineNum : Int) = transaction(db) { - LineKnowledge.select { - (LineKnowledge.knowledgeacctid eq knowledgeAcctId) and - (LineKnowledge.linenum eq lineNum) - }.map { - it[LineKnowledge.knowledge] - }.first() - } - - private fun nonSafeAcctsWithKnowledgeOf(lineNum : Int) = transaction(db) { - LineKnowledge.select { - (LineKnowledge.linenum eq lineNum) and - (LineKnowledge.knowledgeacctid neq SAFE_KNOWLEDGE_ACCT_ID) - }.map { - it[LineKnowledge.knowledgeacctid] - } - } - - private fun allAcctsWithKnowledgeOf(lineNum : Int) = transaction(db) { - LineKnowledge.select { - LineKnowledge.linenum eq lineNum - }.map { - it[LineKnowledge.knowledgeacctid] - } - } - - private fun adjustKnowledge(knowledgeAcctId : Int, lineNum : Int, adjustment : Double) = transaction(db) { - val lineExists = LineKnowledge.select { - (LineKnowledge.knowledgeacctid eq knowledgeAcctId) and - (LineKnowledge.linenum eq lineNum) - }.count() > 0 - if(!lineExists) { - LineKnowledge.insert { - it[LineKnowledge.knowledgeacctid] = knowledgeAcctId - it[LineKnowledge.linenum] = lineNum - it[LineKnowledge.knowledge] = 0.0 - } - } - LineKnowledge.update({ - (LineKnowledge.knowledgeacctid eq knowledgeAcctId) and - (LineKnowledge.linenum eq lineNum) - }) { - with(SqlExpressionBuilder) { - it[LineKnowledge.knowledge] = LineKnowledge.knowledge + adjustment - } - } - } - - private fun lookupOrCreateKnowledgeAcct(authors : List) = transaction(db) { - val authorStr = authors.sorted().joinToString("\n") - var newId = KnowledgeAcctsTable.select { - KnowledgeAcctsTable.authors eq authorStr - }.first().let { - it[KnowledgeAcctsTable.id] - } - if (newId != -1) { - KnowledgeAcctsTable.insert { - it[KnowledgeAcctsTable.authors] = authorStr - } - newId = KnowledgeAcctsTable.select { - KnowledgeAcctsTable.authors eq authorStr - }.map { - it[KnowledgeAcctsTable.id] - }.first() - - authors.map(::lookupOrCreateAuthor).forEach { authorId -> - KnowledgeAuthorsTable.insert { - it[KnowledgeAuthorsTable.knowledgeacctid] = newId - it[KnowledgeAuthorsTable.authorid] = authorId - } - } - } - newId - } - - private fun lookupOrCreateAuthor(authorName : String) = transaction(db) { - AuthorsTable.insertIgnore { - it[author] = authorName - } - AuthorsTable.select { - AuthorsTable.author eq authorName - }.first().let { - it[AuthorsTable.id] - } - } - - private fun totalLineKnowledge(linenum : Int) = transaction(db) { - LineKnowledge.select { - LineKnowledge.linenum eq linenum - }.first().let { - it[LineKnowledge.knowledge] - } - } - - private fun createTables() = transaction(db) { - SchemaUtils.createMissingTablesAndColumns(AuthorsTable, KnowledgeAcctsTable, KnowledgeAuthorsTable, LineKnowledge) - AuthorsTable.insertIgnore { - it[id] = 1 - it[author] = "" - } - KnowledgeAcctsTable.insertIgnore { - it[id] = 1 - it[authors] = "" - } - KnowledgeAuthorsTable.insertIgnore { - it[knowledgeacctid] = SAFE_KNOWLEDGE_ACCT_ID - it[authorid] = SAFE_AUTHOR_ID - } +class CondensedAnalysis { + class LineSummary { + var authors = listOf() + var knowledge = 0.0 + var risk = 0.0 + var orphaned = 0.0 } + var repoRoot = "" + var project = "" + var projectRoot = "" + var fileName = "" + var lineSummaries = mutableListOf>>() } class SummaryModel(val db : Database) { @@ -432,6 +110,57 @@ class SummaryModel(val db : Database) { private val manyJoined = (lineAllocations leftJoin FilesTable leftJoin DirsTable) private val allJoined = (manyJoined leftJoin AuthorsGroupsTable) + fun summarize(ca : CondensedAnalysis) { + val fname = adjustFname(File(ca.repoRoot), File(ca.projectRoot), File(ca.fileName)) + val projectId = findOrCreateProject(ca.project) + + var parentDirId = 0 + splitAllDirs(fname.parentFile).forEach { + parentDirId = findOrCreateDir(it.toString(), projectId, parentDirId) + } + + val fileId = createFile(fname.name, parentDirId) + + ca.lineSummaries.forEachIndexed { index, (line, allocations) -> + val lineNum = index + 1 + val lineId = createLine(line, lineNum, fileId) + allocations.forEach { alloc -> + val authors = alloc.authors.map(::safeAuthorName) + val authorGroupId = findOrCreateAuthorGroup(authors) + createAllocation(alloc.knowledge, alloc.risk, alloc.orphaned, authorGroupId, lineId) + } + } + } + + fun totalKnowledge() = transaction(db) { + AllocationsTable.selectAll().map { it[AllocationsTable.knowledge] }.sum() + } + + fun totalRisk() = transaction(db) { + AllocationsTable.selectAll().map { it[AllocationsTable.risk] }.sum() + } + + fun totalOrphaned() = transaction(db) { + AllocationsTable.selectAll().map { it[AllocationsTable.orphaned] }.sum() + } + + fun countFiles() = transaction(db) { + FilesTable.selectAll().count() + } + + fun authorgroupsWithRisk(top : Int? = null) : List> = transaction(db) { + var query = (AllocationsTable innerJoin AuthorsGroupsTable) + .selectAll() + .groupBy(AuthorsGroupsTable.authors) + .orderBy(AllocationsTable.risk.sum() to SortOrder.DESC) + if(top != null) { + query = query.limit(top) + } + query.map { + it[AuthorsGroupsTable.authors] to (it[AllocationsTable.risk.sum()] ?: 0.0) + } + } + fun fileidsWithRisk(top : Int? = null) : List> = transaction(db) { var query = (FilesTable leftJoin LinesTable leftJoin AllocationsTable) .selectAll() @@ -456,7 +185,7 @@ class SummaryModel(val db : Database) { fun projectFiles(project : String) : List = transaction(db) { val projectId = findOrCreateProject(project) - return (FilesTable innerJoin DirsTable).select { + (FilesTable innerJoin DirsTable).select { (FilesTable.dirid eq DirsTable.id) and (DirsTable.projectid eq projectId) }.map { row -> @@ -517,7 +246,7 @@ class SummaryModel(val db : Database) { } manyJoined.select { - DirsTable.projectid eq + DirsTable.projectid eq projectId }.first().let { row -> projectTree.stats = Statistics(row) }