Skip to the content.

Final


Full Stack Project, CPT Requirements:

Input:

My code recieves user input when:

  • Adding a reaction
    
      document.getElementById('addEmotionForm').addEventListener('submit', async (e) => {
          e.preventDefault();
          const userId = document.getElementById('userId').value;
          const bookDropdown = document.getElementById('bookDropdown');
          const selectedOption = bookDropdown.options[bookDropdown.selectedIndex];
    
          if (!selectedOption.value) {
              showStatusMessage('Please select a book.', false);
              return;
          }
    
          const bookData = JSON.parse(selectedOption.value);
          const bookId = bookData.id;
          const bookTitle = bookData.title;
          const authorId = bookData.author;
          const reactionType = document.getElementById('addReactionType').value;
    
          try {
              const response = await fetch(`${pythonURI}/api/emotion`, {
                  ...fetchOptions,
                  method: 'POST',
                  headers: { 'Content-Type': 'application/json' },
                  body: JSON.stringify({ user_id: userId, title_id: bookId, author_id: authorId, reaction_type: reactionType }),
              });
    
              if (!response.ok) throw new Error(await response.text());
    
              displayAddedEmotion(bookId, bookTitle, authorId, reactionType);
              showStatusMessage('Emotion successfully added!');
          } catch (error) {
              showStatusMessage(`Failed to add emotion: ${error.message}`, false);
          }
      });
    
  • Updating a reaction
    
      async function handleUpdateEmotion(event) {
          const userId = document.getElementById('userId').value;
          const emotionItem = event.target.closest('.emotion-item');
          const titleId = emotionItem.dataset.bookId;
          const authorId = emotionItem.dataset.authorId;
    
          const dropdown = document.createElement('select');
          dropdown.innerHTML = document.getElementById('addReactionType').innerHTML;
          dropdown.value = emotionItem.dataset.reaction;
    
          const submitButton = document.createElement('button');
          submitButton.textContent = 'Save';
          submitButton.onclick = async () => {
              const newReaction = dropdown.value;
              if (!newReaction) return;
    
              try {
                  const response = await fetch(`${pythonURI}/api/emotion/update`, {
                      ...fetchOptions,
                      method: 'PUT',
                      headers: { 'Content-Type': 'application/json' },
                      body: JSON.stringify({ user_id: userId, title_id: titleId, reaction_type: newReaction }),
                  });
    
                  if (!response.ok) throw new Error(await response.text());
    
                  emotionItem.dataset.reaction = newReaction;
                  emotionItem.querySelector('.reaction-text').textContent = newReaction;
                  showStatusMessage('Emotion successfully updated!');
    
                  emotionItem.innerHTML = `
                      <span>📖 <b>${emotionItem.dataset.bookTitle}</b> by ${authorId} — Reaction: <span class="reaction-text">${newReaction}</span></span>
                      <div>
                          <button class="update-button">📝</button>
                          <button class="delete-button">🗑️</button>
                      </div>
                  `;
    
                  emotionItem.querySelector('.update-button').addEventListener('click', handleUpdateEmotion);
                  emotionItem.querySelector('.delete-button').addEventListener('click', handleDeleteEmotion);
              } catch (error) {
                  showStatusMessage(`Failed to update: ${error.message}`, false);
              }
          };
    
  • Deleting a reaction
    async function handleDeleteEmotion(event) {
          const userId = document.getElementById('userId').value;
          const emotionItem = event.target.closest('.emotion-item');
          const titleId = emotionItem.dataset.bookId;
          const authorId = emotionItem.dataset.authorId;
    
          if (!confirm(`Delete emotion for "${emotionItem.dataset.bookTitle}"?`)) return;
    
          try {
              const response = await fetch(`${pythonURI}/api/emotion/delete`, {
                  ...fetchOptions,
                  method: 'DELETE',
                  headers: { 'Content-Type': 'application/json' },
                  body: JSON.stringify({ user_id: userId, title_id: titleId, author_id: authorId }),
              });
    
              if (!response.ok) throw new Error(await response.text());
    
              emotionItem.remove();
              showStatusMessage('Emotion successfully deleted!');
          } catch (error) {
              showStatusMessage(`Failed to delete: ${error.message}`, false);
          }
      }
    

Procedure:

This procedure adds a reaction, and has several parameters, including user_id, reaction_type, title_id, and author_id.

@emotion_api.route('', methods=['POST']) 
def add_emotion():
    data = request.json

    user_id = data.get("user_id")
    reaction_type = data.get("reaction_type")
    title_id = data.get("title_id")
    author_id = data.get("author_id")

    try:
        # Create and add the message_reaction
        message_emotion = Emotion(reaction_type=reaction_type, user_id=user_id, title_id=title_id, author_id=author_id)
        message_emotion.create()
        return jsonify({'message': 'Emotion added successfully to post'}), 201
    except Exception as e:
        return jsonify({'error': 'Failed to add emotion', 'message': str(e)}), 500

Algorithm:

One key algorithm in my program is the update function, which:

  1. Takes user input (including emoji selection) and validates it.
  2. Sends a PUT request to update the item in the backend.
  3. Receives a response and dynamically updates the UI without refreshing the page. It uses conditional statements to ensure only valid updates are processed and iteration to efficiently apply changes to the correct item.

Sequencing: function step by step

Functions execute in a structured order, ensuring that each step (e.g., fetching data → validating input → updating UI) follows logically.

document.addEventListener('DOMContentLoaded', async () => {
    // Step 1: Fetch books from API
    const books = await fetchBooks();  
    populateBookDropdown(books);  // Step 2: Populate dropdown
});

async function fetchBooks() {
    try {
        const response = await fetch(`${pythonURI}/api/wishlist/books`);
        if (!response.ok) throw new Error(`Failed to fetch books: ${response.status}`);
        return await response.json();  // Step 3: Return book data
    } catch (error) {
        console.error('Error:', error);
        return [];
    }
}

document.getElementById('addEmotionForm').addEventListener('submit', async (e) => {
    e.preventDefault();

    const userId = document.getElementById('userId').value;
    const bookDropdown = document.getElementById('bookDropdown');
    const selectedOption = bookDropdown.options[bookDropdown.selectedIndex];

    if (!selectedOption.value) {
        showStatusMessage('Please select a book.', false);
        return;
    }

    const bookData = JSON.parse(selectedOption.value);
    const reactionType = document.getElementById('addReactionType').value;

    try {
        // Step 4: Send request to add emotion
        const response = await fetch(`${pythonURI}/api/emotion`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                user_id: userId,
                title_id: bookData.id,
                author_id: bookData.author,
                reaction_type: reactionType,
            }),
        });

        if (!response.ok) throw new Error(await response.text());

        // Step 5: Update UI after successful API response
        displayAddedEmotion(bookData.id, bookData.title, bookData.author, reactionType);
        showStatusMessage('Emotion successfully added!');
    } catch (error) {
        showStatusMessage(`Failed to add emotion: ${error.message}`, false);
    }
});
  1. fetches books from backend
  2. populates the dropdown with books
  3. handle form submission and validate user input
  4. sends API request to add a reaction
  5. update the UI dynamically upon success

Selection: if else

  1. first if - checks if a book is selection, if not, an error is returned and procedure stops
  2. second if - chekcs if a reaction is selected, if not, execution is stopped
  3. inside try block - if request fails, an error is shown, if successful, the UI updates
  4. catch - it handles the failure if an error occures

Iteration: for loop

books_list = [{'id': book.id, 'title': book.title, 'author': book.author} for book in books]

This list comprehension in backend iterates over all the books retried from the database and formats them into a list of dictionaries.

List

I used a list for my initial data in the database:

emotions = [
        Emotion(user_id=1, reaction_type='😢', title_id="Catcher in the Rye", author_id="J.D."),
        Emotion(user_id=1, reaction_type='❤️', title_id="Hunger Games", author_id="Suzanne Collins")
    ]

Output:

Frontend:

function displayAddedEmotion(bookId, bookTitle, authorId, reactionType) {
        const addedEmotionsDiv = document.getElementById('addedEmotions');
        const emotionItem = document.createElement('div');
        emotionItem.className = 'emotion-item';
        emotionItem.dataset.bookId = bookId;
        emotionItem.dataset.authorId = authorId;
        emotionItem.dataset.reaction = reactionType;

        emotionItem.innerHTML = `
            <span>📖 <b>${bookTitle}</b> by ${authorId} — Reaction: <span class="reaction-text">${reactionType}</span></span>
            <div>
                <button class="update-button">📝</button>
                <button class="delete-button">🗑️</button>
            </div>
        `;

        addedEmotionsDiv.appendChild(emotionItem);
        emotionItem.querySelector('.update-button').addEventListener('click', handleUpdateEmotion);
        emotionItem.querySelector('.delete-button').addEventListener('click', handleDeleteEmotion);
    }

Backend:

return jsonify({"message": "All reactions for the user have been reset"}), 200
except Exception as e:
    return jsonify({'error': 'Failed to reset reactions', 'message': str(e)}), 500

Database + CRUD:

class Emotion(db.Model):
    __tablename__ = 'emotion'
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.String, db.ForeignKey('users.id'), nullable=False) #person
    title_id = db.Column(db.String, db.ForeignKey('books.title'), nullable=False) #book title/series
    author_id = db.Column(db.String, db.ForeignKey('books.author'), nullable=False) #author
    reaction_type = db.Column(db.String, nullable=False) #reaction

    def __init__(self, reaction_type, user_id, title_id, author_id):
        self.reaction_type = reaction_type
        self.user_id = user_id
        self.title_id = title_id
        self.author_id = author_id

    def add_reaction(reaction_type, user_id, title_id, author_id):
        new_reaction = Emotion (
            reaction_type=reaction_type,
            user_id=user_id,
            title_id=title_id,
            author_id=author_id
        )

        try:
            db.session.add(new_reaction)
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            raise e

    def create(self):
        db.session.add(self)
        db.session.commit()
        
    def delete(self):
        try:
            db.session.delete(self)
            db.session.commit()
        except Exception as e:
            db.session.rollback()
            raise e
        
    def read(self):
        return {
            "reaction_type": self.reaction_type,
            "user_id": self.user_id,
            "title_id": self.title_id,
            "author_id": self.author_id
        }
    
    @staticmethod # doesn't need self or cls methods 
    def restore(data):
        restored_reactions = {}

        for reaction_data in data:
            # Remove 'id' from the data if it exists (because id will be auto-generated)
            _ = reaction_data.pop('id', None)

            # Check if the reaction already exists for the same user and book
            existing_reaction = Emotion.query.filter_by(
                user_id=reaction_data.get("user_id"),
                title_id=reaction_data.get("title_id")
            ).first()

            if existing_reaction:
                # Update the existing reaction with new data
                existing_reaction.reaction_type = reaction_data.get('reaction_type', existing_reaction.reaction_type)
                existing_reaction.author_id = reaction_data.get('author_id', existing_reaction.author_id)

                db.session.commit()
                restored_reactions[existing_reaction.id] = existing_reaction
            else:
                # Create a new reaction
                new_reaction = Emotion(
                    reaction_type=reaction_data["reaction_type"],
                    user_id=reaction_data["user_id"],
                    title_id=reaction_data["title_id"],
                    author_id=reaction_data["author_id"]
                )
                new_reaction.create()
                restored_reactions[new_reaction.id] = new_reaction

        return restored_reactions

Big Idea 1: Creative Development

I’ve used creative development in my projects by designing and coding solutions that solve real-world problems. Whether it’s building a frontend that seamlessly integrates with a backend API or improving user experience with an emoji dropdown, I’ve had to think creatively. I’ve also refined my code iteratively, testing and improving it based on feedback and functionality requirements.

My feature is part of a web page that enables users to add, view, and reset their reactions to books through an interactive UI. This program fosters creative expression by allowing users to select emoji-based reactions, making their responses visually engaging and emotionally expressive. Throughout development, I iteratively refined the UI to ensure accessibility and ease of use.

Big Idea 3: Algorithms and Programming

In my work, I apply algorithms and programming principles to ensure my code is efficient and scalable. I use loops, conditionals, and functions to make my code reusable, especially when implementing features like update and delete functionality. Debugging and optimizing algorithms help improve performance, ensuring the frontend interacts smoothly with the backend API.

The program uses a series of functions and event-driven programming to handle user interactions, API calls, and UI updates. Key algorithms include:

  • Fetching book data: Calls the backend API to retrieve books and dynamically populates a dropdown menu.
  • Displaying user reactions: Fetches user reactions from the backend and updates the UI.
  • Resetting reactions: Sends a DELETE request to reset a user’s reactions and removes them from the UI.

Big Idea 4: Computer Systems and Networks

Understanding computer systems and networks has been crucial when working with APIs and ensuring secure data transmission between the frontend and backend. I consider factors like HTTP requests, data formats (JSON), and handling network errors. Security is also important, so I keep in mind best practices for protecting user data when integrating with external systems.

The program depends on HTTP requests to communicate between the frontend and backend. It uses:

  • GET requests to retrieve book and reaction data.
  • POST requests to add user reactions.
  • DELETE requests to reset reactions.

This requires understanding client-server interactions, where the frontend sends structured requests and processes JSON responses asynchronously using JavaScript’s fetch() API. Proper error handling ensures reliability, improving the overall network-based communication in my application.


N@TM Feedback:

Team

  • Good style with a cohesive theme.
  • Liked how everything pulled from one large database.
  • Text was a bit small, some areas needed improvement on style.

Individual

  • I talked to several people when I was at SCDC, and some feedback I receieved was to change my buttons (for CRUD methods) to be more aesthetic and user friendly.

Project Demo:

PPR

The purpose of my feature was to allow users react to books with emojis to show if the book made them feel happy, sad, etc. This is not as quick as a rating, but not quite a review, instead it just allows readers to express how the book made them feel in a simple manner. The program retrieves data from a backend API and displays it in an interactive UI. Users can:

  • Add new items using an input field with an emoji selection feature.
  • Update existing items by modifying their content and saving changes.
  • Delete items when they are no longer needed.
  • The UI dynamically updates based on user actions without requiring a page reload.

A combination of event listeners and API calls (GET, POST, PUT, DELETE) ensures that the frontend remains synchronized with the backend.

My project fulfills the CPT requirements of selection and iteration when adding reaction and updating reactions. When users delete a reaction, a for loop iterates through the existing reactions in the database. My code includes both inputs and outputs for when users add reactions, update reactions, etc.


MC:

Score: 53/67 (79.1%) Time:

Topic Skill Performance Errors:

  • 1.4: Identifying and Correcting Errors: 86% (6/7)
  • 2.1: Binary Numbers: 40% (2/5)
  • 2.2: Data Compression: 0% (0/1)
  • 2.3: Extracting Information from Data: 29% (2/7)
  • 3.1: Variables and Assignments: 0% (0/1)
  • 3.3: Mathematical Expressions: 0% (0/1)
  • 3.7: Nested Conditionals: 50% (1/2)
  • 3.9: Developing Algorithms: 75% (3/4)
  • 3.11: Binary Search: 0% (0/1)
  • 3.12: Calling Procedures: 0% (0/5)
  • 3.16: Simulations: 50% (1/2)
  • 3.17: Algorithmic Efficiency: 0% (0/2)
  • 3.18: Undecidable Problems: 0% (0/1)

Corrections: Q12: Incorrect Binary Representation. E.g. 00100101 = 2^0+2^2+2^5 = 32+4+1 = 37.

Q13: Didn’t quite understand the questions. Should have been B because the system only has information for books that were borrowed. Books that have never been borrowed are not represented in the data. Do not assume.

Q14: With this step, the algorithm will result in an infinite loop (unless number is a multiple of 10). For example, if the input is 512, the algorithm will display the integer quotient 51, then store the remainder 2 in number, then display the integer quotient 0, then store the remainder 2 in number, and so on. If number is a multiple of 10, the algorithm will display the result of dividing number by 10 and then terminate.

Q15: While the two programs initialize i to different values, the same values are printed inside the loop because program A prints i and then increments it and program B increments i and then prints it. Program A initializes i to 1. Inside the loop, it prints i and then increments i. The loop terminates when i is greater than 10, which occurs after 10 is printed. Program A prints 1 2 3 4 5 6 7 8 9 10. Program B initializes i to 0. Inside the loop, it increments i and then prints i. The loop terminates when i equals 10, which occurs after 10 is printed. Program B prints 1 2 3 4 5 6 7 8 9 10.

Q17: III is also correct because at the lowest level, all digital data (including machine language instructions) are represented with sequences of bits, meaning at the lowest level, all digital data (including integers, alphanumeric characters, and machine language instructions) are represented with sequences of bits.

Q20: Didn’t analyze correctly. Should have been A because each value on the right line graph is about 10 times the corresponding value on the left line graph. Therefore, the average amount of data stored per user is about 10 GB.

Q21: Didn’t calculate accurately. SHould have been B because the files that are up to 10 MB represent 17% + 24% + 25% + 10%, or 76%.

Q26: Metadata is data about data, so it’ll not likely have a duplicate copy of the data as it isn’t descriptive, such as copyright information.

Q27: Problems CAN be solved computationally without Internet connection, however, there are some problems that cannot be solved using any algorithm. These problems cannot be solved computationally.

Q28: Mixed up the order. Should have been I and III only.

Q29: Deleting some data then transferring is lossy. Compressing then transfering then restoring is lossless compression. Lossy compression reduces file size by discarding some data, while lossless compression reduces file size without losing any data.

Q30: Selected wrong. Cannot work if the values are out of the range. However, “out of range” isn’t possible because it is a prereq to the previous if else, so it will ALWAYS be in range.

Q32: Didn’t correctly understand the questions. Should be D because the code segment takes the sum of the individual scores and subtracts the lowest score. To obtain the average, the result is divided by one less than the number of scores (since one score was dropped).

Q33: This can be calculated, the vaule of the highest score cannot be because individual assignment scores are not shared.

Q34: Incorrect because it’ll move off the grid - be more attentive.

Q36: Older vs newer models are not related to the amount of time it’ll take to run a simulation. They are changing the complexity, which’ll allow for a lesser run time as it’s less detailed.

Q37: Incorrect because it wouldn’t draw the correct pieces. Rather, A draws the 4 correct segments.

Q41: III also because do the same as I just in a slightly different manner.

Q47: A binary search works on any sorted list, regardless of duplicate values. It just must be sorted.

Q49: Binary - counted incorrect. 6 bits = 2^6 which is 64. 7 bits = 2^7 = 128.

Q50: I, II, and III because Algorithm I accesses elements times (twice for each of n elements), which is considered reasonable time. Algorithm II accesses elements (n times for each of n elements), which is considered reasonable time.

Q56: Verson II actually takes longer because it’ll call GetPredition twice for each element while Verson I only calls GetPredition once, so verson II will take longer than version I.

Q60: Incorrect because it’ll produce the wrong result. D is correct because it creates newList1, containing the unique elements from list1, and newList2, containing the unique elements from list2. These two lists are combined to form bothList. Any elements that appear in both lists are removed from bothList to form uniqueList. The correct count is the difference between the lengths of bothList and uniqueList.

Q66: Error in recognizing nested loops. Should have been line 9 because every integer from start to end should be checked, so currentNum should only be incremented inside the loop but outside the body of the IF statement.

Next Time:

  • Binary starts w 0 - 2^0, 2^1, 2^2, etc
  • be more attentive
  • test the procedures
  • double check
  • read carefully