Skip to content

Commit

Permalink
feat: update designs and optimize tasks
Browse files Browse the repository at this point in the history
  • Loading branch information
Ali Salman authored and Ali Salman committed Nov 22, 2023
1 parent f7de918 commit 3850f48
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 6 deletions.
9 changes: 4 additions & 5 deletions lms/djangoapps/badges/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from lms.djangoapps.badges.events.course_meta import award_enrollment_badge
from lms.djangoapps.badges.models import BadgeAssertion, LeaderboardConfiguration, LeaderboardEntry
from lms.djangoapps.badges.utils import badges_enabled, calculate_score
from lms.djangoapps.badges.tasks import update_leaderboard_enties


@receiver(ENROLL_STATUS_CHANGE)
Expand Down Expand Up @@ -55,12 +56,10 @@ def update_leaderboard_entry(sender, instance, **kwargs):
def update_leaderboard_scores(sender, instance, **kwargs):
"""
Update scores for all entries when LeaderboardConfiguration is updated
Intiate a Celery task as the update could be time intensive.
"""
leaderboard_entries = LeaderboardEntry.objects.all()
course_badge_score, event_badge_score = instance.course_badge_score, instance.event_badge_score
if not instance.enabled:
course_badge_score, event_badge_score = instance.COURSE_BADGE_SCORE, instance.EVENT_BADGE_SCORE

leaderboard_entries.update(
score=F('course_badge_count') * course_badge_score + F('event_badge_count') * event_badge_score
)

update_leaderboard_enties.delay(course_badge_score, event_badge_score)
3 changes: 3 additions & 0 deletions lms/djangoapps/badges/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,3 +388,6 @@ class LeaderboardEntry(models.Model):

def __str__(self):
return f"LeaderboardEntry for {self.user.username}"

class Meta:
app_label = "badges"
28 changes: 28 additions & 0 deletions lms/djangoapps/badges/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
Defines asynchronous celery task for updateing leaderboard entries
"""
import logging

from django.db.models import F
from celery import shared_task
from celery_utils.logged_task import LoggedTask
from edx_django_utils.monitoring import set_code_owner_attribute
from lms.djangoapps.badges.models import BadgeAssertion, LeaderboardConfiguration, LeaderboardEntry


log = logging.getLogger(__name__)


@shared_task(base=LoggedTask)
@set_code_owner_attribute
def update_leaderboard_enties(course_badge_score, event_badge_score):
"""
Bulk Update scores for all entries in the LeaderboardEntry
"""
leaderboard_entries = LeaderboardEntry.objects.all()
leaderboard_entries.update(
score=F('course_badge_count') * course_badge_score + F('event_badge_count') * event_badge_score
)
log.info(
f"Updated {leaderboard_entries.count()} enties in the LeaderboardEntry table"
)
19 changes: 19 additions & 0 deletions lms/static/images/stars.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion lms/static/js/dashboard/badges.js

This file was deleted.

85 changes: 85 additions & 0 deletions lms/static/js/dashboard/leaderboard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Function to fetch data from the API
async function fetchData(url) {
try {
const response = await fetch(url);
const data = await response.json();
return data; // Assuming the API response is in JSON format
} catch (error) {
console.error('Error fetching data:', error);
}
}

// Function to render a single user item
function renderUserListItem(data) {
const listItem = document.createElement('li');
listItem.className = 'user-item';

const avatarDiv = document.createElement('div');
avatarDiv.className = 'avatar';
const avatarImg = document.createElement('img');
avatarImg.src = data.user.profile_image_url;
avatarImg.alt = 'User Avatar';
avatarDiv.appendChild(avatarImg);

const userInfoDiv = document.createElement('div');
userInfoDiv.className = 'user-info';
const userNameDiv = document.createElement('div');
userNameDiv.className = 'user-name';
userNameDiv.textContent = data.user.name;
userInfoDiv.appendChild(userNameDiv);

const userScoreDiv = document.createElement('div');
userScoreDiv.className = 'user-score';
userScoreDiv.textContent = data.score;

listItem.appendChild(avatarDiv);
listItem.appendChild(userInfoDiv);
listItem.appendChild(userScoreDiv);

return listItem;
}

// Function to render user list
async function renderUserList() {
const userListElement = document.getElementById('userList');
let nextPageUrl = '/api/badges/v1/leaderboard/';

// Variable to track if data is currently being fetched to avoid multiple simultaneous requests
let fetchingData = false;

async function fetchAndRenderNextPage() {
fetchingData = true;

// Fetch the next set of data
if (nextPageUrl){
const nextPageData = await fetchData(nextPageUrl);

if (nextPageData.results && Array.isArray(nextPageData.results)) {
nextPageData.results.forEach(user => {
// Create and append list items for the next set of data
const listItem = renderUserListItem(user);
userListElement.appendChild(listItem);
});

// Update the next page URL
nextPageUrl = nextPageData.next;
}

fetchingData = false;
}
}

// Initial rendering
await fetchAndRenderNextPage();

// Add event listener to window scroll
window.addEventListener('scroll', async () => {
// Check if user has scrolled to the bottom
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 1000 && !fetchingData) {
await fetchAndRenderNextPage();
}
});
}

// Call the function to render the initial user list when the page loads
document.addEventListener('DOMContentLoaded', renderUserList);
3 changes: 3 additions & 0 deletions lms/static/sass/_build-lms-v1.scss
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@
@import 'features/content-type-gating';
@import 'features/course-duration-limits';

// sdaia features
@import 'features/leaderboard';

// search
@import 'search/search';
@import 'search/instant-search';
Expand Down
84 changes: 84 additions & 0 deletions lms/static/sass/features/_leaderboard.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
.leaderboard {
margin: 20px;
background-color: #fff;
border-radius: 8px;
padding: 10px;
border: 1px solid #D9D9D9;


.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
overflow: hidden;
}

.avatar img {
width: 100%;
height: 100%;
object-fit: cover;
}

.leaderboard-header {
font-size: 1.1em;
padding: 10px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #D9D9D9;

.star-icon {
width: 25%;
}
}

.header {
list-style: none;
overflow-y: auto;
padding: 0px 10px 0px 10px;
margin: 0px;

.header-item {
display: flex;
align-items: center;
justify-content: space-between;
color: #7C7C7C;

.header-info {
flex: 1;
}
}
}

.user-list {
list-style: none;
max-height: 300px; /* Set your desired maximum height */
overflow-y: auto;
padding: 0px 10px 0px 10px;
margin: 0px;

.user-item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;

.user-info {
flex: 1;
margin-right: 10px;
margin-left: 10px;
}

.user-name {
font-weight: 600;
color: #1C355E;
}

.user-score {
color: #EA6852;
font-size: 1em; /* Adjust font size if necessary */
}

}
}
}
21 changes: 21 additions & 0 deletions lms/templates/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,25 @@ <h3>${course_dir}</h3>
<div id="dashboard-search-results" class="search-results dashboard-search-results"></div>
% endif

<div class="leaderboard">
<div class="leaderboard-header">
<div class="title">${_('Leaderboard')}</div>
<!-- <span class="icon fa fa-remove" aria-hidden="true"></span> -->
<img class="star-icon" src="${static.url('images/stars.svg')}" alt="Leaderboard">
</div>
<ul class="header">
<li class="header-item">
<div class="avatar"></div>
<div class="header-info">
<div class="col-name">${_('name')}</div>
</div>
<div class="col-score">${_('score')}</div>
</li>
</ul>
<ul class="user-list" id="userList">
</ul>
</div>

<%block name="skip_links">
% if settings.FEATURES.get('ENABLE_ANNOUNCEMENTS'):
<a id="announcements-skip" class="nav-skip sr-only sr-only-focusable" href="#announcements">${_("Skip to list of announcements")}</a>
Expand Down Expand Up @@ -401,4 +420,6 @@ <h2 id="unenrollment-modal-title">
</div>
</div>

<script src="lms/static/js/dashboard/leaderboard.js"></script>

<%include file="dashboard/_dashboard_entitlement_unenrollment_modal.html"/>

0 comments on commit 3850f48

Please sign in to comment.