Finish summary model
This commit is contained in:
parent
da895cbc91
commit
f754b90353
254
src/main/kotlin/me/msoucy/gbat/models/KnowledgeModel.kt
Normal file
254
src/main/kotlin/me/msoucy/gbat/models/KnowledgeModel.kt
Normal file
@ -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<String>,
|
||||||
|
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<String>) = 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
src/main/kotlin/me/msoucy/gbat/models/LineModel.kt
Normal file
45
src/main/kotlin/me/msoucy/gbat/models/LineModel.kt
Normal file
@ -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<Line>()
|
||||||
|
|
||||||
|
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 }
|
||||||
|
}
|
5
src/main/kotlin/me/msoucy/gbat/models/Models.kt
Normal file
5
src/main/kotlin/me/msoucy/gbat/models/Models.kt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package me.msoucy.gbat.models
|
||||||
|
|
||||||
|
enum class ChangeType {
|
||||||
|
Add, Change, Remove
|
||||||
|
}
|
55
src/main/kotlin/me/msoucy/gbat/models/RiskModel.kt
Normal file
55
src/main/kotlin/me/msoucy/gbat/models/RiskModel.kt
Normal file
@ -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<String>()
|
||||||
|
val risks = mutableMapOf<String, Double>().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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,346 +1,24 @@
|
|||||||
package me.msoucy.gbat
|
package me.msoucy.gbat.models
|
||||||
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import kotlin.io.forEachLine
|
|
||||||
import org.jetbrains.exposed.dao.id.IntIdTable
|
import org.jetbrains.exposed.dao.id.IntIdTable
|
||||||
import org.jetbrains.exposed.sql.*
|
import org.jetbrains.exposed.sql.*
|
||||||
import org.jetbrains.exposed.sql.transactions.transaction
|
import org.jetbrains.exposed.sql.transactions.transaction
|
||||||
|
|
||||||
enum class ChangeType {
|
class CondensedAnalysis {
|
||||||
Add, Change, Remove
|
class LineSummary {
|
||||||
}
|
var authors = listOf<String>()
|
||||||
|
var knowledge = 0.0
|
||||||
class RiskModel(val threshold : Double,
|
var risk = 0.0
|
||||||
val default : Double,
|
var orphaned = 0.0
|
||||||
val busRiskFile : File?,
|
}
|
||||||
val departedFile : File?) {
|
var repoRoot = ""
|
||||||
val departed = mutableSetOf<String>()
|
var project = ""
|
||||||
val risks = mutableMapOf<String, Double>().withDefault {default}
|
var projectRoot = ""
|
||||||
|
var fileName = ""
|
||||||
init {
|
var lineSummaries = mutableListOf<Pair<String, List<LineSummary>>>()
|
||||||
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<Line>()
|
|
||||||
|
|
||||||
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<String>,
|
|
||||||
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<String>) = 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 SummaryModel(val db : Database) {
|
class SummaryModel(val db : Database) {
|
||||||
@ -432,6 +110,57 @@ class SummaryModel(val db : Database) {
|
|||||||
private val manyJoined = (lineAllocations leftJoin FilesTable leftJoin DirsTable)
|
private val manyJoined = (lineAllocations leftJoin FilesTable leftJoin DirsTable)
|
||||||
private val allJoined = (manyJoined leftJoin AuthorsGroupsTable)
|
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<Pair<String, Double>> = 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<Pair<Int, Double>> = transaction(db) {
|
fun fileidsWithRisk(top : Int? = null) : List<Pair<Int, Double>> = transaction(db) {
|
||||||
var query = (FilesTable leftJoin LinesTable leftJoin AllocationsTable)
|
var query = (FilesTable leftJoin LinesTable leftJoin AllocationsTable)
|
||||||
.selectAll()
|
.selectAll()
|
||||||
@ -456,7 +185,7 @@ class SummaryModel(val db : Database) {
|
|||||||
|
|
||||||
fun projectFiles(project : String) : List<ProjectFilesResult> = transaction(db) {
|
fun projectFiles(project : String) : List<ProjectFilesResult> = transaction(db) {
|
||||||
val projectId = findOrCreateProject(project)
|
val projectId = findOrCreateProject(project)
|
||||||
return (FilesTable innerJoin DirsTable).select {
|
(FilesTable innerJoin DirsTable).select {
|
||||||
(FilesTable.dirid eq DirsTable.id) and
|
(FilesTable.dirid eq DirsTable.id) and
|
||||||
(DirsTable.projectid eq projectId)
|
(DirsTable.projectid eq projectId)
|
||||||
}.map { row ->
|
}.map { row ->
|
||||||
@ -517,7 +246,7 @@ class SummaryModel(val db : Database) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
manyJoined.select {
|
manyJoined.select {
|
||||||
DirsTable.projectid eq
|
DirsTable.projectid eq projectId
|
||||||
}.first().let { row ->
|
}.first().let { row ->
|
||||||
projectTree.stats = Statistics(row)
|
projectTree.stats = Statistics(row)
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user