Blog Review
Blog Review and answers to questions/responses
Intro Requests for Review:
Our group’s program is a collaborative drawing game similar to Skribbl.io. The game lets a user create a drawing and another person guessing the drawing with hints and using their creativity. This process promotes creativity, teamwork, and interaction among users. My specific contribution is the implementation of the drawing pad feature, which allows users to draw on a canvas, save their drawings, and interact with others in real-time.
Purpose of Our Group’s Program:
The overall aim of our game is to foster a fun, creative environment where users can both express themselves artistically and guess the artwork of others. By integrating a drawing pad feature, users can engage with the game actively, producing drawings that are then guessed by others.
Purpose of Our Individual Feature:
My task involves building the drawing board where users can draw and create. The drawing board allows users to select colors, adjust brush sizes, erase strokes, and save their creations. These features work seamlessly with the backend to store and retrieve drawings in real-time.
List Requests:
Our application makes extensive use of lists and dictionaries to manage and manipulate data. For example, when retrieving data from the database, we often work with lists of rows and dictionaries representing columns. This allows us to efficiently handle and process large amounts of data.
import sqlite3
# Connect to the SQLite database
conn = sqlite3.connect('example.db')
c = conn.cursor()
# Create a table
c.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER
)
''')
# Insert some sample data
c.execute("INSERT INTO users (name, age) VALUES ('Alice', 30)")
c.execute("INSERT INTO users (name, age) VALUES ('Bob', 25)")
c.execute("INSERT INTO users (name, age) VALUES ('Charlie', 35)")
conn.commit()
# Retrieve data from the database
c.execute('SELECT * FROM users')
rows = c.fetchall()
# Convert the rows to a list of dictionaries
users = []
for row in rows:
user = {
'id': row[0],
'name': row[1],
'age': row[2]
}
users.append(user)
# Print the list of dictionaries
for user in users:
print(user)
# Close the connection
conn.close()
Formatting Response Data (JSON) from API into DOM:
When we receive data from the API, it is typically in JSON format. We then parse this JSON data and dynamically update the DOM to reflect the new information. This ensures that our frontend is always in sync with the backend.
# Makes the HTTP import requests
import requests
from flask import Flask, jsonify, render_template_string
app = Flask(__name__)
# API response
users_data = [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Charlie", "age": 35}
]
# Flask application, get function is called
@app.route('/api/users')
def get_users():
return jsonify(users_data) # Return JSON response
Queries from Database:
We use SQLAlchemy, a third-party library, to perform queries on our database. This allows us to extract lists of rows and work with them in Python. For example, we might query all drawings for a specific user and then process this list to display the drawings on the frontend.
from flask import Flask, jsonify
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# Configure the SQLAlchemy part of the app instance
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///drawings.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Create the SQLAlchemy db instance
db = SQLAlchemy(app)
# Define a model for the drawings table
class Drawing(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, nullable=False)
drawing_data = db.Column(db.Text, nullable=False)
# Create the database and the drawings table
db.create_all()
# Sample route to get all drawings for a specific user
@app.route('/api/drawings/<int:user_id>')
def get_drawings(user_id):
# Query all drawings for the specified user
drawings = Drawing.query.filter_by(user_id=user_id).all()
# Convert the list of Drawing objects to a list of dictionaries
drawings_list = [{'id': drawing.id, 'user_id': drawing.user_id, 'drawing_data': drawing.drawing_data} for drawing in drawings]
# Return the list of drawings as a JSON response
return jsonify(drawings_list)
if __name__ == '__main__':
app.run(debug=True)
Methods in Class:
Our application includes several methods within classes to handle CRUD (Create, Read, Update, Delete) operations on database columns. These methods ensure that we can efficiently manage our data and keep it up-to-date.
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# Configure the SQLAlchemy part of the app instance
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///items.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Create the SQLAlchemy db instance
db = SQLAlchemy(app)
# Define a model for the items table
class Item(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(80), nullable=False)
description = db.Column(db.String(200), nullable=True)
# Method to create a new item
@classmethod
def create_item(cls, name, description):
new_item = cls(name=name, description=description)
db.session.add(new_item)
db.session.commit()
return new_item
# Method to read an item by id
@classmethod
def read_item(cls, item_id):
return cls.query.get(item_id)
# Method to update an item by id
@classmethod
def update_item(cls, item_id, name=None, description=None):
item = cls.query.get(item_id)
if item:
if name:
item.name = name
if description:
item.description = description
db.session.commit()
return item
# Method to delete an item by id
@classmethod
def delete_item(cls, item_id):
item = cls.query.get(item_id)
if item:
db.session.delete(item)
db.session.commit()
return item
# Create the database and the items table
db.create_all()
# Sample route to demonstrate CRUD operations
@app.route('/api/items', methods=['POST'])
def create_item():
data = request.get_json()
item = Item.create_item(data['name'], data.get('description'))
return jsonify({'id': item.id, 'name': item.name, 'description': item.description})
# Defubes riyte read by ID
@app.route('/api/items/<int:item_id>', methods=['GET'])
def read_item(item_id):
item = Item.read_item(item_id)
if item:
return jsonify({'id': item.id, 'name': item.name, 'description': item.description})
return jsonify({'error': 'Item not found'}), 404
# Defines route updated by ID
@app.route('/api/items/<int:item_id>', methods=['PUT'])
def update_item(item_id):
data = request.get_json()
item = Item.update_item(item_id, data.get('name'), data.get('description'))
if item:
return jsonify({'id': item.id, 'name': item.name, 'description': item.description})
return jsonify({'error': 'Item not found'}), 404
# Deletes item by ID
@app.route('/api/items/<int:item_id>', methods=['DELETE'])
def delete_item(item_id):
item = Item.delete_item(item_id)
if item:
return jsonify({'message': 'Item deleted'})
return jsonify({'error': 'Item not found'}), 404
# Checks if run correctly and starts application
if __name__ == '__main__':
app.run(debug=True)
Algorithmic Code Request:
Our API class includes methods to handle GET, POST, PUT, and DELETE requests. These methods are responsible for performing the necessary operations on the backend and returning the appropriate responses.
from flask import Flask, request, jsonify
app = Flask(__name__)
# The database items
items = []
class API:
@staticmethod
@app.route('/api/items', methods=['GET'])
def get_items():
# Handle GET request to retrieve all items
return jsonify(items) # Return the list of items as a JSON response
@staticmethod
@app.route('/api/items', methods=['POST'])
def create_item():
# Handle POST request to create a new item
data = request.get_json() # Get the JSON data from the request body
item = {
'id': len(items) + 1, # Generate a new ID for the item
'name': data['name'],
'description': data.get('description', '') # Use an empty string if description is not provided
}
items.append(item) # Add the new item to the list
return jsonify(item), 201 # Return the created item and a 201 Created status
@staticmethod
@app.route('/api/items/<int:item_id>', methods=['PUT'])
def update_item(item_id):
# Handle PUT request to update an existing item
data = request.get_json() # Get the JSON data from the request body
item = next((item for item in items if item['id'] == item_id), None) # Find the item by ID
if item:
item['name'] = data.get('name', item['name']) # Update the name if provided
item['description'] = data.get('description', item['description']) # Update the description if provided
return jsonify(item) # Return the updated item
return jsonify({'error': 'Item not found'}), 404 # Return a 404 Not Found status if item is not found
@staticmethod
@app.route('/api/items/<int:item_id>', methods=['DELETE'])
def delete_item(item_id):
# Handle DELETE request to delete an existing item
global items
items = [item for item in items if item['id'] != item_id] # Remove the item by ID
return jsonify({'message': 'Item deleted'}) # Return a success message
if __name__ == '__main__':
app.run(debug=True)
Method/Procedure in Class:
One of our methods includes sequencing, selection, and iteration to process incoming requests. For example, when a user submits a drawing, we first validate the data (selection), then save it to the database (sequencing), and finally iterate over the list of connected users to broadcast the new drawing.
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# Configure the SQLAlchemy part of the app instance
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///drawings.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
# Create the SQLAlchemy db instance
db = SQLAlchemy(app)
# Define a model for the drawings table
class Drawing(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, nullable=False)
drawing_data = db.Column(db.Text, nullable=False)
# Create the database and the drawings table
db.create_all()
# Simulate a list of connected users
connected_users = []
class DrawingAPI:
@staticmethod
@app.route('/api/drawings', methods=['POST'])
def submit_drawing():
# Get the JSON data from the request body
data = request.get_json()
# Selection: Validate the incoming data
if 'user_id' not in data or 'drawing_data' not in data:
return jsonify({'error': 'Invalid data'}), 400
# Sequencing: Save the drawing to the database
new_drawing = Drawing(user_id=data['user_id'], drawing_data=data['drawing_data'])
db.session.add(new_drawing)
db.session.commit()
# Iteration: Broadcast the new drawing to connected users
for user in connected_users:
# Simulate broadcasting the drawing to each connected user
print(f"Broadcasting drawing to user {user['id']}")
# Return a success response
return jsonify({'message': 'Drawing submitted successfully', 'drawing_id': new_drawing.id}), 201
if __name__ == '__main__':
app.run(debug=True)
Parameters and Return Type:
Our API methods typically accept a JSON body as a parameter and return a JSON response using the jsonify function. This ensures that our API is consistent and easy to work with.
from flask import Flask, request, jsonify
app = Flask(__name__)
# Initializes an empty list and creates flask modules
items = []
class API:
@staticmethod
@app.route('/api/items', methods=['POST'])
def create_item():
# Get the JSON data from the request body
data = request.get_json()
# Validate the incoming data
if 'name' not in data or 'description' not in data:
return jsonify({'error': 'Invalid data'}), 400
# Create a new item
item = {
'id': len(items) + 1, # Generate a new ID for the item
'name': data['name'],
'description': data['description']
}
items.append(item) # Add the new item to the list
# Return the created item as a JSON response
return jsonify(item), 201 # 201 Created status
@staticmethod
@app.route('/api/items/<int:item_id>', methods=['GET'])
def get_item(item_id):
# Find the item by ID
item = next((item for item in items if item['id'] == item_id), None)
# If the item is not found, return a 404 error
if item is None:
return jsonify({'error': 'Item not found'}), 404
# Return the found item as a JSON response
return jsonify(item)
@staticmethod
@app.route('/api/items/<int:item_id>', methods=['PUT'])
def update_item(item_id):
# Get the JSON data from the request body
data = request.get_json()
# Find the item by ID
item = next((item for item in items if item['id'] == item_id), None)
# If the item is not found, return a 404 error
if item is None:
return jsonify({'error': 'Item not found'}), 404
# Update the item's name and description if provided
item['name'] = data.get('name', item['name'])
item['description'] = data.get('description', item['description'])
# Return the updated item as a JSON response
return jsonify(item)
@staticmethod
@app.route('/api/items/<int:item_id>', methods=['DELETE'])
def delete_item(item_id):
# Find the item by ID
global items
item = next((item for item in items if item['id'] == item_id), None)
# If the item is not found, return a 404 error
if item is None:
return jsonify({'error': 'Item not found'}), 404
# Remove the item from the list
items = [item for item in items if item['id'] != item_id]
# Returns a success message as a JSON response
return jsonify({'message': 'Item deleted'})
if __name__ == '__main__':
app.run(debug=True)
Call to Algorithm Request:
When making a request to our API, we use the fetch function to send the request and handle the response. This allows us to interact with the backend and update the frontend based on the returned data.
from flask import Flask, request, jsonify
app = Flask(__name__)
# Sample data to simulate a database
items = [
{'id': 1, 'name': 'Item 1', 'description': 'Description 1'},
{'id': 2, 'name': 'Item 2', 'description': 'Description 2'}
]
@app.route('/api/items', methods=['GET'])
def get_items():
# Return the list of items as a JSON response
return jsonify(items)
if __name__ == '__main__':
app.run(debug=True)
Handling Data:
We handle the returned data by parsing the JSON response and updating the DOM accordingly. This ensures that our application remains responsive and provides real-time feedback to the user.
from flask import Flask, request, jsonify
app = Flask(__name__)
# Simulates the database
items = [
{'id': 1, 'name': 'Item 1', 'description': 'Description 1'},
{'id': 2, 'name': 'Item 2', 'description': 'Description 2'}
]
@app.route('/api/items', methods=['GET'])
def get_items():
# Returns the list of items as a JSON response
return jsonify(items)
if __name__ == '__main__':
app.run(debug=True)
Changing Data or Method:
When we change the data or method, we handle different responses by checking the status code and message in the response. For example, if an error occurs, we display an error message to the user. This allows us to handle both normal and error conditions gracefully.
from flask import Flask, request, jsonify
app = Flask(__name__)
# The database of items
items = [
{'id': 1, 'name': 'Item 1', 'description': 'Description 1'},
{'id': 2, 'name': 'Item 2', 'description': 'Description 2'}
]
@app.route('/api/items', methods=['GET'])
def get_items():
# Returns the list of items as a JSON response
return jsonify(items)
@app.route('/api/items', methods=['POST'])
def create_item():
data = request.get_json()
if 'name' not in data or 'description' not in data:
# Returns an error response if the data is invalid
return jsonify({'error': 'Invalid data'}), 400
# Creates a new item
new_item = {
'id': len(items) + 1,
'name': data['name'],
'description': data['description']
}
items.append(new_item)
# Returns the created item as a JSON response
return jsonify(new_item), 201
if __name__ == '__main__':
app.run(debug=True)
End-to-End Tracing:
Ensure seamless communication and data flow between frontend and backend. This involves testing the entire flow from drawing on the canvas to saving the drawing in the database.
# Imports the necessary modules from flask and initializes the Flask app
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/save-drawing', methods=['POST'])
def save_drawing():
data = request.get_json()
print('Saving drawing:', data)
# Creates the JSON response with success message, and drawing saved
return jsonify({'status': 'success', 'message': 'Drawing saved'})
if __name__ == '__main__':
app.run(debug=True)
Testing:
Build and execute tests using Postman. Add test data to systems to validate functionality and performance.
# Unittest is the built in python module for testing, it is used to test the API endpoints
import unittest
from app import app
class DrawingTestCase(unittest.TestCase):
def setUp(self):
self.app = app.test_client()
self.app.testing = True
# Tests the api save drawing endpoint and a POST request to drawing data
def test_save_drawing(self):
response = self.app.post('/api/save-drawing', json={
'drawing': 'data:image/png;base64,...'
})
self.assertEqual(response.status_code, 200)
self.assertIn('Drawing saved', response.get_data(as_text=True))
# Runs the unittest
if __name__ == '__main__':
unittest.main()
Database Management with SQLite:
Set up and manage SQLite databases to store user data, posts, and images. Design database schemas to efficiently handle social media data.
# SQLite database setup
import sqlite3
conn = sqlite3.connect('drawings.db')
c = conn.cursor()
# Connects to the SQLite database named drawings.db and it will create a database if it does not exist
c.execute('''CREATE TABLE drawings
(id INTEGER PRIMARY KEY, user_id INTEGER, drawing_data TEXT)''')
# SQL command to create a table
c.execute("INSERT INTO drawings (user_id, drawing_data) VALUES (1, 'data:image/png;base64,...')")
# Save (commit) the changes
conn.commit()
# Close the connection
conn.close()
### Image Upload and Storage:
Implement functionality to upload and store images in the database or file system.
Ensure images are properly linked to user posts and profiles.
from flask import Flask, request
import base64
app = Flask(__name__)
# This function accepts POST requests called a api upload image endpoint
@app.route('/api/upload-image', methods=['POST'])
def upload_image():
data = request.get_json()
image_data = base64.b64decode(data['image'])
with open('uploaded_image.png', 'wb') as f:
f.write(image_data)
return {'status': 'success', 'message': 'Image uploaded'}
# This code checks if the script is being run directly
if __name__ == '__main__':
app.run(debug=True)
Data Security and Privacy:
Implement security measures to protect user data and images. Ensure compliance with privacy regulations and best practices.
# Ensures data is encrypted and decrypted securely
from cryptography.fernet import Fernet
# Generates a key for encription
key = Fernet.generate_key()
cipher_suite = Fernet(key)
# Encrypts the data
data = b'My secret data'
cipher_text = cipher_suite.encrypt(data)
# Decrypts the data
plain_text = cipher_suite.decrypt(cipher_text)
print('Decrypted data:', plain_text)
# This data is then printed to the console to verify it matches the original data
Data Retrieval and Display:
Develop efficient queries to retrieve and display data on the frontend. Optimize database performance for fast data access.
# Retrieves data from the database (SQL)
import sqlite3
# Connects to SQLite database named drawings.db, creates if DNE
conn = sqlite3.connect('drawings.db')
c = conn.cursor()
# Retrieves data, fetches all rows from the drawings table, the user id being 1
c.execute('SELECT * FROM drawings WHERE user_id = 1')
rows = c.fetchall()
for row in rows:
print(row)
conn.close()
Data Backup and Recovery:
Implement backup strategies to prevent data loss. Develop recovery procedures to restore data in case of failure.
# Backs up my SQLite database
import shutil
# Copies file drawings.db to drawings_backup.db
shutil.copy('drawings.db', 'drawings_backup.db')
print('Database backup created.')
# Copies drawings_backup.db to drwaings,db, restores database
shutil.copy('drawings_backup.db', 'drawings.db')
# Print message that the database is restored
print('Database restored from backup.')
Conclusion:
My blog demonstrates how to implement a collaborative drawing feature into a game. By utilizing backend API to handle drawing data and storing that data in a database, the drawing pad feature becomes interactive and ensures that user drawings are saved and accessible to all players. Our website allows people to work together and be creative with whatever they want to create.