Skip to content

Commit

Permalink
Merge pull request #2450 from NationalSecurityAgency/t#2425/show-expi…
Browse files Browse the repository at this point in the history
…ration

T#2425/show expiration
  • Loading branch information
sudo-may authored Oct 24, 2023
2 parents bc53248 + c47dfcc commit 8bf0a8a
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 1 deletion.
11 changes: 11 additions & 0 deletions client-display/src/userSkills/skill/progress/SkillProgress2.vue
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ limitations under the License.
<i class="fas fa-clock skills-color-expiration mr-2"></i>Expires <span class="font-weight-bold">{{ expirationDate(true) | relativeTime() }}</span>, perform this skill to keep your points!
</div>
</div>
<div v-if="showHasExpiredMessage" data-cy="hasExpired">
<div class="my-2">
<i class="fas fa-clock skills-color-expiration mr-2"></i>Points expired <span class="font-weight-bold">{{ skill.lastExpirationDate | relativeTime() }}</span>
</div>
</div>

<div v-if="skill.selfReporting && skill.selfReporting.requestedOn && allowDrillDown" data-cy="approvalPending">
<span v-if="!skill.selfReporting.rejectedOn"><i class="far fa-clock" aria-hidden="true"></i> Pending Approval</span>
Expand Down Expand Up @@ -315,6 +320,12 @@ limitations under the License.
showBadgesAndTagsRow() {
return ((this.skill.badges && this.skill.badges.length > 0 && !this.badgeId) || (this.skill.tags && this.skill.tags.length > 0));
},
showHasExpiredMessage() {
if (this.skillInternal && this.skillInternal.lastExpirationDate && this.skillInternal.points === 0) {
return true;
}
return false;
},
showMotivationalExpirationMessage() {
if (this.skillInternal && this.skillInternal.achievedOn && this.expirationDate() && this.skillInternal.isMotivationalSkill) {
if (this.motivationalSkillWarningGracePeriod) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,42 @@ describe('Client Display Expiration Tests', () => {
cy.get(`[data-cy="expirationDate"]`).contains(`Expires ${thirdRuntime.fromNow()}`)

});

it('expired achievement shows how long ago in UI', () => {
cy.configureExpiration(1, 0, 1, 'DAILY');
cy.configureExpiration(2, 0, 1, 'DAILY');
cy.configureExpiration(3, 0, 3, 'DAILY');
const yesterday = moment.utc().subtract(1, 'day')
const twoDaysAgo = moment.utc().subtract(2, 'day')
cy.doReportSkill({ project: 1, skill: 1, subjNum: 1, userId: Cypress.env('proxyUser'), date: yesterday.format('YYYY-MM-DD HH:mm') })
cy.doReportSkill({ project: 1, skill: 1, subjNum: 1, userId: Cypress.env('proxyUser'), date: twoDaysAgo.format('YYYY-MM-DD HH:mm') })
cy.doReportSkill({ project: 1, skill: 2, subjNum: 1, userId: Cypress.env('proxyUser'), date: yesterday.format('YYYY-MM-DD HH:mm') })
cy.doReportSkill({ project: 1, skill: 2, subjNum: 1, userId: Cypress.env('proxyUser'), date: twoDaysAgo.format('YYYY-MM-DD HH:mm') })
cy.doReportSkill({ project: 1, skill: 3, subjNum: 1, userId: Cypress.env('proxyUser'), date: yesterday.format('YYYY-MM-DD HH:mm') })

cy.expireSkills();
cy.cdVisit('/');
cy.cdClickSubj(0);

cy.get(`[data-cy="skillProgress_index-0"] [data-cy="hasExpired"]`)
.should('exist');
cy.get(`[data-cy="skillProgress_index-0"] [data-cy="hasExpired"]`).contains(`Points expired a few seconds ago`)
cy.get(`[data-cy="skillProgress_index-1"] [data-cy="hasExpired"]`)
.should('exist');
cy.get(`[data-cy="skillProgress_index-1"] [data-cy="hasExpired"]`).contains('Points expired a few seconds ago')
cy.get(`[data-cy="skillProgress_index-2"] [data-cy="hasExpired"]`)
.should('not.exist');

cy.cdClickSkill(0);
cy.get(`[data-cy="hasExpired"]`).should('exist');
cy.get(`[data-cy="hasExpired"]`).contains(`Points expired a few seconds ago`)

cy.get('[data-cy="nextSkill"]').click();
cy.get(`[data-cy="hasExpired"]`).should('exist');
cy.get(`[data-cy="hasExpired"]`).contains('Points expired a few seconds ago')

cy.get('[data-cy="nextSkill"]').click();
cy.get(`[data-cy="hasExpired"]`).should('not.exist');

});
});
1 change: 0 additions & 1 deletion e2e-tests/cypress/support/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -1402,7 +1402,6 @@ Cypress.Commands.add('configureExpiration', (skillNum = '1', numDays = 30, every
nextExpirationDate: m ? m.format('x') : null
});
});

Cypress.Commands.add('expireSkills', () => {
cy.logout();
cy.resetEmail();
Expand Down
17 changes: 17 additions & 0 deletions service/src/main/java/skills/skillLoading/SkillsLoader.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import skills.services.GlobalBadgesService
import skills.services.LevelDefinitionStorageService
import skills.services.admin.SkillTagService
import skills.services.admin.SkillsGroupAdminService
import skills.services.admin.UserAchievementExpirationService
import skills.services.admin.UserCommunityService
import skills.services.admin.skillReuse.SkillReuseIdUtil
import skills.services.attributes.BonusAwardAttrs
Expand Down Expand Up @@ -162,6 +163,9 @@ class SkillsLoader {
@Autowired
SkillAttributeService skillAttributeService

@Autowired
ExpiredUserAchievementRepo expiredUserAchievementRepo

@Autowired
TaskConfig taskConfig

Expand Down Expand Up @@ -528,10 +532,17 @@ class SkillsLoader {
ExpirationAttrs expirationAttrs = skillAttributeService.getExpirationAttrs(projectId, skillId)
Date expirationDate
Date mostRecentlyPerformedOn
Date lastExpirationDate
int daysOfInactivityBeforeExp = 0
Boolean isMotivationalSkill = false
if (expirationAttrs) {
expirationDate = expirationAttrs.nextExpirationDate
if(!achievedOn) {
def expiredSkill = expiredUserAchievementRepo.findMostRecentExpirationForSkill(projectId, userId, skillId)
if (expiredSkill) {
lastExpirationDate = expiredSkill.expiredOn
}
}
isMotivationalSkill = expirationAttrs?.expirationType == ExpirationAttrs.DAILY
if (isMotivationalSkill) {
UserPerformedSkill mostRecentUPS = userPerformedSkillRepo.findTopBySkillRefIdAndUserIdOrderByPerformedOnDesc(skillDef.id, userId)
Expand Down Expand Up @@ -612,6 +623,7 @@ class SkillsLoader {
isMotivationalSkill: isMotivationalSkill,
daysOfInactivityBeforeExp: daysOfInactivityBeforeExp,
mostRecentlyPerformedOn: mostRecentlyPerformedOn,
lastExpirationDate: lastExpirationDate
)
}

Expand Down Expand Up @@ -1226,11 +1238,15 @@ class SkillsLoader {
Date achievedOn = achievedLevelRepository.getAchievedDateByUserIdAndProjectIdAndSkillId(userId, skillDef.projectId, skillDef.skillId)
Date expirationDate
Date mostRecentlyPerformedOn
Date lastExpirationDate
int daysOfInactivityBeforeExp = 0
Boolean isMotivationalSkill = false
if (skillDefAndUserPoints.attributes && skillDefAndUserPoints.attributes.type == SkillAttributesDef.SkillAttributesType.AchievementExpiration) {
ExpirationAttrs expirationAttrs = skillAttributeService.convertAttrs(skillDefAndUserPoints.attributes, ExpirationAttrs)
expirationDate = expirationAttrs.nextExpirationDate
if(!achievedOn) {
lastExpirationDate = skillDefAndUserPoints.expiredOn
}
isMotivationalSkill = expirationAttrs?.expirationType == ExpirationAttrs.DAILY
if (isMotivationalSkill) {
UserPerformedSkill mostRecentUPS = userPerformedSkillRepo.findTopBySkillRefIdAndUserIdOrderByPerformedOnDesc(skillDef.id, userId)
Expand Down Expand Up @@ -1273,6 +1289,7 @@ class SkillsLoader {
isMotivationalSkill: isMotivationalSkill,
daysOfInactivityBeforeExp: daysOfInactivityBeforeExp,
mostRecentlyPerformedOn: mostRecentlyPerformedOn,
lastExpirationDate: lastExpirationDate
)
}
}
Expand Down
23 changes: 23 additions & 0 deletions service/src/main/java/skills/skillLoading/SubjectDataLoader.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import skills.services.settings.SettingsService
import skills.skillLoading.model.SkillDependencySummary
import skills.skillLoading.model.SkillTag
import skills.storage.model.*
import skills.storage.repos.ExpiredUserAchievementRepo
import skills.storage.repos.QuizToSkillDefRepo
import skills.storage.repos.SkillApprovalRepo
import skills.storage.repos.SkillDefRepo
Expand Down Expand Up @@ -66,6 +67,9 @@ class SubjectDataLoader {
@Autowired
SkillApprovalRepo skillApprovalRepo

@Autowired
ExpiredUserAchievementRepo expiredUserAchievementRepo

static class SkillsAndPoints {
SkillDef skillDef
int points
Expand All @@ -85,6 +89,7 @@ class SubjectDataLoader {
List<SimpleBadgeRes> badges = []
List<SkillTag> tags = []
SkillAttributesDef attributes
Date expiredOn
}

static class SkillsData {
Expand Down Expand Up @@ -140,6 +145,7 @@ class SubjectDataLoader {
skillsAndPoints = handleBadges(projectId, skillsAndPoints)
skillsAndPoints = handleSkillTags(projectId, skillsAndPoints)
skillsAndPoints = handleSkillQuizInfo(projectId, skillsAndPoints)
skillsAndPoints = handleSkillExpirations(projectId, userId, skillsAndPoints)

new SkillsData(childrenWithPoints: skillsAndPoints)
}
Expand Down Expand Up @@ -172,6 +178,23 @@ class SubjectDataLoader {
return skillsAndPoints;
}

private List<SkillsAndPoints> handleSkillExpirations(String projectId, String userId, List<SkillsAndPoints> skillsAndPoints) {
if(projectId) {
List<String> skillIds = collectSkillIds(skillsAndPoints)
def expiredSkills = expiredUserAchievementRepo.findMostRecentExpirationForAllSkills(projectId, userId, skillIds)
if (expiredSkills) {
skillsAndPoints.each { it ->
def expirations = expiredSkills.findAll{skill -> skill.skillId == it.skillDef.skillId}
if(expirations) {
expirations?.sort { skill -> skill.expiredOn }
it.expiredOn = expirations.first()?.expiredOn
}
}
}
}
return skillsAndPoints
}

@Profile
private List<SkillsAndPoints> handleSkillTags(String projectId, List<SkillsAndPoints> skillsAndPoints) {
if(projectId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ class SkillSummary extends SkillSummaryParent {
int daysOfInactivityBeforeExp
Date mostRecentlyPerformedOn
Date expirationDate
Date lastExpirationDate
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import org.springframework.data.domain.PageRequest
import org.springframework.data.jpa.repository.Modifying
import org.springframework.data.jpa.repository.Query
import org.springframework.data.repository.CrudRepository
import org.springframework.lang.Nullable
import org.springframework.data.repository.query.Param
import skills.controller.result.model.ExpiredSkillRes
import skills.storage.model.ExpiredUserAchievement
Expand Down Expand Up @@ -82,4 +83,20 @@ interface ExpiredUserAchievementRepo extends CrudRepository<ExpiredUserAchieveme
@Param("userFilter") String userFilter,
@Param("skillNameFilter") String skillNameFilter, PageRequest pageRequest)

@Query(value = '''
SELECT eua
FROM ExpiredUserAchievement eua
WHERE eua.projectId = :projectId AND eua.skillId = :skillId AND eua.userId = :userId
ORDER BY eua.expiredOn DESC LIMIT 1
''')
@Nullable
ExpiredUserAchievement findMostRecentExpirationForSkill(@Param("projectId") String projectId, @Param("userId") String userId, @Param("skillId") String skillId)

@Query(value = '''
SELECT eua
FROM ExpiredUserAchievement eua
WHERE eua.projectId = :projectId AND eua.skillId IN (:skills) AND eua.userId = :userId
''')
@Nullable
List<ExpiredUserAchievement> findMostRecentExpirationForAllSkills(@Param("projectId") String projectId, @Param("userId") String userId, @Param("skills") List<String> skills)
}
Loading

0 comments on commit 8bf0a8a

Please sign in to comment.