Skip to content

Commit b975051

Browse files
authored
Merge pull request #52 from RGBOARD/image-processing
Image Processing and Preview
2 parents 494c20a + 14de169 commit b975051

27 files changed

Lines changed: 960 additions & 430 deletions

README.md

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#### Running the Backend
2+
13
To run app.py:
24

35
1. Create a virtual environment:
@@ -51,14 +53,34 @@ To run app.py:
5153
5254
5. If you are on vscode, remember to set the python interpreter to the created environment's python version.
5355

54-
6. After python env is ready, create the database in the root folder /backend:
56+
6. After python env is ready, database must be created in the root folder (```/backend```):
5557
```sh
5658
python utilities/create.py
5759
```
5860

59-
Now cd into the frontend:
60-
1. ```cd view```
61-
2. ```⁠npm install```
62-
3. ⁠Make sure the /backend is running in another terminal using: ```python app.py``` or ```flask run```
63-
4. ⁠Run frontend: ```npm run dev```
61+
7. Run the server (in its own terminal):
62+
```sh
63+
python app.py
64+
```
65+
66+
#### Running the Web App (Frontend)
67+
68+
1. Navigate to the frontend directory (in its own terminal):
69+
```sh
70+
cd view
71+
```
72+
73+
2. Install dependencies:
74+
```sh
75+
npm install
76+
```
77+
78+
3. Run the development server:
79+
```sh
80+
npm run dev
81+
```
82+
83+
#### After Sign Up and Log in
84+
1. **Without credentials file:** Open your database application and check the `VerificationCode` table to find the code associated with the newly created account.
6485

86+
2. **With credentials file:** You should receive an email containing the verification code.

app.py

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,52 +114,64 @@ def handleUserById(user_id):
114114

115115

116116
# Design-----------------------------------------------------------------------------------------------------------
117-
118117
@app.route("/design", methods=['POST'])
119118
@jwt_required()
120119
def upload_design():
121120
handler = Design(email=get_jwt_identity())
122-
return handler.add_new_design(title=request.form.get('title'), files=request.files)
121+
return handler.add_new_design(
122+
title=request.form.get('title'),
123+
pixel_data=request.form.get('pixel_data')
124+
)
125+
126+
127+
@app.route("/design/<int:design_id>/image", methods=['PUT'])
128+
@jwt_required()
129+
def update_design_image(design_id):
130+
handler = Design(email=get_jwt_identity())
131+
return handler.update_design_image(
132+
design_id=design_id,
133+
pixel_data=request.form.get('pixel_data')
134+
)
135+
123136

124137
@app.route("/design/<int:design_id>", methods=['GET'])
125138
@jwt_required()
126139
def get_design():
127140
handler = Design(email=get_jwt_identity())
128141
return handler.get_design(design_id=request.form.get('design_id'))
129142

143+
130144
@app.route("/design/<int:design_id>/title", methods=['PUT'])
131145
@jwt_required()
132146
def update_design_title():
133147
handler = Design(email=get_jwt_identity())
134148
return handler.update_design_title(design_id=request.form.get('design_id'), title=request.form.get('title'))
135149

136-
@app.route("/design/<int:design_id>/image", methods=['PUT'])
137-
@jwt_required()
138-
def update_design_image():
139-
handler = Design(email=get_jwt_identity())
140-
return handler.update_design_image(design_id=request.form.get('design_id'), image=request.files.get('image'))
141150

142151
@app.route("/design/<int:design_id>/approval", methods=['PUT'])
143152
@jwt_required()
144153
def update_design_approval(design_id):
145154
data = request.get_json()
146155
approval = data.get('approval')
147-
148156
handler = Design(email=get_jwt_identity())
149157
return handler.update_design_approval(design_id=design_id, approval=approval)
158+
159+
150160
@app.route("/design/<int:design_id>/status", methods=['PUT'])
151161
@jwt_required()
152162
def update_design_status():
153163
handler = Design(email=get_jwt_identity())
154164
return handler.update_design_status(design_id=request.form.get('design_id'), status=request.form.get('status'))
155165

166+
156167
@app.route("/design/approved", methods=['GET'])
157168
@jwt_required()
158169
def get_approved_designs():
159170
handler = Design(email=get_jwt_identity())
160171
approved = handler.getApprovedDesigns()
161172
return jsonify(approved), 200
162173

174+
163175
#AdminAction-----------------------------------------------------------------------------------------------------------
164176
@app.route("/admin_action", methods=['GET', 'POST'])
165177
def handleAdminAction():
@@ -250,6 +262,8 @@ def get_queue_paginated():
250262

251263
except Exception as e:
252264
return jsonify({'error': str(e)}), 500
265+
266+
253267
@app.route("/queue_item", methods=['GET', 'POST'])
254268
def handleQueueItem():
255269
if request.method == 'GET':

controller/design.py

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import json
12
from flask import jsonify, request
2-
import base64
33
from controller.user import User
44
from model.design import DesignDAO
55

@@ -9,7 +9,7 @@ def serialize_design(t):
99
'design_id': t[0],
1010
'user_id': t[1],
1111
'title': t[2],
12-
'image': base64.b64encode(t[3]).decode('utf-8') if t[3] else None,
12+
'pixel_data': t[3],
1313
'is_approved': t[4],
1414
'status': t[5],
1515
'created_at': t[6],
@@ -32,21 +32,25 @@ def __init__(self, email=None):
3232
if email:
3333
self.user = User(email=email)
3434

35-
def add_new_design(self, title=None, files=None):
36-
if 'image' not in files:
37-
return jsonify(error="No image file provided"), 400
35+
def add_new_design(self, title=None, pixel_data=None):
36+
if not pixel_data:
37+
return jsonify(error="No pixel data provided"), 400
3838

39-
image_file = files['image']
40-
if not image_file:
41-
return jsonify(error="No image file provided"), 400
39+
try:
40+
# Parse the JSON to validate it, but store as string
41+
json.loads(pixel_data)
42+
except json.JSONDecodeError:
43+
return jsonify(error="Invalid pixel data format"), 400
44+
45+
if not title:
46+
title = "Untitled Design"
4247

4348
user_id = self.user.get_user_id()
4449
if user_id is None:
4550
return jsonify(error='User not found'), 404
46-
47-
image = image_file.read()
51+
4852
dao = DesignDAO()
49-
new_id = dao.add_new_design(user_id, title, image)
53+
new_id = dao.add_new_design(user_id, title, pixel_data)
5054

5155
if isinstance(new_id, int) and new_id > 0:
5256
return jsonify(message="Design created", design_id=new_id), 201
@@ -100,9 +104,19 @@ def update_design_title(self, design_id, title):
100104
else:
101105
return jsonify(error="Unathorized."), 403
102106

103-
def update_design_image(self, design_id, image):
107+
def update_design_image(self, design_id, pixel_data=None):
104108
if design_id is None:
105109
return jsonify(error="No id provided."), 400
110+
111+
if not pixel_data:
112+
return jsonify(error="No pixel data provided"), 400
113+
114+
# Validate pixel_data is valid JSON
115+
try:
116+
# Parse the JSON to validate it, but store as string
117+
json.loads(pixel_data)
118+
except json.JSONDecodeError:
119+
return jsonify(error="Invalid pixel data format"), 400
106120

107121
user_id = self.user.get_user_id()
108122
if user_id is None:
@@ -112,21 +126,11 @@ def update_design_image(self, design_id, image):
112126
design_user_id = design_dao.get_user_id(design_id)
113127

114128
if design_user_id == user_id or self.user.is_admin():
115-
116-
# TODO: Check that the user hasn't exceed capacity limits
117-
## Nothing stops the user from uploading a gamzillion bytes.
118-
## Sum up all images of the user in the system and make sure
119-
## it doesn't exceed some size, like 1MB of total images.
120-
121-
# TODO: Image Processing and Constraints
122-
## Transform the image to fit the board and return it to the user
123-
## once we get a 201. The view should show and ask if they like it.
124-
125-
response = design_dao.update_design_image(design_id, image)
129+
response = design_dao.update_design_image(design_id, pixel_data)
126130
if response == 0:
127-
return jsonify(message="Title updated."), 200
131+
return jsonify(message="Design updated."), 200
128132
else:
129-
return jsonify(error="Couldn't update title"), 500
133+
return jsonify(error="Couldn't update design"), 500
130134
else:
131135
return jsonify(error="Unauthorized."), 403
132136

controller/queue_item.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
from flask import jsonify
2-
import base64
32
from controller.user import User
4-
53
from model.queue_item import QueueItemDAO
64

75

8-
96
class QueueItem:
107
def __init__(self, email=None):
118
self.email = email
@@ -100,7 +97,7 @@ def getScheduledDesigns(self):
10097
"display_order": row[5],
10198
"scheduled": row[6],
10299
"scheduled_at": row[7],
103-
"image": base64.b64encode(row[8]).decode('utf-8') if row[9] else None,
100+
"pixel_data": row[8],
104101
"is_approved": row[9],
105102
"design_created_at": row[10],
106103
"design_updated_at": row[11]
@@ -124,7 +121,7 @@ def get_all_items_paginated(self, page, page_size):
124121
"scheduled": item["scheduled"],
125122
"scheduled_at": item["scheduled_at"],
126123
"is_approved": item["is_approved"],
127-
"image": base64.b64encode(item["image"]).decode("utf-8"),
124+
"pixel_data": item["pixel_data"],
128125
"title": item["title"]
129126
})
130127

controller/setting.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import json
2-
32
import flask
4-
53
from google_auth_oauthlib.flow import Flow
64

75

controller/user.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import json
44
import random
55
from datetime import datetime, timedelta, timezone
6-
from flask import jsonify, request
6+
from flask import jsonify
77
from flask_jwt_extended import create_access_token
88

99
import base64

model/design.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ def get_user_id(self, design_id: int):
3838
finally:
3939
cursor.close()
4040

41-
def add_new_design(self, user_id, title, image):
41+
def add_new_design(self, user_id, title, pixel_data):
4242
cursor = self.conn.cursor()
43-
query = "INSERT INTO design (user_id, title, image) VALUES (?, ?, ?);"
43+
query = "INSERT INTO design (user_id, title, pixel_data) VALUES (?, ?, ?);"
4444
try:
45-
cursor.execute(query, (user_id, title, image))
45+
cursor.execute(query, (user_id, title, pixel_data))
4646
self.conn.commit()
4747
new_id = cursor.lastrowid
4848
return new_id
@@ -68,13 +68,13 @@ def update_design_title(self, design_id: int, title: str):
6868
cursor.close()
6969
return status
7070

71-
def update_design_image(self, design_id: int, image):
71+
def update_design_image(self, design_id: int, pixel_data):
7272
status = 1
7373
cursor = self.conn.cursor()
74-
query = "UPDATE design SET image = ?, updated_at = CURRENT_TIMESTAMP WHERE design_id = ?"
74+
query = "UPDATE design SET pixel_data = ?, updated_at = CURRENT_TIMESTAMP WHERE design_id = ?"
7575

7676
try:
77-
cursor.execute(query, (image, design_id))
77+
cursor.execute(query, (pixel_data, design_id))
7878
self.conn.commit()
7979
status = 0
8080
except sqlite3.Error:

model/queue_item.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def getScheduledDesigns(self):
116116
q.display_order,
117117
q.scheduled,
118118
q.scheduled_at,
119-
d.image,
119+
d.pixel_data,
120120
d.is_approved,
121121
d.created_at,
122122
d.updated_at
@@ -148,7 +148,7 @@ def get_all_items_paginated(self, page, page_size):
148148
q.display_order,
149149
q.scheduled,
150150
q.scheduled_at,
151-
d.image,
151+
d.pixel_data,
152152
d.title,
153153
d.is_approved
154154
FROM queue_item q

utilities/create.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sqlite3
22

3-
con = sqlite3.connect('../data.db')
3+
con = sqlite3.connect('data.db')
44
cur = con.cursor()
55

66
cur.execute("""
@@ -34,7 +34,7 @@
3434
design_id INTEGER PRIMARY KEY AUTOINCREMENT,
3535
user_id INTEGER NOT NULL,
3636
title TEXT NOT NULL,
37-
image BLOB NOT NULL,
37+
pixel_data TEXT NOT NULL, -- Changed to pixel_data since conversion is client-side
3838
is_approved BOOLEAN NOT NULL DEFAULT 1,
3939
status BOOLEAN NOT NULL DEFAULT 0,
4040
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,

view/public/images/logo.png

-57.3 KB
Binary file not shown.

0 commit comments

Comments
 (0)