universe@150: /* universe@150: * Copyright 2020 Mike Becker. All rights reserved. universe@150: * universe@150: * Redistribution and use in source and binary forms, with or without universe@150: * modification, are permitted provided that the following conditions are met: universe@150: * universe@150: * 1. Redistributions of source code must retain the above copyright universe@150: * notice, this list of conditions and the following disclaimer. universe@150: * universe@150: * 2. Redistributions in binary form must reproduce the above copyright universe@150: * notice, this list of conditions and the following disclaimer in the universe@150: * documentation and/or other materials provided with the distribution. universe@150: * universe@150: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" universe@150: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE universe@150: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE universe@150: * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE universe@150: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL universe@150: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR universe@150: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER universe@150: * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, universe@150: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE universe@150: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. universe@150: */ universe@150: universe@150: package de.uapcore.lightpit.entities universe@150: universe@150: import java.sql.Date universe@150: import java.sql.Timestamp universe@150: import java.time.Instant universe@150: import kotlin.math.roundToInt universe@150: universe@150: data class IssueStatusPhase(val number: Int) { universe@150: companion object { universe@150: val Open = IssueStatusPhase(0) universe@150: val WorkInProgress = IssueStatusPhase(1) universe@150: val Done = IssueStatusPhase(2) universe@150: } universe@150: } universe@150: universe@150: enum class IssueStatus(val phase: IssueStatusPhase) { universe@150: InSpecification(IssueStatusPhase.Open), universe@150: ToDo(IssueStatusPhase.Open), universe@150: Scheduled(IssueStatusPhase.Open), universe@150: InProgress(IssueStatusPhase.WorkInProgress), universe@150: InReview(IssueStatusPhase.WorkInProgress), universe@150: Done(IssueStatusPhase.Done), universe@150: Rejected(IssueStatusPhase.Done), universe@150: Withdrawn(IssueStatusPhase.Done), universe@150: Duplicate(IssueStatusPhase.Done); universe@150: } universe@150: universe@150: enum class IssueCategory { universe@150: Feature, Improvement, Bug, Task, Test universe@150: } universe@150: universe@150: data class Issue(var id: Int) { universe@150: universe@150: var project: Project? = null universe@150: var component: Component? = null universe@150: universe@150: var status = IssueStatus.InSpecification universe@150: var category = IssueCategory.Feature universe@150: universe@150: var subject: String? = null universe@150: var description: String? = null universe@150: var assignee: User? = null universe@150: universe@150: var affectedVersions = emptyList() universe@150: var resolvedVersions = emptyList() universe@150: universe@150: var created: Timestamp = Timestamp.from(Instant.now()) universe@150: var updated: Timestamp = Timestamp.from(Instant.now()) universe@150: var eta: Date? = null universe@150: universe@150: /** universe@150: * An issue is overdue, if it is not done and the ETA is before the current time. universe@150: */ universe@150: val overdue get() = status.phase != IssueStatusPhase.Done && eta?.before(Date(System.currentTimeMillis())) ?: false universe@150: } universe@150: universe@150: class IssueSummary { universe@150: var open = 0 universe@150: var active = 0 universe@150: var done = 0 universe@150: universe@150: val total get() = open + active + done universe@150: universe@150: val openPercent get() = 100 - activePercent - donePercent universe@150: val activePercent get() = if (total > 0) (100f * active / total).roundToInt() else 0 universe@150: val donePercent get() = if (total > 0) (100f * done / total).roundToInt() else 100 universe@150: universe@150: /** universe@150: * Adds the specified issue to the summary by incrementing the respective counter. universe@150: * @param issue the issue universe@150: */ universe@150: fun add(issue: Issue) { universe@150: when (issue.status.phase) { universe@150: IssueStatusPhase.Open -> open++ universe@150: IssueStatusPhase.WorkInProgress -> active++ universe@150: IssueStatusPhase.Done -> done++ universe@150: } universe@150: } universe@150: } universe@150: