Difference between revisions of "Widget:MerlinProficiencyProgress"

From Coder Merlin
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
<noinclude>
<script type="module">
Runs the JavaScript for the progress HTML table.
import * as merlinapi from "/wiki/merlin_extensions/merlin-api.mjs";
</noinclude>


<includeonly>
const userName = "<!--{$userName}-->".toLowerCase();
<script>
const sessionID = "<!--{$sessionID}-->";
// Wait for the other scripts to be ready because we need jQuery
 
window.addEventListener('load', function () {
function MasteryProgressModel(data) {
   
    this.masteryProgresses = ko.observableArray(data);
    /**
}
    * Add a row to a table
    *
    * @param {jQuery} $table The jQuery object for the table element
    * @param  {Array} cells An array of cells to add to the table
    * @param  {Object} attrs Any attributes to set on the row
    * @return {jQuery} The row jQuery object
    */
    function addRowToTable($table, cells, attrs = {}) {
        // Figure out tbody
        const $tbody = $table.find('tbody');
       
        // Create an empty row
        let $row = $(`<tr></tr>`);


        // Iterate over each cell
// Activates knockout.js
        for (let i = 0; i < cells.length; i++) {
function init(event) {
            // Get the cell
    let successHandler = function (data) {
            const cell = cells[i];
        data.sort((left, right) => {
           
return left.masteryProgramTopicSequence > right.masteryProgramTopicSequence ? 1 :
            let $cell = null;
      (left.masteryProgramTopicSequence < right.masteryProgramTopicSequence ? -1 :
           
      0)
            // Check if it should be a th or td
    });
            if (cell.header) {
    let model = new MasteryProgressModel(data);
                $cell = $('<th scope="row"></th>');
    ko.applyBindings(model);
            } else {
  }
                $cell = $('<td></td>');
  let errorHandler = function(error) {
            }
      console.error(error.message);
           
  }
            // Set any necessary attributes
  merlinapi.MasteryProgressModel.load(successHandler, errorHandler, userName, sessionID);
            if (cell.attributes && (typeof cell.attributes == 'object')) {
}
                for (let [key, value] of Object.entries(cell.attributes)) {
     
                    // Combine into a string if needed
window.addEventListener("load", (event) => {
                    if (Array.isArray(value)) {
  init(event)
                        value = value.join(' ');
                    }
                   
                    // Set the attribute
                    $cell.attr(key, value);
                }
            }
           
            // Set the cell data
            $cell.html(cell.data);
           
            // Add to the cell to the row
            $row.append($cell);
        }
 
        // Add attributes to the row
        for (const [key, value] of Object.entries(attrs)) {
            $row.attr(key, value);
        }
       
        $tbody.append($row);
 
        return $row;
    }
 
    // Fetch and process the data
    async function getData() {
        // Staging or production
        let baseUrl = "https://" + (window.location.hostname == 'www.codermerlin.com' ? 'api-server.codermerlin.com' : 'api-server-stg.codermerlin.com');
 
        // Add the path
        const username = "<!--{$userName}-->".toLowerCase();
        const sessionId = "<!--{$sessionID}-->";
        const path = `/mission-manager/users/${username}/mastery-progress/programs`;
 
        // Development
        let json;
        if (window.location.hostname == "") {
        } else {
            // Request the data
            json = await $.ajax(baseUrl + path, {
                headers: {
                    username: username,
                    sessionId: sessionId
                },
                dataType: "json"
            });
        }
 
        let rawData = json;
 
        // Re-order everything by the sequence
        const orderKey = 'masteryProgramTopicSequence';
 
        rawData.rows.sort(function(a, b) {
            if (a[orderKey] > b[orderKey]) {
                return -1;
            } else if (a[orderKey] < b[orderKey]) {
                return 1;
            } else {
                return 0;
            }
        });
 
        let data = [];
 
        const stages = ['Inevident', 'Emerging', 'Developing', 'Proficient', 'Exemplary'];
 
        for (let i = 0; i < rawData.rows.length; i++) {
            // Get the row
            let row = rawData.rows[i];
 
            // Extract the necessary info
            const points = row['pointsEarned'];
            let dataRow = {};
 
            dataRow['topic'] = row['masteryProgramTopicName'];
            dataRow['data'] = [];
           
            // Iterate over the stages
            for (let j = 0; j < stages.length; j++) {
                // Build the key for the minimum points to get to a stage
                let stageKey = stages[j].toLowerCase() + 'MinimumPoints';
 
                // The minimum points
                const minPoints = row[stageKey];
 
                // Calculate the percentage complete
                let pct = points / minPoints;
 
                // If the last stage isn't complete, the next one can't be either
                if (j > 0 && dataRow['data'][j - 1]['pct'] < 1.0) {
                    pct = 0.0;
                }
 
                let formattedPct = (pct > 0) ? Math.round(pct * 100) + '%' : '';
 
                dataRow['data'].push({
                    date: (points >= minPoints) ? 'Passed' : formattedPct,
                    pct: pct
                });
            }
 
            data.push(dataRow);
        }
 
 
        return data;
    }
 
    // Wait for the DOM
    $(function () {
        // Get a table instance
        let $table = $('table#mastery-progress-table');
       
        // Fetch the data
        let $rows = [];
        getData().then(async function (data) {
            // Iterate over each topic
            for (let i = 0; i < data.length; i++) {
                let topic = data[i];
 
                let cells = [];
 
                // Set the first row to the title
                cells.push({
                    header: true,
                    data: topic.topic,
                    attributes: {
                        style: `background-color: ${topic.data[0].date ? 'lightgreen' : 'lightgray'};`
                    }
                });
 
                // Iterate over the rest of the data
                for (let j = 0; j < topic.data.length; j++) {
                    let stage = topic.data[j];
 
                    // Calculate the RGB green value
                    const m = 15;
                    const greenColor = ((j + 1) * m) + (240 - (m * topic.data.length));
 
                    // Get the percentage rounded to 2 decimal places
                    //  4 because a percentage is a decimal anyways
                    const pct = parseFloat(stage.pct).toFixed(4);
 
                    // Background color for the cell
                    let backgroundColor = `rgb(0, ${greenColor}, 0)`;
 
                    // If not complete, the background should be blue
                    if (pct < 1.0) {
                        backgroundColor = "rgb(0, 167, 225)";
                    }
                   
                    // Append
                    cells.push({
                        header: false,
                        data: stage.date,
                        attributes: {
                            //style: `background-color: ${(stage.date) ? `rgb(0, ${greenColor}, 0)` : 'lightgray'}; text-align: center;`,
                            style: `background-image: linear-gradient(to right, ${backgroundColor} ${pct * 100}%, lightgray 0%); text-align: center;`
                        }
                    });
                }
 
                // Add to the table
                let $row = addRowToTable($table, cells, {style: 'opacity: 0.0;'});
 
                $rows.push($row);
            }
 
            for (let i = 0; i < $rows.length; i++) {
                let $row = $rows[i];
 
                // Animate
                const animationTime = 200;
 
                $row.animate({opacity: 1.0}, animationTime);
 
                // Wait for half of the animation
                await (new Promise(resolve => setTimeout(resolve, animationTime / 5)));
            }
        });
    });
});
});
</script>
</script>
</includeonly>
 
<table class="merlin-proficiency-progress-table" style="display: none" data-bind="visible: true">
    <thead>
    <tr>
        <th>Program Level Name</th>
        <th>Emerging</th>
        <th>Developing</th>
        <th>Proficient</th>
        <th>Exemplary</th>
      </tr>
    </thead>
    <tbody data-bind="foreach: masteryProgresses">
        <tr>
        <td data-bind="style: {'background-color': color()}, click: toggleMissionDisplay" class="merlin-proficiency-progress-program-level-name">
            <span data-bind="text: arrowInnerText()"></span>
            <span data-bind="text: standardName()"></span>
                <ul data-bind="style: {'display': (shouldShowMissions()) ? 'block' : 'none'},foreach: missions">
                <li data-bind="text: standardName()"></li>
                </ul>
        </td>
            <td><span data-bind="class: emergingClassName()"></span></td>
            <td><span data-bind="class: developingClassName()"></span></td>
            <td><span data-bind="class: proficientClassName()"></span></td>
            <td><span data-bind="class: exemplaryClassName()"></span></td>
        </tr>
    </tbody>
</table>

Latest revision as of 20:11, 5 April 2023

<script type="module"> import * as merlinapi from "/wiki/merlin_extensions/merlin-api.mjs";

const userName = "".toLowerCase(); const sessionID = "";

function MasteryProgressModel(data) {

   this.masteryProgresses = ko.observableArray(data);

}

// Activates knockout.js function init(event) {

   let successHandler = function (data) {
       data.sort((left, right) => {

return left.masteryProgramTopicSequence > right.masteryProgramTopicSequence ? 1 : (left.masteryProgramTopicSequence < right.masteryProgramTopicSequence ? -1 : 0) }); let model = new MasteryProgressModel(data); ko.applyBindings(model); } let errorHandler = function(error) { console.error(error.message); } merlinapi.MasteryProgressModel.load(successHandler, errorHandler, userName, sessionID); }

window.addEventListener("load", (event) => { init(event) }); </script>

<thead> </thead> <tbody data-bind="foreach: masteryProgresses"> </tbody>