Individual Blog (Ian):


Scribble Personal Story Issue
Scribble Burndown List
Scribble Khanban Board & Code Overview

Major Commits:

  • Built API (api/competition.py) that fetches username based on a JWT authentication token from login, amount of time taken from fetching through api/competition/timer, and word given.

  • Made model file (model/competition.py) to create a seperate db tables inside of user_management.db to store information with fetched username, time, and given word, storing information inside database.

  • Developed Deployment Blog with port 8203, overview of setup on backend page (scribble.stu.nighthawkcodingsociety.com) [Assistant Deployment Admin]

  • Made navigation bar and nav to main homepage, styled individual page + deployment blog, changed readability on site. [Frontend Developer]

Minor Commits:

  • Changed overall JWT Token request from jwt_authorize.py to fetch user_name details and formatting.

  • Adjusted port on makefile, put files such as /sass-cache/ into .gitignore to help with github deployment.

  • Made issue on 4.2, 4.3 about fault tolerances and computing models (Sequential, Parallel, Distributed, Efficiency Comparisons).

Features Showcase:

Description

  • Competition is a timed drawing game that fetches users JSON data into an api (competition.py) and updates/stores progress after timer ends, such as the users (profile name, time taken, and a user’s given word).

  • The user’s saved data is stored within a separate model file named competition.py, which creates a separate db_table under user_manangement.db with separate variables from Competition.

CPT Requirements:


Component Competency (What It Does) Performance (How It Works) Task (Implementation Details) Correlations
Page Layout & Metadata Defines page structure and access requirements Uses layout: needsAuth to restrict access; sets title: Competition Ensures only authenticated users can access the competition page Frontend: Layout design; Backend: Access control; Security and Authentication: Authentication handling; HTTP & RESTful APIs: Request filtering
Timer Controls Allows users to manage a countdown timer Inputs a duration, starts/stops the timer, and updates the display Uses #timerDuration, #startTimer, and #stopTimer buttons Frontend: Timer controls UI; Backend: Timer management APIs; HTTP & RESTful APIs: Timer state handling
Timer Display Displays remaining time dynamically Updates text inside #timerDisplay using JavaScript Calls updateTimerDisplay() to refresh every second Frontend: JavaScript for updating DOM; Backend: Timer synchronization if needed
Save Drawing Allows users to save their drawings Converts the canvas to a PNG image and downloads it Uses toDataURL('image/png') and <a> tag for download Frontend: Image conversion and download handling; Image Upload and Storage: Storing drawings on server-side if needed
Results Table Displays competition results Fetches and populates user entries from an API Uses fetchResults() to update #resultsBody dynamically Frontend: DOM population; Backend: Results API; Database Management with SQLite: Storing competition results
Timer API Integration Communicates with backend to start/stop timer Sends POST, PUT, and GET requests to /api/competition/timer Uses fetch() with credentials: 'include' for authentication Backend: Timer management API; Postman Testing: API testing for timer operations; HTTP & RESTful APIs: API interaction for timer control
Error Handling Displays errors to the user Shows and hides an error message div dynamically Uses showError(message) to alert users of issues Frontend: Error handling UI; Backend: Error handling in API responses; Monitoring and Logging: Logging errors for troubleshooting

Procedure Steps

  1. Initialize Repeated Execution:
    • Start a recurring interval (setInterval) that executes every 1000ms (1 second).
  2. Perform API Request:
    • Send an asynchronous HTTP GET request to the competition timer API endpoint (/api/competition/timer).
    • Include credentials ('include') for authentication/session handling.
  3. Process API Response:
    • Await the API response and parse it into JSON format.
    • If the response status is not OK (!response.ok), throw an error with the server-provided message.
  4. Update Timer Display:
    • Extract time_remaining from the API response.
    • Modify the DOM element with ID timerDisplay to display the remaining time.
  5. Evaluate Timer State:
    • If is_active is false, execute the following actions:
      • Stop the interval (clearInterval(timerInterval)).
      • Enable the “Start Timer” button (#startTimer).
      • Disable the “Stop Timer” button (#stopTimer).
  6. Error Handling:
    • If any error occurs, log the error to the console.
    • Stop the interval (clearInterval(timerInterval)) to prevent redundant API calls.

Code Implementation

async function updateTimerDisplay() {
    timerInterval = setInterval(async () => {
        try {
            const response = await fetch(`${pythonURI}/api/competition/timer`, {
                method: 'GET',
                credentials: 'include'
            });

            const data = await response.json();
            if (!response.ok) throw new Error(data.message);

            document.getElementById('timerDisplay').textContent = 
                `Time: ${data.time_remaining}s`;

            if (!data.is_active) {
                clearInterval(timerInterval);
                document.getElementById('startTimer').disabled = false;
                document.getElementById('stopTimer').disabled = true;
            }

        } catch (error) {
            console.error('Error:', error);
            clearInterval(timerInterval);
        }
    }, 1000);
}

Requirements & Constraints

  • The function must be executed in a browser environment.
  • The API endpoint (/api/competition/timer) must be accessible and return a valid JSON response.
  • The target DOM elements (#timerDisplay, #startTimer, #stopTimer) must exist before function execution.
  • The function must handle network errors and API failures gracefully.
  • setInterval must be cleared when is_active is false to prevent unnecessary API calls.

User Results

Objective

To fetch competition result data from an API and update the user interface dynamically.

Procedure Steps

  1. Perform API Request:
    • Send an asynchronous HTTP GET request to the competition results API endpoint (/api/competition/times).
    • Include credentials ('include') for authentication/session handling.
  2. Validate API Response:
    • Check if the response status is OK (response.ok).
    • If not, throw an error with the message 'Failed to fetch results'.
  3. Process API Data:
    • Parse the JSON response and extract the list of result entries.
    • Select the target table body element (#resultsBody).
    • Clear any existing content in the table body.
  4. Populate Table with Results:
    • Iterate through each entry in the response data.
    • Create a new table row (<tr>) for each entry.
    • Insert the following data into the corresponding cells (<td>):
      • User’s Name (users_name) or 'Unknown' if not available.
      • Time Taken (time_taken) or '0' if not available.
      • Drawn Word (drawn_word) or 'Unknown' if not available.
  5. Error Handling:
    • If any error occurs during execution:
      • Log the error message to the console.
      • Display an alert to inform the user that the fetch operation failed.
async function fetchResults() {
    try {
        const response = await fetch(`${pythonURI}/api/competition/times`, {
            credentials: 'include'
        });

        if (!response.ok) {
            throw new Error('Failed to fetch results');
        }

        const entries = await response.json();
        const tbody = document.getElementById('resultsBody');
        tbody.innerHTML = '';

        entries.forEach(entry => {
            const row = tbody.insertRow();
            row.insertCell().textContent = entry.users_name || 'Unknown'; // Changed from profile_name
            row.insertCell().textContent = entry.time_taken || '0';
            row.insertCell().textContent = entry.drawn_word || 'Unknown'; // Changed from word_drawn
        });

    } catch (error) {
        console.error('Error:', error);
        alert('Failed to fetch results: ' + error.message);
    }
}

// Initial fetch of results
document.addEventListener('DOMContentLoaded', fetchResults);

Requirements & Constraints

  • The function must be executed in a browser environment.
  • The API endpoint (/api/competition/times) must be accessible and return a valid JSON response.
  • The target DOM element (#resultsBody) must exist before function execution.
  • The function must handle network errors and API failures gracefully.
  • Data fields (users_name, time_taken, drawn_word) must be validated to prevent rendering issues.

Admin Panel, Delete Records & restore:

Objective

To display and manage competition data in a structured table, allowing admins to delete entries.

Procedure Steps

1. Template Inheritance & Styling

  • Extend the base template (layouts/base.html).
  • Apply CSS styles to improve the UI, including:
    • White background with padding and shadow for the container.
    • Speed factor color coding based on performance.

2. Render Competition Data in a Table

  • Create a responsive HTML table (#competitionTable).
  • Populate rows dynamically using competition_data.
  • Display the following fields:
    • ID
    • Player Name (users_name)
    • Drawing Word (drawn_word)
    • Timer Duration (timer_duration)
    • Time Taken (time_taken)
    • Speed Factor (timer_duration / time_taken)
    • Date Created (date_created)
  • Conditionally display the Delete button for Admin users.

3. Admin-Only Delete Functionality

  • If the user role is 'Admin', enable the Delete button.
  • Use JavaScript to:
    • Attach click events to .delete-btn.
    • Confirm deletion before sending an API request.
    • Send a DELETE request to /api/competition/times with the entry ID.
    • Reload the page upon success or show an error message upon failure.

4. Initialize DataTable (for Enhanced Table Management)

  • Use jQuery DataTables to:
    • Enable sorting (default: by speed factor descending).
    • Set pagination (pageLength: 25).

5. Error Handling

  • If API deletion fails:
    • Log the error to the console.
    • Show an alert to notify the user.
  • If the request is successful:
    • Display a success message.
    • Refresh the page to reflect changes.
<style>
.container {
    background: white;
    padding: 2rem;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    margin-top: 2rem;
    margin-bottom: 2rem;
}

.table {
    background: white;
}

.speed-factor {
    font-weight: bold;
    color: #2196F3;
}

.speed-high {
    color: #4CAF50;
}

.speed-medium {
    color: #FFC107;
}

.speed-low {
    color: #FF5722;
}
</style>

<div class="container">
    <h1 class="mb-4">Competition Management</h1>
    <table class="table table-striped" id="competitionTable">
        <thead>
            <tr>
                <th>ID</th>
                <th>Player</th>
                <th>Drawing Word</th>
                <th>Timer Duration</th>
                <th>Time Taken</th>
                <th>Speed Factor</th>
                <th>Date Created</th>
                
            </tr>
        </thead>
        <tbody>
            
        </tbody>
    </table>
</div>
<script>
$(document).ready(function() {
    // Initialize DataTable
    $("#competitionTable").DataTable({
        order: [[5, 'desc']], // Sort by speed factor by default
        pageLength: 25
    });

    // Handle delete button clicks
    $(document).on("click", ".delete-btn", function() {
        var id = $(this).data("id");
        
        if (!confirm("Are you sure you want to delete this competition entry?")) {
            return;
        }

        fetch("/api/competition/times", {
            method: "DELETE",
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ id: id })
        })
        .then(response => response.json())
        .then(data => {
            if (data.message) {
                alert(data.message);
                location.reload();
            } else {
                alert(data.error || "Operation failed");
            }
        })
        .catch(error => {
            console.error('Error:', error);
            alert("Failed to process request");
        });
    });
});
</script>

Requirements & Constraints

  • Must be used within a Flask/Jinja templating environment.
  • The competition_data variable must be populated before rendering.
  • The current_user.role variable must be available to determine Admin access.
  • JavaScript/jQuery must be enabled for table sorting and delete functionality.
  • The API endpoint /api/competition/times must support DELETE requests with an entry ID.

N@TM Feedback:

During N@TM, we recieved a moderate feedback in regards to our site about the functionality of Scribble and the purpose of the project. When people tried to use our site, most people were able to figure out how to work the site, however didn’t understand its efficiency and the general purpose of the website.

N@TM IMG

What we could do in the future of fixing this issue is adding a purpose for the website at the main page (our index.md) instead of a regular drawing board at the start of the website. This way people are able to get a “semi-tutorial” of the website and what each link does with the game.

N@TM Prism

A good example of how we’d fix this problem was with a group called Prism, where they put an explanation of their grading function while it was running. The page was simple without any hard navigation, and people were able to get the purpose of their website without any questions, as the idea and functions were basic. For example, their website was able to fit CPT requirements due to the idea of grading systems, and how admins can create or delete a students score, giving a purpose for teachers and a goal for students. Contrast to Scribble, although our website was able to fulfill CPT requirements in a way, my contribution of implementing competition between players may be confusing for other players and doesn’t have a clear goal overall.

N@TM IMG


MCQ Reflection
Plans For Next Trimester

Self-Assessment

Overall in this trimester, I felt that even though I was able to help organize the page and have my general features running and connected to one another, most of my commits were primarly focused on one specific assessments, which wasted the amount of things I was able to do and made my work habits less productive. Because of this, I began to fall a bit behind with making an individual admin page for my website, making deployment blogs for the website, and fixing and adjusting navigation pages.

Criteria Description Score
5 things you did over 12 weeks, Issues, burndown, presentation Committed, however focused too much on one subject that it limited how much work I did for others in regards to my role as Assistant Deployment Admin. 3.9/5
Full Stack Project Demo, including CPT requirement highlights, and N@tM feedback Highlights CPT requirements and spands Full Stack demo, however CPT requirements are choppy and wordy. 1.8/2
Project Feature blog write up, using CPT/FRQ language Feature blog showing each feature coorelating with CPT requirements (ex: frontend to backend [big idea 1.4], and HTTPS requests [big idea 4.0]) 0.9/1
MCQ Reviewed 2020 MCQ review, went over problems missed and went over the overall section of the error and what to look out for. 0.9/1
10th point Mentioned strengths and weaknesses, plans on going forward but not much depth. 0.5/1
Overall   8.5/10

Looking further I’ll attempt to become more productive with tasks and understand what i’m doing as well. This could be done with making more effective burndown lists at the start, and mark things down with what is achievable and what isn’t.