Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions backend/devmateback/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,27 @@ def delete_device(device):
else:
logger.error(f'Device {device} not found')
return jsonify({'message': 'Device not found'}), HTTPStatus.NOT_FOUND


@devices_bp.route('/update', methods=['POST'])
def update_device_info():
"""Update information field for a device."""
logger.debug('Updating device info')

is_valid, error_response, status_code = validate_request(request, ['device', 'info'])
if not is_valid:
logger.error('Invalid request')
return error_response, status_code

device_name = request.json['device']
info = request.json['info']

device = Device.query.filter_by(name=device_name).first()
if device:
device.info = info
db.session.commit()
logger.info(f'Device {device} info updated')
return jsonify({'message': 'Device info updated'}), HTTPStatus.OK
else:
logger.error(f'Device {device_name} not found')
return jsonify({'message': 'Device not found'}), HTTPStatus.NOT_FOUND
31 changes: 31 additions & 0 deletions backend/tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,37 @@ def test_delete_nonexistent_device(self):
self.assertEqual(response.get_json(), {'message': 'Device not found'})


class TestUpdateDeviceInfo(BaseTestCase):

def test_update_device_info_success(self):
with app.app_context():
device = Device(name='Device1', model='Model1', status='free', info='old')
db.session.add(device)
db.session.commit()

response = self.client.post('/devices/update', json={'device': 'Device1', 'info': 'new info'})
self.assertEqual(HTTPStatus.OK, response.status_code)
self.assertEqual(response.get_json(), {'message': 'Device info updated'})
with app.app_context():
device = Device.query.filter_by(name='Device1').first()
self.assertEqual(device.info, 'new info')

def test_update_device_info_not_found(self):
response = self.client.post('/devices/update', json={'device': 'NonExist', 'info': 'info'})
self.assertEqual(HTTPStatus.NOT_FOUND, response.status_code)
self.assertEqual(response.get_json(), {'message': 'Device not found'})

def test_update_device_info_missing_params(self):
response = self.client.post('/devices/update', json={'device': 'Device1'})
self.assertEqual(HTTPStatus.BAD_REQUEST, response.status_code)
self.assertEqual(response.get_json(), {'message': 'Missing parameters: info'})

def test_update_device_info_empty_params(self):
response = self.client.post('/devices/update', json={'device': '', 'info': ''})
self.assertEqual(HTTPStatus.BAD_REQUEST, response.status_code)
self.assertEqual(response.get_json(), {'message': 'Empty parameters: device, info'})


class TestValidation(unittest.TestCase):

def setUp(self):
Expand Down
36 changes: 36 additions & 0 deletions webui/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,41 @@ const App = () => {
await handleApiCall(`/devices/delete/${deviceName}`, 'delete', null).then(handleSuccess).catch(handleError);
};

const handleUpdateInfo = async (deviceName, info) => {
const handleError = (error) => {
if (error.response) {
switch (error.response.status) {
case 404:
console.warn(`Device ${deviceName} does not exist.`);
showSnackbar(`Device ${deviceName} does not exist.`);
break;
case 400:
console.warn(`Bad request. Missing parameters.`);
break;
default:
console.error('An error occurred:', error);
}
} else {
console.error('An error occurred:', error);
handleHealth();
}
};

const handleSuccess = async (response) => {
switch (response.status) {
case 200:
console.log('Device info updated successfully.');
break;
default:
console.warn('Unexpected response status:', response.status);
}
await handleList();
};

const payload = {device: deviceName, info: info};
await handleApiCall(`/devices/update`, 'post', payload).then(handleSuccess).catch(handleError);
};

const handleGetCLI = async () => {
setShowHelp(true);
}
Expand All @@ -459,6 +494,7 @@ const App = () => {
handleOnline={handleOnline}
handleOffline={handleOffline}
handleDelete={handleDelete}
handleUpdateInfo={handleUpdateInfo}
/>
<AddDeviceSection
newDevice={newDevice}
Expand Down
3 changes: 2 additions & 1 deletion webui/src/DeviceRow.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const calculateTimeDiff = (reservation_time) => {
return duration.format("d [days] h [hrs] m [min] s [sec]");
};

const DeviceRow = ({device, handleUsernameChange, deviceUsernames, handleReserve, handleRelease, handleOffline, handleDelete, handleOnline, showMaintenanceMode}) => {
const DeviceRow = ({device, handleUsernameChange, deviceUsernames, handleReserve, handleRelease, handleOffline, handleDelete, handleOnline, handleUpdateInfo, showMaintenanceMode}) => {
return (
<TableRow key={device.name}>
<TableCell>
Expand Down Expand Up @@ -60,6 +60,7 @@ const DeviceRow = ({device, handleUsernameChange, deviceUsernames, handleReserve
handleOffline={handleOffline}
handleDelete={handleDelete}
handleOnline={handleOnline}
handleUpdateInfo={handleUpdateInfo}
showMaintenanceMode={showMaintenanceMode}/>
</TableRow>
)
Expand Down
3 changes: 2 additions & 1 deletion webui/src/DevicesList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {Button, Paper, Table, TableBody, TableCell, TableContainer, TableHead, T
import DeviceRow from "./DeviceRow";
import React, {useState} from "react";

const DevicesList = ({devices, handleUsernameChange, deviceUsernames, handleReserve, handleRelease, handleOffline, handleDelete, handleOnline}) => {
const DevicesList = ({devices, handleUsernameChange, deviceUsernames, handleReserve, handleRelease, handleOffline, handleDelete, handleOnline, handleUpdateInfo}) => {
const [showMaintenanceMode, setShowMaintenanceMode] = useState(false);
return (
<TableContainer component={Paper}>
Expand Down Expand Up @@ -33,6 +33,7 @@ const DevicesList = ({devices, handleUsernameChange, deviceUsernames, handleRes
handleOffline={handleOffline}
handleDelete={handleDelete}
handleOnline={handleOnline}
handleUpdateInfo={handleUpdateInfo}
showMaintenanceMode={showMaintenanceMode}
/>
))}
Expand Down
26 changes: 23 additions & 3 deletions webui/src/MaintenanceGroup.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
import React from 'react';
import { Button, Box, TableCell } from '@mui/material';
import React, {useState} from 'react';
import { Button, Box, TableCell, TextField } from '@mui/material';
const MaintenanceGroup = ({device, handleOffline, handleDelete, handleOnline, handleUpdateInfo, showMaintenanceMode}) => {
const [editInfoMode, setEditInfoMode] = useState(false);
const [infoValue, setInfoValue] = useState(device.info || '');

const handleEditClick = () => {
if (editInfoMode) {
handleUpdateInfo(device.name, infoValue);
}
setEditInfoMode(!editInfoMode);
}

const MaintenanceGroup = ({device, handleOffline, handleDelete, handleOnline, showMaintenanceMode}) => {
return (
<TableCell style={{ visibility: showMaintenanceMode ? 'visible' : 'hidden' }}>
<Box display={"flex"} alignItems={"center"} sx={{ gap: 1 }}>
{editInfoMode && (
<TextField
size="small"
multiline
value={infoValue}
onChange={(e) => setInfoValue(e.target.value)}
/>
)}
<Button variant="contained" color="primary" onClick={handleEditClick}>
{editInfoMode ? 'Save Info' : 'Edit Info'}
</Button>
{device.status !== "offline" ? (
<Button
variant="contained"
Expand Down