Skip to content

Commit 2a7ff14

Browse files
authored
Merge pull request #71 from RGBOARD/item-reorder
Item Reorder Rework
2 parents cd5ac6c + 632aec9 commit 2a7ff14

8 files changed

Lines changed: 146 additions & 220 deletions

File tree

app.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -318,11 +318,13 @@ def schedule_image():
318318
handler = RotationSystem(email=get_jwt_identity(), json_data=request.json)
319319
return handler.schedule_image()
320320

321-
@app.route("/rotation/reorder", methods=['POST'])
321+
@app.route("/rotation/<int:item_id>/reorder", methods=['PUT'])
322322
@jwt_required()
323-
def reorder_images():
324-
handler = RotationSystem(email=get_jwt_identity(), json_data=request.json)
325-
return handler.reorder_images()
323+
def reorder_images(item_id):
324+
data = request.get_json()
325+
new_order = data.get('new_order')
326+
handler = RotationSystem(email=get_jwt_identity())
327+
return handler.reorder_images(item_id=item_id, new_order=new_order)
326328

327329
@app.route("/rotation/rotate", methods=['POST'])
328330
@jwt_required()

controller/rotation_system.py

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from datetime import datetime, timedelta
33
from model.rotation_system import RotationSystemDAO
44
from utilities.validators import validate_required_fields
5+
from controller.user import User
56

67
class RotationSystem:
78
"""
@@ -12,6 +13,8 @@ class RotationSystem:
1213
def __init__(self, email=None, json_data=None):
1314
"""Initialize the handler with user email and request data."""
1415
self.email = email
16+
if email:
17+
self.user = User(email=email)
1518
self.json_data = json_data
1619
self.dao = RotationSystemDAO()
1720

@@ -149,36 +152,22 @@ def schedule_image(self):
149152
except Exception as e:
150153
return jsonify({"error": str(e)}), 500
151154

152-
def reorder_images(self):
155+
def reorder_images(self, item_id, new_order):
153156
"""Set a custom display order for images in the rotation."""
154-
try:
155-
# Validate required fields
156-
required_fields = ['order']
157-
validation_result = validate_required_fields(self.json_data, required_fields)
158-
if validation_result:
159-
return validation_result
160-
161-
order = self.json_data.get('order')
162-
163-
# Validate order is a list
164-
if not isinstance(order, list):
165-
return jsonify({"error": "Order must be a list of design IDs"}), 400
166-
167-
# Reorder images
168-
success = self.dao.reorder_images(order)
169-
170-
if success:
171-
return jsonify({
172-
"success": True,
173-
"message": "Images reordered successfully"
174-
}), 200
157+
158+
if item_id is None:
159+
return jsonify(error="No id provided."), 400
160+
if self.email is not None:
161+
if self.user.is_admin():
162+
response = self.dao.reorder_images(item_id, new_order)
163+
if response == 0:
164+
return jsonify("Order updated"), 200
165+
else:
166+
return jsonify(error="Couldn't update Order"), 500
175167
else:
176-
return jsonify({
177-
"error": "Failed to reorder images"
178-
}), 500
179-
180-
except Exception as e:
181-
return jsonify({"error": str(e)}), 500
168+
return jsonify(error="Unauthorized."),
169+
170+
return jsonify(error="Unauthorized. No token."), 401
182171

183172
def rotate_to_next(self):
184173
"""Manually rotate to the next image."""

model/rotation_system.py

Lines changed: 41 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ def _get_connection(self):
2020
conn.execute("PRAGMA foreign_keys = ON")
2121
conn.execute("PRAGMA busy_timeout = 5000;")
2222
return conn
23+
24+
def get_rotation_item_byid(self, item_id):
25+
conn = self._get_connection()
26+
cursor = conn.cursor()
27+
28+
query = "select * from rotation_queue where item_id = ?;"
29+
cursor.execute(query, (item_id,))
30+
result = cursor.fetchone()
31+
cursor.close()
32+
return result
2333

2434
def get_active_image(self) -> Optional[Dict]:
2535
"""
@@ -420,90 +430,31 @@ def _get_image_info_by_id(self, item_id: int) -> Optional[Dict]:
420430
if 'conn' in locals() and conn:
421431
conn.close()
422432

423-
def reorder_images(self, ordered_design_ids: List[int]) -> bool:
424-
"""
425-
Set a custom display order for images in the rotation.
426-
427-
Args:
428-
ordered_design_ids: List of design IDs in the desired order
429-
430-
Returns:
431-
Success status
432-
"""
433-
if not ordered_design_ids:
434-
print("Cannot reorder: empty list provided")
435-
return False
436-
437-
# Ensure no duplicate IDs in the input list
438-
if len(ordered_design_ids) != len(set(ordered_design_ids)):
439-
print("Cannot reorder: duplicate design IDs in the list")
440-
return False
441-
442-
conn = None
443-
cur = None
444-
433+
def reorder_images(self, item_id: int, new_order: int):
434+
435+
status = 1
436+
query = None
437+
conn = self._get_connection()
438+
cursor = conn.cursor()
439+
old_order = self.get_rotation_item_byid(item_id)[3]
440+
now = datetime.now()
441+
if new_order < old_order:
442+
query = "UPDATE rotation_queue SET display_order = display_order + 1, updated_at = ? WHERE display_order >= ? AND display_order < ?"
443+
elif new_order > old_order:
444+
query = "UPDATE rotation_queue SET display_order = display_order - 1, updated_at = ? WHERE display_order <= ? AND display_order > ?"
445445
try:
446-
conn = self._get_connection()
447-
cur = conn.cursor()
448-
449-
# Start a transaction
450-
cur.execute("BEGIN TRANSACTION")
451-
452-
# First, get all items
453-
cur.execute("SELECT item_id, design_id FROM rotation_queue")
454-
all_items = cur.fetchall()
455-
456-
if not all_items:
457-
print("No items found in rotation queue")
458-
return False
459-
460-
items = {row['design_id']: row['item_id'] for row in all_items}
461-
462-
# Validate that all design IDs in the ordered list exist in the database
463-
missing_designs = [design_id for design_id in ordered_design_ids if design_id not in items]
464-
if missing_designs:
465-
print(f"Some design IDs not found in database: {missing_designs}")
466-
return False
467-
468-
# Create a timestamp once for all updates
469-
now = datetime.now()
470-
471-
# Now update display_order for each item in the ordered list
472-
for i, design_id in enumerate(ordered_design_ids):
473-
cur.execute("""
474-
UPDATE rotation_queue
475-
SET display_order = ?, updated_at = ?
476-
WHERE item_id = ?
477-
""", (i+1, now, items[design_id]))
478-
479-
# Update any remaining items with higher order values
480-
remaining_designs = set(items.keys()) - set(ordered_design_ids)
481-
next_order = len(ordered_design_ids) + 1
482-
483-
for design_id in remaining_designs:
484-
cur.execute("""
485-
UPDATE rotation_queue
486-
SET display_order = ?, updated_at = ?
487-
WHERE item_id = ?
488-
""", (next_order, now, items[design_id]))
489-
next_order += 1
490-
446+
if query:
447+
cursor.execute(query, (now, new_order, old_order))
448+
shift = "UPDATE rotation_queue SET display_order = ?, updated_at = ? WHERE item_id = ?"
449+
cursor.execute(shift,(new_order, now, item_id))
491450
conn.commit()
492-
print(f"Successfully reordered {len(ordered_design_ids)} images")
493-
return True
494-
495-
except Exception as e:
496-
if conn:
497-
conn.rollback()
498-
print(f"Error reordering images: {e}")
499-
return False
500-
451+
status = 0
452+
except sqlite3.Error:
453+
status = 1
501454
finally:
502-
if cur:
503-
cur.close()
504-
if conn:
505-
conn.close()
506-
455+
cursor.close()
456+
return status
457+
507458
def get_time_left_for_current(self) -> Optional[float]:
508459
"""
509460
Get the number of seconds left for the current active image.
@@ -633,8 +584,15 @@ def remove_item_from_rotation(self, item_id: int) -> bool:
633584
# If we deleted the active item, select a new one
634585
if success and item_id == active_item_id:
635586
self._select_new_active_item(conn)
636-
637-
# TODO: update order numbering
587+
588+
589+
cur.execute("SELECT item_id FROM rotation_queue ORDER BY display_order ASC")
590+
items = cur.fetchall()
591+
for index, item in enumerate(items):
592+
cur.execute(
593+
"UPDATE rotation_queue SET display_order = ? WHERE item_id = ?",
594+
(index + 1, item[0])
595+
)
638596

639597
conn.commit()
640598
return success

utilities/create.py

Lines changed: 1 addition & 1 deletion
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("""

view/src/auth/AuthProvider.jsx

Lines changed: 5 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -297,57 +297,16 @@ export const AuthProvider = ({ children }) => {
297297
// Update rotation order for an item
298298
const updateRotationOrder = async (itemId, newOrder) => {
299299
try {
300-
// We'll reuse the reorder endpoint with a custom order array
301-
const response = await axios.get('/rotation/items', {
302-
headers: {
303-
'Authorization': `Bearer ${getToken()}`
304-
}
305-
});
306-
307-
if (response.data && Array.isArray(response.data.items)) {
308-
// Get all design IDs in current order
309-
const currentItems = response.data.items;
310-
311-
// Find the item we want to move
312-
const targetItem = currentItems.find(item => item.item_id === itemId);
313-
if (!targetItem) {
314-
throw new Error('Item not found');
315-
}
316-
317-
// Remove the item from its current position
318-
const filteredItems = currentItems.filter(item => item.item_id !== itemId);
319-
320-
// Create a new array with the item at the new position
321-
const newOrderedItems = [
322-
...filteredItems.slice(0, newOrder - 1),
323-
targetItem,
324-
...filteredItems.slice(newOrder - 1)
325-
];
326-
327-
// Extract just the design IDs in the new order
328-
const orderedDesignIds = newOrderedItems.map(item => item.design_id);
329-
330-
// Make the reorder request
331-
await axios.post('/rotation/reorder', {
332-
order: orderedDesignIds
333-
}, {
334-
headers: {
335-
'Authorization': `Bearer ${getToken()}`
336-
}
337-
});
338-
339-
return { success: true };
340-
} else {
341-
throw new Error('Failed to get current rotation items');
342-
}
300+
await axios.put(`/rotation/${itemId}/reorder`, {new_order: newOrder});
301+
return {success: true};
343302
} catch (error) {
344-
console.error('Update rotation order error:', error);
303+
console.error('Order update error:', error);
345304
return {
346305
success: false,
347-
error: error.response?.data?.error || 'Failed to update rotation order'
306+
error: error.response?.data?.error || 'Failed to update item order'
348307
};
349308
}
350-
};
309+
}
351310

352311
// Auth context value
353312
const value = {

0 commit comments

Comments
 (0)