Skip to content

Commit

Permalink
🐛 [MTA-1879] Fix Risk/Confidence calculations to only count responses…
Browse files Browse the repository at this point in the history
… to required questionnaires. (#581)

This PR fixes two problems:

1) Application `Risk` and `Confidence` will be `"unknown"` and `0`
respectively until all required questionnaires are answered.
2) Answers to archived questionnaires will be ignored while performing
the calculation.

Fixes https://issues.redhat.com/browse/MTA-1879

Signed-off-by: Sam Lucidi <[email protected]>
  • Loading branch information
mansam authored Dec 19, 2023
1 parent f9209a8 commit d8e71ec
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 27 deletions.
31 changes: 20 additions & 11 deletions api/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ package api

import (
"encoding/json"
"net/http"
"sort"
"strings"

"github.com/gin-gonic/gin"
"github.com/konveyor/tackle2-hub/assessment"
"github.com/konveyor/tackle2-hub/metrics"
"github.com/konveyor/tackle2-hub/model"
"gorm.io/gorm/clause"
"net/http"
"sort"
"strings"
)

//
Expand Down Expand Up @@ -502,7 +503,12 @@ func (h ApplicationHandler) TagList(ctx *gin.Context) {
_ = ctx.Error(err)
return
}
resolver := assessment.NewApplicationResolver(app, tagsResolver, membership, nil)
questionnaire, err := assessment.NewQuestionnaireResolver(h.DB(ctx))
if err != nil {
_ = ctx.Error(err)
return
}
resolver := assessment.NewApplicationResolver(app, tagsResolver, membership, questionnaire)
if includeArchetype {
archetypeTags, err := resolver.ArchetypeTags()
if err != nil {
Expand Down Expand Up @@ -1137,6 +1143,7 @@ func (r *Application) With(m *model.Application, tags []model.ApplicationTag) {
})
r.Effort = m.Analyses[len(m.Analyses)-1].Effort
}
r.Risk = assessment.RiskUnknown
}

//
Expand Down Expand Up @@ -1172,13 +1179,15 @@ func (r *Application) WithResolver(resolver *assessment.ApplicationResolver) (er
if err != nil {
return
}
r.Confidence, err = resolver.Confidence()
if err != nil {
return
}
r.Risk, err = resolver.Risk()
if err != nil {
return
if r.Assessed {
r.Confidence, err = resolver.Confidence()
if err != nil {
return
}
r.Risk, err = resolver.Risk()
if err != nil {
return
}
}
return
}
Expand Down
17 changes: 13 additions & 4 deletions api/archetype.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package api

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/konveyor/tackle2-hub/assessment"
"github.com/konveyor/tackle2-hub/metrics"
"github.com/konveyor/tackle2-hub/model"
"gorm.io/gorm/clause"
"net/http"
)

//
Expand Down Expand Up @@ -152,7 +153,12 @@ func (h ArchetypeHandler) Create(ctx *gin.Context) {
}

membership := assessment.NewMembershipResolver(h.DB(ctx))
resolver := assessment.NewArchetypeResolver(m, nil, membership, nil)
questionnaires, err := assessment.NewQuestionnaireResolver(h.DB(ctx))
if err != nil {
_ = ctx.Error(err)
return
}
resolver := assessment.NewArchetypeResolver(m, nil, membership, questionnaires)
r.With(m)
err = r.WithResolver(resolver)
if err != nil {
Expand Down Expand Up @@ -384,15 +390,18 @@ func (r *Archetype) With(m *model.Archetype) {
ref.With(m.Review.ID, "")
r.Review = ref
}
r.Risk = assessment.RiskUnknown
}

//
// WithResolver uses an ArchetypeResolver to update the resource with
// values derived from the archetype's assessments.
func (r *Archetype) WithResolver(resolver *assessment.ArchetypeResolver) (err error) {
r.Risk = resolver.Risk()
r.Confidence = resolver.Confidence()
r.Assessed = resolver.Assessed()
if r.Assessed {
r.Risk = resolver.Risk()
r.Confidence = resolver.Confidence()
}
apps, err := resolver.Applications()
for i := range apps {
ref := Ref{}
Expand Down
40 changes: 31 additions & 9 deletions assessment/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,23 @@ func (r *ApplicationResolver) ArchetypeTags() (tags []model.Tag, err error) {
return
}

//
// RequiredAssessments returns the slice of assessments that are for required questionnaires.
func (r *ApplicationResolver) RequiredAssessments() (required []Assessment) {
for _, a := range r.application.Assessments {
if r.questionnaireResolver.Required(a.QuestionnaireID) {
required = append(required, a)
}
}
return
}

//
// AssessmentTags returns the list of tags that the application should inherit from the answers given
// to its assessments.
func (r *ApplicationResolver) AssessmentTags() (tags []model.Tag) {
seenTags := make(map[uint]bool)
for _, assessment := range r.application.Assessments {
for _, assessment := range r.RequiredAssessments() {
aTags := r.tagResolver.Assessment(assessment)
for _, t := range aTags {
if _, found := seenTags[t.ID]; !found {
Expand All @@ -116,16 +127,21 @@ func (r *ApplicationResolver) AssessmentTags() (tags []model.Tag) {
// Risk returns the overall risk level for the application based on its or its archetypes' assessments.
func (r *ApplicationResolver) Risk() (risk string, err error) {
var assessments []Assessment
if len(r.application.Assessments) > 0 {
assessments = r.application.Assessments
requiredAssessments := r.RequiredAssessments()
if len(requiredAssessments) > 0 {
assessments = requiredAssessments
} else {
archetypes, aErr := r.Archetypes()
if aErr != nil {
err = aErr
return
}
for _, a := range archetypes {
assessments = append(assessments, a.Assessments...)
for _, assessment := range a.Assessments {
if r.questionnaireResolver.Required(assessment.QuestionnaireID) {
assessments = append(assessments, assessment)
}
}
}
}
risk = Risk(assessments)
Expand All @@ -136,16 +152,21 @@ func (r *ApplicationResolver) Risk() (risk string, err error) {
// Confidence returns the application's overall assessment confidence score.
func (r *ApplicationResolver) Confidence() (confidence int, err error) {
var assessments []Assessment
if len(r.application.Assessments) > 0 {
assessments = r.application.Assessments
requiredAssessments := r.RequiredAssessments()
if len(requiredAssessments) > 0 {
assessments = requiredAssessments
} else {
archetypes, aErr := r.Archetypes()
if aErr != nil {
err = aErr
return
}
for _, a := range archetypes {
assessments = append(assessments, a.Assessments...)
for _, assessment := range a.Assessments {
if r.questionnaireResolver.Required(assessment.QuestionnaireID) {
assessments = append(assessments, assessment)
}
}
}
}
confidence = Confidence(assessments)
Expand All @@ -157,8 +178,9 @@ func (r *ApplicationResolver) Confidence() (confidence int, err error) {
func (r *ApplicationResolver) Assessed() (assessed bool, err error) {
// if the application has any of its own assessments, only consider them for
// determining whether it has been assessed.
if len(r.application.Assessments) > 0 {
assessed = r.questionnaireResolver.Assessed(r.application.Assessments)
assessments := r.RequiredAssessments()
if len(assessments) > 0 {
assessed = r.questionnaireResolver.Assessed(assessments)
return
}
// otherwise the application is assessed if all of its archetypes are fully assessed.
Expand Down
17 changes: 14 additions & 3 deletions assessment/archetype.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,28 @@ func (r *ArchetypeResolver) AssessmentTags() (tags []model.Tag) {
return
}

//
// RequiredAssessments returns the slice of assessments that are for required questionnaires.
func (r *ArchetypeResolver) RequiredAssessments() (required []Assessment) {
for _, a := range r.archetype.Assessments {
if r.questionnaire.Required(a.QuestionnaireID) {
required = append(required, a)
}
}
return
}

//
// Risk returns the overall risk level for the archetypes' assessments.
func (r *ArchetypeResolver) Risk() (risk string) {
risk = Risk(r.archetype.Assessments)
risk = Risk(r.RequiredAssessments())
return
}

//
// Confidence returns the archetype's overall assessment confidence score.
func (r *ArchetypeResolver) Confidence() (confidence int) {
confidence = Confidence(r.archetype.Assessments)
confidence = Confidence(r.RequiredAssessments())
return
}

Expand All @@ -85,7 +96,7 @@ func (r *ArchetypeResolver) Assessed() (assessed bool) {
if r.questionnaire == nil {
return
}
assessed = r.questionnaire.Assessed(r.archetype.Assessments)
assessed = r.questionnaire.Assessed(r.RequiredAssessments())
return
}

Expand Down
6 changes: 6 additions & 0 deletions assessment/questionnaire.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ func (r *QuestionnaireResolver) cacheQuestionnaires() (err error) {
return
}

//
// Required returns whether a questionnaire is required.
func (r *QuestionnaireResolver) Required(id uint) (required bool) {
return r.requiredQuestionnaires.Contains(id)
}

//
// Assessed returns whether a slice contains a completed assessment for each of the required
// questionnaires.
Expand Down

0 comments on commit d8e71ec

Please sign in to comment.