Starting summary model

This is rather large...
This commit is contained in:
Matt Soucy 2020-06-25 21:23:03 -04:00
parent 604702d2d2
commit ffaf4aed34

View File

@ -1,7 +1,9 @@
package me.msoucy.gbat package me.msoucy.gbat
import java.io.File import java.io.File
import java.nio.file.Paths
import kotlin.io.forEachLine import kotlin.io.forEachLine
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
@ -97,21 +99,20 @@ class LineModel() {
} }
class KnowledgeModel(val constant : Double, val riskModel : RiskModel) { class KnowledgeModel(val db : Database, val constant : Double, val riskModel : RiskModel) {
class KnowledgeAcct(var knowledgeAcctId : Int, class KnowledgeAcct(var knowledgeAcctId : Int,
var authors : List<String>, var authors : List<String>,
var authorsStr : String) var authorsStr : String)
object AuthorsTable : Table("authors") { object AuthorsTable : Table("authors") {
val id = integer("id") val id = integer("authorid")
val author = text("author") val author = text("author").uniqueIndex("authors_idx")
val idx = integer("idx").uniqueIndex()
override val primaryKey = PrimaryKey(id) override val primaryKey = PrimaryKey(id)
} }
object KnowledgeAcctsTable : Table("knowledgeaccts") { object KnowledgeAcctsTable : Table("knowledgeaccts") {
val id = integer("id") val id = integer("knowledgeacctid")
val authors = text("authors") val authors = text("authors").uniqueIndex("knowledgeacctsauthors_idx")
val idx = integer("idx").uniqueIndex()
override val primaryKey = PrimaryKey(id) override val primaryKey = PrimaryKey(id)
} }
object KnowledgeAuthorsTable : Table("knowedgeaccts_authors") { object KnowledgeAuthorsTable : Table("knowedgeaccts_authors") {
@ -125,6 +126,10 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
val knowledge = double("knowledge") val knowledge = double("knowledge")
} }
init {
createTables()
}
val SAFE_AUTHOR_ID = 1 val SAFE_AUTHOR_ID = 1
val SAFE_KNOWLEDGE_ACCT_ID = 1 val SAFE_KNOWLEDGE_ACCT_ID = 1
val KNOWLEDGE_PER_LINE_ADDED = 1000.0 val KNOWLEDGE_PER_LINE_ADDED = 1000.0
@ -160,7 +165,7 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
adjustKnowledge(knowledgeAcctId, lineNum, KNOWLEDGE_PER_LINE_ADDED) adjustKnowledge(knowledgeAcctId, lineNum, KNOWLEDGE_PER_LINE_ADDED)
} }
fun knowledgeSummary(lineNum : Int) = transaction { fun knowledgeSummary(lineNum : Int) = transaction(db) {
LineKnowledge.select { LineKnowledge.select {
LineKnowledge.linenum eq lineNum LineKnowledge.linenum eq lineNum
}.map { }.map {
@ -171,7 +176,7 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
}.copyOf() }.copyOf()
} }
private fun bumpAllLinesFrom(lineNum : Int, adjustment : Int) = transaction { private fun bumpAllLinesFrom(lineNum : Int, adjustment : Int) = transaction(db) {
LineKnowledge.update({LineKnowledge.linenum greater lineNum}) { LineKnowledge.update({LineKnowledge.linenum greater lineNum}) {
with(SqlExpressionBuilder) { with(SqlExpressionBuilder) {
it[LineKnowledge.linenum] = LineKnowledge.linenum + adjustment it[LineKnowledge.linenum] = LineKnowledge.linenum + adjustment
@ -179,7 +184,7 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
} }
} }
private fun getKnowledgeAcct(knowledgeAcctId : Int) = transaction { private fun getKnowledgeAcct(knowledgeAcctId : Int) = transaction(db) {
KnowledgeAcctsTable.select { KnowledgeAcctsTable.select {
KnowledgeAcctsTable.id eq knowledgeAcctId KnowledgeAcctsTable.id eq knowledgeAcctId
}.map { }.map {
@ -191,7 +196,7 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
}.first() }.first()
} }
private fun destroyLineKnowledge(knowledgeId : Int, lineNum : Int) = transaction { private fun destroyLineKnowledge(knowledgeId : Int, lineNum : Int) = transaction(db) {
LineKnowledge.deleteWhere { LineKnowledge.deleteWhere {
(LineKnowledge.knowledgeacctid eq knowledgeId) and (LineKnowledge.knowledgeacctid eq knowledgeId) and
(LineKnowledge.linenum eq lineNum) (LineKnowledge.linenum eq lineNum)
@ -226,7 +231,7 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
} }
} }
private fun knowledgeInAcct(knowledgeAcctId : Int, lineNum : Int) = transaction { private fun knowledgeInAcct(knowledgeAcctId : Int, lineNum : Int) = transaction(db) {
LineKnowledge.select { LineKnowledge.select {
(LineKnowledge.knowledgeacctid eq knowledgeAcctId) and (LineKnowledge.knowledgeacctid eq knowledgeAcctId) and
(LineKnowledge.linenum eq lineNum) (LineKnowledge.linenum eq lineNum)
@ -235,7 +240,7 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
}.first() }.first()
} }
private fun nonSafeAcctsWithKnowledgeOf(lineNum : Int) = transaction { private fun nonSafeAcctsWithKnowledgeOf(lineNum : Int) = transaction(db) {
LineKnowledge.select { LineKnowledge.select {
(LineKnowledge.linenum eq lineNum) and (LineKnowledge.linenum eq lineNum) and
(LineKnowledge.knowledgeacctid neq SAFE_KNOWLEDGE_ACCT_ID) (LineKnowledge.knowledgeacctid neq SAFE_KNOWLEDGE_ACCT_ID)
@ -244,7 +249,7 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
} }
} }
private fun allAcctsWithKnowledgeOf(lineNum : Int) = transaction { private fun allAcctsWithKnowledgeOf(lineNum : Int) = transaction(db) {
LineKnowledge.select { LineKnowledge.select {
LineKnowledge.linenum eq lineNum LineKnowledge.linenum eq lineNum
}.map { }.map {
@ -252,7 +257,7 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
} }
} }
private fun adjustKnowledge(knowledgeAcctId : Int, lineNum : Int, adjustment : Double) = transaction { private fun adjustKnowledge(knowledgeAcctId : Int, lineNum : Int, adjustment : Double) = transaction(db) {
val lineExists = LineKnowledge.select { val lineExists = LineKnowledge.select {
(LineKnowledge.knowledgeacctid eq knowledgeAcctId) and (LineKnowledge.knowledgeacctid eq knowledgeAcctId) and
(LineKnowledge.linenum eq lineNum) (LineKnowledge.linenum eq lineNum)
@ -274,7 +279,7 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
} }
} }
private fun lookupOrCreateKnowledgeAcct(authors : List<String>) = transaction { private fun lookupOrCreateKnowledgeAcct(authors : List<String>) = transaction(db) {
val authorStr = authors.sorted().joinToString("\n") val authorStr = authors.sorted().joinToString("\n")
var newId = -1 var newId = -1
KnowledgeAcctsTable.select { KnowledgeAcctsTable.select {
@ -293,8 +298,7 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
it[KnowledgeAcctsTable.id] it[KnowledgeAcctsTable.id]
}.first() }.first()
authors.map(::lookupOrCreateAuthor). authors.map(::lookupOrCreateAuthor).forEach { authorId ->
forEach { authorId ->
KnowledgeAuthorsTable.insert { KnowledgeAuthorsTable.insert {
it[KnowledgeAuthorsTable.knowledgeacctid] = newId it[KnowledgeAuthorsTable.knowledgeacctid] = newId
it[KnowledgeAuthorsTable.authorid] = authorId it[KnowledgeAuthorsTable.authorid] = authorId
@ -304,27 +308,26 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
newId newId
} }
private fun lookupOrCreateAuthor(authorName : String) = transaction { private fun lookupOrCreateAuthor(authorName : String) = transaction(db) {
AuthorsTable.insertIgnore { AuthorsTable.insertIgnore {
it[author] = authorName it[author] = authorName
} }
AuthorsTable.select { AuthorsTable.select {
AuthorsTable.author eq authorName AuthorsTable.author eq authorName
}.fetchSize(1). }.fetchSize(1).map {
map {
it[AuthorsTable.id] it[AuthorsTable.id]
}.first() }.first()
} }
private fun totalLineKnowledge(linenum : Int) = transaction { private fun totalLineKnowledge(linenum : Int) = transaction(db) {
LineKnowledge.select { LineKnowledge.select {
LineKnowledge.linenum eq linenum LineKnowledge.linenum eq linenum
}.fetchSize(1). }.fetchSize(1).map {
map { it[LineKnowledge.knowledge] }. it[LineKnowledge.knowledge]
sum() }.first()
} }
private fun createTables() = transaction { private fun createTables() = transaction(db) {
SchemaUtils.createMissingTablesAndColumns(AuthorsTable, KnowledgeAcctsTable, KnowledgeAuthorsTable, LineKnowledge) SchemaUtils.createMissingTablesAndColumns(AuthorsTable, KnowledgeAcctsTable, KnowledgeAuthorsTable, LineKnowledge)
AuthorsTable.insertIgnore { AuthorsTable.insertIgnore {
it[id] = 1 it[id] = 1
@ -334,9 +337,271 @@ class KnowledgeModel(val constant : Double, val riskModel : RiskModel) {
it[id] = 1 it[id] = 1
it[authors] = "" it[authors] = ""
} }
KnowledgeAuthorsTable.insertIgnore { KnowledgeAuthorsTable.insertIgnore {
it[knowledgeacctid] = SAFE_KNOWLEDGE_ACCT_ID it[knowledgeacctid] = SAFE_KNOWLEDGE_ACCT_ID
it[authorid] = SAFE_AUTHOR_ID it[authorid] = SAFE_AUTHOR_ID
} }
} }
}
class SummaryModel(val db : Database) {
object ProjectTable : IntIdTable("projects", "projectid") {
val project = text("project").uniqueIndex("project_idx")
}
object DirsTable : IntIdTable("dirs", "dirid") {
val dir = text("dir")
val parentdirid = integer("parentdirid")
val projectid = integer("projectid")
val dirsproj_idx = uniqueIndex("dirsproj_idx", dir, parentdirid, projectid)
}
object FilesTable : IntIdTable("files", "fileid") {
val fname = text("fname")
val dirid = integer("dirid").index("filesdir_idx")
}
object LinesTable : IntIdTable("lines", "lineid") {
val line = text("line")
val fileid = integer("fileid").index("linesfile_idx")
val linenum = integer("linenum")
val linesnumfile_idx = uniqueIndex("linesnumfile_idx", fileid, linenum)
}
object AuthorsTable : IntIdTable("authors", "authorid") {
val author = text("author").uniqueIndex("authorstrs_idx")
}
object AuthorsGroupsTable : IntIdTable("authorgroups", "authorgroupid") {
val authors = text("authorsstr").uniqueIndex("authorgroupsstrs_idx")
}
object AuthorsAuthorGroupsTable : Table("authors_authorgroups") {
val authorid = integer("authorid")
val groupid = integer("authorgroupid")
override val primaryKey = PrimaryKey(authorid, groupid)
}
object AllocationsTable : IntIdTable("allocations", "allocationid") {
val knowledge = double("knowledge")
val risk = double("risk")
val orphaned = double("orphaned")
val lineid = integer("lineid").index("linealloc_idx")
val authorgroupid = integer("authorgroupid")
}
val GIT_BY_A_BUS_BELOW_THRESHOLD = "Git by a Bus Safe Author"
init {
createTables()
}
data class Statistics(var totKnowledge : Double = 0.0,
var totRisk : Double = 0.0,
var totOrphaned : Double = 0.0) {
constructor(row : ResultRow) :
this(row[AllocationsTable.knowledge.sum()] ?: 0.0,
row[AllocationsTable.risk.sum()] ?: 0.0,
row[AllocationsTable.orphaned.sum()] ?: 0.0) {}
}
data class AuthorRisk(var stats : Statistics = Statistics())
data class LineDict(var stats : Statistics = Statistics(), var authorRisks : MutableMap<String, AuthorRisk> = mutableMapOf())
class FileTree(var name : String = "",
var stats : Statistics = Statistics(),
var authorRisks : MutableMap<String, AuthorRisk> = mutableMapOf(),
var lines : MutableList<LineDict> = mutableListOf())
fun fileSummary(fileId : Int) = transaction(db) {
var fileTree = FileTree()
val joinA = Join(LinesTable, AllocationsTable,
JoinType.LEFT,
LinesTable.id, AllocationsTable.lineid)
val joinB = Join(joinA, AuthorsGroupsTable,
JoinType.LEFT,
AuthorsGroupsTable.id, AllocationsTable.authorgroupid
)
joinB.select {
LinesTable.fileid eq fileId
}.groupBy(AuthorsGroupsTable.id).forEach { row ->
val authors = row[AuthorsGroupsTable.authors]
fileTree.authorRisks[authors] =
AuthorRisk(Statistics(row))
}
Join(joinA, FilesTable,
JoinType.LEFT,
LinesTable.fileid, FilesTable.id
).select {
LinesTable.fileid eq fileId
}.fetchSize(1).forEach { row ->
fileTree.stats = Statistics(row)
}
fileTree.name = FilesTable.select {
FilesTable.id eq fileId
}.map { it[FilesTable.fname] }.first()
LinesTable.select {
LinesTable.fileid eq fileId
}.map {
it[LinesTable.id].value
}.forEach { lineId ->
val lineDict = LineDict()
joinB.select {
LinesTable.id eq lineId
}.groupBy(AuthorsGroupsTable.id).forEach { lineRow ->
lineDict.authorRisks[lineRow[AuthorsGroupsTable.authors]] = AuthorRisk(Statistics(lineRow))
}
joinA.select {
LinesTable.id eq lineId
}.fetchSize(1).forEach {
lineDict.stats = Statistics(it)
}
fileTree.lines.add(lineDict)
}
fileTree
}
fun fileLines(fileId : Int) : List<String> = transaction(db) {
LinesTable.select {
LinesTable.fileid eq fileId
}.orderBy(LinesTable.linenum).map {
it[LinesTable.line]
}
}
private fun Double?.zeroIfNone() = this ?: 0.0
private fun reconsDir(dirId : Int) = transaction(db) {
val segs = mutableListOf<String>()
var newDirId = dirId
while(newDirId != 0) {
DirsTable.select {
DirsTable.id eq newDirId
}.forEach {
segs.add(it[DirsTable.dir])
newDirId = it[DirsTable.parentdirid]
}
}
Paths.get(segs.reversed().joinToString("/")).normalize()
}
private fun safeAuthorName(author : String?) = author ?: GIT_BY_A_BUS_BELOW_THRESHOLD
private fun createAllocation(knowledge : Double, risk : Double, orphaned : Double, authorGroupId : Int, lineId : Int) = transaction(db) {
AllocationsTable.insert {
it[AllocationsTable.knowledge] = knowledge
it[AllocationsTable.risk] = risk
it[AllocationsTable.orphaned] = orphaned
it[AllocationsTable.lineid] = lineId
it[AllocationsTable.authorgroupid] = authorGroupId
}
}
private fun findOrCreateAuthorGroup(authors : List<String>) : Int = transaction(db) {
val authorsstr = authors.joinToString("\n")
var authorGroupId = AuthorsGroupsTable.select {
AuthorsGroupsTable.authors eq authorsstr
}.map {
it[AuthorsGroupsTable.id].value
}.firstOrNull()
if (authorGroupId == null) {
authorGroupId = AuthorsGroupsTable.insertAndGetId {
it[AuthorsGroupsTable.authors] = authorsstr
}.value
authors.forEach {
val authorId = findOrCreateAuthor(it)
AuthorsAuthorGroupsTable.insert {
it[AuthorsAuthorGroupsTable.authorid] = authorId
it[AuthorsAuthorGroupsTable.groupid] = authorGroupId
}
}
}
authorGroupId
}
private fun findOrCreateAuthor(author : String) : Int = transaction(db) {
AuthorsTable.insertIgnore {
it[AuthorsTable.author] = author
}
ProjectTable.select {
AuthorsTable.author eq author
}.map {
it[AuthorsTable.id].value
}.first()
}
private fun createLine(line : String, lineNum : Int, fileId : Int) = transaction(db) {
LinesTable.insertAndGetId {
it[LinesTable.line] = line
it[LinesTable.linenum] = lineNum
it[LinesTable.fileid] = fileId
}.value
}
private fun createFile(fname : String, parentDirId : Int) = transaction(db) {
FilesTable.insertAndGetId {
it[FilesTable.fname] = fname
it[FilesTable.dirid] = parentDirId
}.value
}
private fun findOrCreateProject(project : String) : Int = transaction(db) {
ProjectTable.insertIgnore {
it[ProjectTable.project] = project
}
ProjectTable.select {
ProjectTable.project eq project
}.map {
it[ProjectTable.id].value
}.first()
}
private fun findOrCreateDir(dirname : String, projectId : Int, parentDirId : Int) : Int = transaction(db) {
DirsTable.insertIgnoreAndGetId {
it[dir] = dirname
it[parentdirid] = parentDirId
it[projectid] = projectId
}
DirsTable.select {
DirsTable.dir eq dirname
DirsTable.parentdirid eq parentDirId
DirsTable.projectid eq projectId
}.map {
it[DirsTable.id]
}.first().value
}
private fun splitAllDirs(dirname : File) = dirname.toPath().iterator().asSequence().toList()
private fun adjustFname(repoRoot : File, projectRoot : File, fname : File) : File {
var rootDiff = projectRoot.relativeTo(repoRoot)
return if(rootDiff.toString().length != 0) {
fname.relativeTo(rootDiff)
} else {
fname
}
}
private fun reconsDirs(dirId : Int) = transaction(db) {
val dirs = mutableListOf<String>()
var parentDirId = dirId
while(parentDirId != 0) {
DirsTable.select {
DirsTable.id eq parentDirId
}.fetchSize(1).
forEach {
dirs.add(it[DirsTable.dir])
parentDirId = it[DirsTable.parentdirid]
}
}
dirs.reversed()
}
private fun createTables() = transaction(db) {
SchemaUtils.createMissingTablesAndColumns(
ProjectTable,
FilesTable,
LinesTable,
AuthorsTable,
AuthorsGroupsTable,
AuthorsAuthorGroupsTable,
AllocationsTable
)
}
} }