Checkpoint
This commit is contained in:
		@@ -1,5 +1,6 @@
 | 
			
		||||
plugins {
 | 
			
		||||
	id 'org.jetbrains.kotlin.jvm' version "$kotlinVersion"
 | 
			
		||||
    id 'org.jetbrains.kotlin.plugin.serialization' version "$kotlinVersion"
 | 
			
		||||
	id 'application'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -24,5 +25,6 @@ dependencies {
 | 
			
		||||
	implementation "org.jetbrains.exposed:exposed-dao:$kotlin_exposed_version"
 | 
			
		||||
	implementation "org.jetbrains.exposed:exposed-jdbc:$kotlin_exposed_version"
 | 
			
		||||
	implementation "org.xerial:sqlite-jdbc:3.30.1"
 | 
			
		||||
    implementation 'com.google.code.gson:gson:2.8.6'
 | 
			
		||||
	testImplementation 'junit:junit:4.12'
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -138,8 +138,9 @@ fun main(args: Array<String>) = mainBody {
 | 
			
		||||
        val dbFname = File(outDir, "summary.db")
 | 
			
		||||
        val summaryDb = Database.connect("jdbc:sqlite:${dbFname.absolutePath}", driver="org.sqlite.JDBC")
 | 
			
		||||
        transaction(summaryDb) {
 | 
			
		||||
            exec("PRAGMA journal_mode = OFF")
 | 
			
		||||
            exec("PRAGMA synchronous = OFF")
 | 
			
		||||
            addLogger(StdOutSqlLogger)
 | 
			
		||||
            // exec("PRAGMA journal_mode = OFF")
 | 
			
		||||
            // exec("PRAGMA synchronous = OFF")
 | 
			
		||||
        }
 | 
			
		||||
        val summaryModel = SummaryModel(summaryDb)
 | 
			
		||||
 | 
			
		||||
@@ -155,6 +156,8 @@ fun main(args: Array<String>) = mainBody {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        renderSummary(projectRootFile, summaryModel, outDir)
 | 
			
		||||
 | 
			
		||||
        // Render summary
 | 
			
		||||
        System.err.println("Done, summary is in ${outDir}/index.html")
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										56
									
								
								src/main/kotlin/me/msoucy/gbat/Renderer.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/main/kotlin/me/msoucy/gbat/Renderer.kt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,56 @@
 | 
			
		||||
package me.msoucy.gbat
 | 
			
		||||
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
import me.msoucy.gbat.models.ProjectTreeNode
 | 
			
		||||
import me.msoucy.gbat.models.ProjectTreeResult
 | 
			
		||||
import me.msoucy.gbat.models.Statistics
 | 
			
		||||
import me.msoucy.gbat.models.SummaryModel
 | 
			
		||||
 | 
			
		||||
import com.google.gson.GsonBuilder
 | 
			
		||||
import org.jetbrains.exposed.sql.*
 | 
			
		||||
import org.jetbrains.exposed.sql.transactions.transaction
 | 
			
		||||
 | 
			
		||||
val NUM_RISKIEST_AUTHORS = 10
 | 
			
		||||
val NUM_RISKIEST_FILES = 10
 | 
			
		||||
 | 
			
		||||
class SummaryRenderer(
 | 
			
		||||
    val summaryModel : SummaryModel,
 | 
			
		||||
    val outputDir : File
 | 
			
		||||
) {
 | 
			
		||||
    private val filesDir = File(outputDir, "files")
 | 
			
		||||
    private val gson = GsonBuilder().setPrettyPrinting().create()
 | 
			
		||||
 | 
			
		||||
    fun renderAll(projectRoot : File) {
 | 
			
		||||
        createFilesDir()
 | 
			
		||||
        renderSummaryJson(projectRoot)
 | 
			
		||||
        renderFileJson(projectRoot)
 | 
			
		||||
        // renderSrc(projectRoot)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun renderSummaryJson(projectRoot : File) {
 | 
			
		||||
        val summary = summaryModel.projectSummary(projectRoot.absolutePath)
 | 
			
		||||
        val json = gson.toJson(summary)
 | 
			
		||||
        File(filesDir, "summary.json").writeText(json)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun renderFileJson(projectRoot : File) {
 | 
			
		||||
        summaryModel.projectFiles(projectRoot.absolutePath).forEach {
 | 
			
		||||
            val json = gson.toJson(summaryModel.fileSummary(it.fileId))
 | 
			
		||||
            File(filesDir, "${it.fileId}.json").writeText(json)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun createFilesDir() = filesDir.mkdirs()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun renderSummary(
 | 
			
		||||
    projectRoot : File,
 | 
			
		||||
    summaryModel : SummaryModel,
 | 
			
		||||
    outputDir : File
 | 
			
		||||
) {
 | 
			
		||||
    transaction(summaryModel.db) {
 | 
			
		||||
        val renderer = SummaryRenderer(summaryModel, outputDir)
 | 
			
		||||
        renderer.renderAll(projectRoot)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -237,7 +237,6 @@ class KnowledgeModel(val db : Database, val constant : Double, val riskModel : R
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun createTables() = transaction(db) {
 | 
			
		||||
        println ("-- In create tables")
 | 
			
		||||
        SchemaUtils.dropDatabase()
 | 
			
		||||
        SchemaUtils.createMissingTablesAndColumns(AuthorsTable, KnowledgeAcctsTable, KnowledgeAuthorsTable, LineKnowledge)
 | 
			
		||||
        AuthorsTable.insertIgnore { 
 | 
			
		||||
 
 | 
			
		||||
@@ -7,89 +7,92 @@ import org.jetbrains.exposed.dao.id.IntIdTable
 | 
			
		||||
import org.jetbrains.exposed.sql.*
 | 
			
		||||
import org.jetbrains.exposed.sql.transactions.transaction
 | 
			
		||||
 | 
			
		||||
class SummaryModel(val db : Database) {
 | 
			
		||||
private object ProjectTable : IntIdTable("projects", "projectid") {
 | 
			
		||||
    val project = text("project").uniqueIndex("project_idx")
 | 
			
		||||
}
 | 
			
		||||
private object DirsTable : IntIdTable("dirs", "dirid") {
 | 
			
		||||
    val dir = text("dir")
 | 
			
		||||
    val parentdirid = integer("parentdirid").references(DirsTable.id)
 | 
			
		||||
    val projectid = integer("projectid").references(ProjectTable.id)
 | 
			
		||||
    val dirsproj_idx = uniqueIndex("dirsproj_idx", dir, parentdirid, projectid)
 | 
			
		||||
}
 | 
			
		||||
private object FilesTable : IntIdTable("files", "fileid") {
 | 
			
		||||
    val fname = text("fname")
 | 
			
		||||
    val dirid = integer("dirid").index("filesdir_idx").references(DirsTable.id)
 | 
			
		||||
}
 | 
			
		||||
private object LinesTable : IntIdTable("lines", "lineid") {
 | 
			
		||||
    val line = text("line")
 | 
			
		||||
    val fileid = integer("fileid").index("linesfile_idx").references(FilesTable.id)
 | 
			
		||||
    val linenum = integer("linenum")
 | 
			
		||||
    val linesnumfile_idx = uniqueIndex("linesnumfile_idx", fileid, linenum)
 | 
			
		||||
}
 | 
			
		||||
private object AuthorsTable : IntIdTable("authors", "authorid") {
 | 
			
		||||
    val author = text("author").uniqueIndex("authorstrs_idx")
 | 
			
		||||
}
 | 
			
		||||
private object AuthorsGroupsTable : IntIdTable("authorgroups", "authorgroupid") {
 | 
			
		||||
    val authors = text("authorsstr").uniqueIndex("authorgroupsstrs_idx")
 | 
			
		||||
}
 | 
			
		||||
private object AuthorsAuthorGroupsTable : Table("authors_authorgroups") {
 | 
			
		||||
    val authorid = integer("authorid").references(AuthorsTable.id)
 | 
			
		||||
    val groupid = integer("authorgroupid").references(AuthorsGroupsTable.id)
 | 
			
		||||
    override val primaryKey = PrimaryKey(authorid, groupid)
 | 
			
		||||
}
 | 
			
		||||
private object AllocationsTable : IntIdTable("allocations", "allocationid") {
 | 
			
		||||
    val knowledge = double("knowledge")
 | 
			
		||||
    val risk = double("risk")
 | 
			
		||||
    val orphaned = double("orphaned")
 | 
			
		||||
    val lineid = integer("lineid").index("linealloc_idx").references(LinesTable.id)
 | 
			
		||||
    val authorgroupid = integer("authorgroupid").references(AuthorsGroupsTable.id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    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").references(DirsTable.id)
 | 
			
		||||
        val projectid = integer("projectid").references(ProjectTable.id)
 | 
			
		||||
        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").references(DirsTable.id)
 | 
			
		||||
    }
 | 
			
		||||
    object LinesTable : IntIdTable("lines", "lineid") {
 | 
			
		||||
        val line = text("line")
 | 
			
		||||
        val fileid = integer("fileid").index("linesfile_idx").references(FilesTable.id)
 | 
			
		||||
        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").references(AuthorsTable.id)
 | 
			
		||||
        val groupid = integer("authorgroupid").references(AuthorsGroupsTable.id)
 | 
			
		||||
        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").references(LinesTable.id)
 | 
			
		||||
        val authorgroupid = integer("authorgroupid").references(AuthorsGroupsTable.id)
 | 
			
		||||
    }
 | 
			
		||||
class LineDict {
 | 
			
		||||
    var stats = Statistics()
 | 
			
		||||
    var authorRisks = mutableMapOf<String, Statistics>()
 | 
			
		||||
}
 | 
			
		||||
class FileTree {
 | 
			
		||||
    var name = ""
 | 
			
		||||
    var stats = Statistics()
 | 
			
		||||
    var authorRisks = mutableMapOf<String, Statistics>()
 | 
			
		||||
    var lines = mutableListOf<LineDict>()
 | 
			
		||||
}
 | 
			
		||||
class FileEntry(var name : String = "") {
 | 
			
		||||
    var stats = Statistics()
 | 
			
		||||
    var authorRisks = mutableMapOf<String, Statistics>()
 | 
			
		||||
}
 | 
			
		||||
class ProjectTree {
 | 
			
		||||
    var name = "root"
 | 
			
		||||
    var files = mutableMapOf<Int, FileEntry>()
 | 
			
		||||
    var dirs = mutableListOf<Int>()
 | 
			
		||||
}
 | 
			
		||||
class ProjectFilesResult(var fileId : Int, var fname : Path)
 | 
			
		||||
 | 
			
		||||
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) {}
 | 
			
		||||
}
 | 
			
		||||
class ProjectTreeNode {
 | 
			
		||||
    var name = "root"
 | 
			
		||||
    var files = mutableListOf<FileEntry>()
 | 
			
		||||
    var dirs = mutableListOf<ProjectTreeNode>()
 | 
			
		||||
}
 | 
			
		||||
class ProjectTreeResult(var name : String, var root : ProjectTreeNode) {
 | 
			
		||||
    var stats = Statistics()
 | 
			
		||||
    var authorRisks = mutableMapOf<String, Statistics>()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class SummaryModel(val db : Database) {
 | 
			
		||||
 | 
			
		||||
    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) {}
 | 
			
		||||
    }
 | 
			
		||||
    class LineDict {
 | 
			
		||||
        var stats = Statistics()
 | 
			
		||||
        var authorRisks = mutableMapOf<String, Statistics>()
 | 
			
		||||
    }
 | 
			
		||||
    class FileTree {
 | 
			
		||||
        var name = ""
 | 
			
		||||
        var stats = Statistics()
 | 
			
		||||
        var authorRisks = mutableMapOf<String, Statistics>()
 | 
			
		||||
        var lines = mutableListOf<LineDict>()
 | 
			
		||||
    }
 | 
			
		||||
    class FileEntry(var name : String) {
 | 
			
		||||
        var stats = Statistics()
 | 
			
		||||
        var authorRisks = mutableMapOf<String, Statistics>()
 | 
			
		||||
    }
 | 
			
		||||
    class ProjectTree {
 | 
			
		||||
        var name = "root"
 | 
			
		||||
        var files = mutableMapOf<Int, FileEntry>()
 | 
			
		||||
        var dirs = mutableListOf<Int>()
 | 
			
		||||
    }
 | 
			
		||||
    class ProjectTreeNode {
 | 
			
		||||
        var name = "root"
 | 
			
		||||
        var files = mutableListOf<FileEntry>()
 | 
			
		||||
        var dirs = mutableListOf<ProjectTreeNode>()
 | 
			
		||||
    }
 | 
			
		||||
    class ProjectTreeResult(var name : String, var root : ProjectTreeNode) {
 | 
			
		||||
        var stats = Statistics()
 | 
			
		||||
        var authorRisks = mutableMapOf<String, Statistics>()
 | 
			
		||||
    }
 | 
			
		||||
    class ProjectFilesResult(var fileId : Int, var fname : Path)
 | 
			
		||||
    
 | 
			
		||||
    private val lineAllocations = (LinesTable leftJoin AllocationsTable)
 | 
			
		||||
    private val lineAllocationGroups = (lineAllocations leftJoin AuthorsGroupsTable)
 | 
			
		||||
@@ -204,7 +207,9 @@ class SummaryModel(val db : Database) {
 | 
			
		||||
                entry.value.files[row[FilesTable.id].value] = FileEntry(row[FilesTable.fname])
 | 
			
		||||
            }
 | 
			
		||||
            entry.value.files.entries.forEach { (fileId, fileEntry) ->
 | 
			
		||||
                lineAllocations.select { LinesTable.fileid eq fileId }
 | 
			
		||||
                lineAllocations
 | 
			
		||||
                .slice(AllocationsTable.knowledge.sum(), AllocationsTable.risk.sum(), AllocationsTable.orphaned.sum())
 | 
			
		||||
                .select { LinesTable.fileid eq fileId }
 | 
			
		||||
                .groupBy(LinesTable.fileid)
 | 
			
		||||
                .forEach { row ->
 | 
			
		||||
                    fileEntry.stats.totKnowledge = row[AllocationsTable.knowledge.sum()] ?: 0.0
 | 
			
		||||
@@ -213,7 +218,13 @@ class SummaryModel(val db : Database) {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            entry.value.files.entries.forEach { (fileId, fileEntry) ->
 | 
			
		||||
                lineAllocationGroups.select { LinesTable.fileid eq fileId }
 | 
			
		||||
                lineAllocationGroups
 | 
			
		||||
                .slice(
 | 
			
		||||
                    AllocationsTable.knowledge.sum(),
 | 
			
		||||
                    AllocationsTable.risk.sum(),
 | 
			
		||||
                    AllocationsTable.orphaned.sum(),
 | 
			
		||||
                    AuthorsGroupsTable.authors
 | 
			
		||||
                ).select { LinesTable.fileid eq fileId }
 | 
			
		||||
                .groupBy(AllocationsTable.authorgroupid)
 | 
			
		||||
                .orderBy(AuthorsGroupsTable.authors)
 | 
			
		||||
                .forEach { row ->
 | 
			
		||||
@@ -227,13 +238,25 @@ class SummaryModel(val db : Database) {
 | 
			
		||||
        val root = transformedRoot.dirs.first()
 | 
			
		||||
        val projectTree = ProjectTreeResult(project, root)
 | 
			
		||||
 | 
			
		||||
        allJoined.select { DirsTable.projectid eq projectId }
 | 
			
		||||
        allJoined
 | 
			
		||||
        .slice(
 | 
			
		||||
            AllocationsTable.knowledge.sum(),
 | 
			
		||||
            AllocationsTable.risk.sum(),
 | 
			
		||||
            AllocationsTable.orphaned.sum(),
 | 
			
		||||
            AuthorsGroupsTable.authors
 | 
			
		||||
        )
 | 
			
		||||
        .select { DirsTable.projectid eq projectId }
 | 
			
		||||
        .groupBy(AuthorsGroupsTable.id)
 | 
			
		||||
        .forEach { row ->
 | 
			
		||||
            projectTree.authorRisks[row[AuthorsGroupsTable.authors]] = Statistics(row)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        manyJoined.select {
 | 
			
		||||
        manyJoined
 | 
			
		||||
        .slice(
 | 
			
		||||
            AllocationsTable.knowledge.sum(),
 | 
			
		||||
            AllocationsTable.risk.sum(),
 | 
			
		||||
            AllocationsTable.orphaned.sum()
 | 
			
		||||
        ).select {
 | 
			
		||||
            DirsTable.projectid eq projectId
 | 
			
		||||
        }.first().let { row ->
 | 
			
		||||
            projectTree.stats = Statistics(row)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user