diff --git a/backend/sql/create_tables.sql b/backend/sql/create_tables.sql index f22296d8..823f89ae 100644 --- a/backend/sql/create_tables.sql +++ b/backend/sql/create_tables.sql @@ -69,7 +69,7 @@ CREATE TABLE Person ( PRIMARY KEY (person_id) ); -CREATE TYPE trans_type AS ENUM ('donation', 'purchase', 'throw out' ); +CREATE TYPE trans_type AS ENUM ('donation', 'purchase', 'throw out', 'checkout'); CREATE TABLE Transaction ( trans_id serial, person_id int, diff --git a/backend/sql/dummy_data2.sql b/backend/sql/dummy_data2.sql index 22e6b52c..89b04d3b 100644 --- a/backend/sql/dummy_data2.sql +++ b/backend/sql/dummy_data2.sql @@ -37,7 +37,11 @@ WHERE email = 'johndoe@example.com'; INSERT INTO Transaction (person_id, date, trans_type, site) VALUES ((SELECT MAX(person_id) FROM Person), NOW(), 'donation', 1), -- Add old transaction with goods that have been expiring. - ((SELECT MIN(person_id) FROM Person), NOW() - INTERVAL '3 weeks', 'donation', 1); + ((SELECT MAX(person_id) FROM Person), NOW() - INTERVAL '3 weeks', 'donation', 1), + -- Add three purchase transactions of same person_id: + ((SELECT MAX(person_id) FROM Person), NOW(), 'checkout', 1), + ((SELECT MAX(person_id) FROM Person), NOW(), 'checkout', 1), + ((SELECT MAX(person_id) FROM Person), NOW(), 'checkout', 1); -- Insert test data for Trans_items INSERT INTO Trans_items (trans_id, item_id, quantity, expiration) diff --git a/backend/sql/dummy_data3.sql b/backend/sql/dummy_data3.sql new file mode 100644 index 00000000..32a39ab2 --- /dev/null +++ b/backend/sql/dummy_data3.sql @@ -0,0 +1,61 @@ +-- Insert test data for Shelf +INSERT INTO Shelf (section_id, capacity) +VALUES ((SELECT MAX(section_id) FROM Section), 10), + ((SELECT MAX(section_id) FROM Section), 5), + ((SELECT MAX(section_id) FROM Section)-1, 7), + ((SELECT MAX(section_id) FROM Section)-1, 15); + +-- Insert test data for Aisle +INSERT INTO Aisle (site_id, info) +VALUES ((SELECT MAX(site_id) FROM Site), 'Fruit Aisle'); + +-- Insert test data for Section +INSERT INTO Section (aisle_id, stor_id) +VALUES ((SELECT MAX(aisle_id) FROM Aisle), (SELECT MAX(stor_id) FROM Storage_type)-2), + ((SELECT MAX(aisle_id) FROM Aisle), (SELECT MAX(stor_id) FROM Storage_type)-2); + +-- Insert test data for Shelf +INSERT INTO Shelf (section_id, capacity) +VALUES ((SELECT MAX(section_id) FROM Section), 10), + ((SELECT MAX(section_id) FROM Section), 5), + ((SELECT MAX(section_id) FROM Section)-1, 7), + ((SELECT MAX(section_id) FROM Section)-1, 15); + +-- Insert test data for Permissions +INSERT INTO Permissions (perm_num, description) +VALUES (1, 'Can view inventory'), + (2, 'Can edit inventory'); + +-- Insert test data for Empl_info +INSERT INTO Empl_info (perm_id, role) +VALUES ((SELECT MAX(perm_id) FROM Permissions), 'volunteer'); + +UPDATE Person SET empl_id = (SELECT MAX(empl_id) FROM Empl_info) +WHERE email = 'janesmith@example.com'; + +-- Insert test data for Transaction +INSERT INTO Transaction (person_id, date, trans_type, site) +VALUES ((SELECT MIN(person_id) FROM Person), NOW(), 'donation', 1), + -- Add three purchase transactions of same person_id: + ((SELECT MIN(person_id) FROM Person), NOW(), 'checkout', 1), + ((SELECT MIN(person_id) FROM Person), NOW(), 'checkout', 1), + ((SELECT MIN(person_id) FROM Person), NOW(), 'checkout', 1); + +-- Insert test data for Trans_items +INSERT INTO Trans_items (trans_id, item_id, quantity, expiration) +VALUES ((SELECT MIN(trans_id) FROM Transaction), 6, 7, NOW() + INTERVAL '3 weeks'), + ((SELECT MIN(trans_id) FROM Transaction), 5, 5, NOW() + INTERVAL '1 week'), + -- Donate goods that expired 3 weeks ago, 1 week ago, and 1 week from now. + -- Old apples + ((SELECT MAX(trans_id) FROM Transaction), 2, 10, NOW() - INTERVAL '2 weeks'), + -- Old oranges + ((SELECT MAX(trans_id) FROM Transaction), 1, 3, NOW() - INTERVAL '3 weeks'), + -- Nearly old Milk + ((SELECT MAX(trans_id) FROM Transaction), 4, 2, NOW() + INTERVAL '5 days'); + +-- Insert test data for Shelf_contents +INSERT INTO Shelf_contents (trans_item_id, shelf_id, store_date, quantity) +VALUES ((SELECT MAX(trans_item_id) FROM Trans_items), (SELECT MAX(shelf_id) FROM Shelf), NOW(), 10), + ((SELECT MAX(trans_item_id) FROM Trans_items), (SELECT MAX(shelf_id) FROM Shelf), NOW(), 15); + +REFRESH MATERIALIZED VIEW stock; \ No newline at end of file diff --git a/backend/src/app.js b/backend/src/app.js index d125df93..6bc15abe 100644 --- a/backend/src/app.js +++ b/backend/src/app.js @@ -97,6 +97,9 @@ app.get('/feed', feedRouter); app.get('/items', itemsRouter); app.get('/items/expired', itemsRouter); app.get('/items/nearly_expired', itemsRouter); +app.get('/items/total_donations', itemsRouter); +app.get('/items/total_checkouts', itemsRouter); +app.get('/items/unique_checkouts', itemsRouter); app.get('/donors', donorRouter); app.get('/lookupDonor', donorRouter); diff --git a/backend/src/routes/barcode.ts b/backend/src/routes/barcode.ts index a6cc33b9..28ce8ef9 100644 --- a/backend/src/routes/barcode.ts +++ b/backend/src/routes/barcode.ts @@ -8,47 +8,43 @@ const axiosInstance = axios.create({ baseURL: "https://world.openfoodfacts.org/api/v2", timeout: 3000 }); - - async function UPC_lookup(barcode: string) { - try { - var scannedItem = await axiosInstance.get('/search', {params: {code: barcode}}); - var product_name = scannedItem.data.products[0].product_name_en; - return product_name; - } catch (error) { - console.error(error); - return null; - } + try { + var scannedItem = await axiosInstance.get('/search', {params: {code: barcode}}); + var product_name = scannedItem.data.products[0].product_name_en; + return product_name; + } catch (error) { + console.error(error); + return null; + } } async function PLU_lookup(barcode: string) { - let product = await item.LookUpbarcode(barcode); - if (product !== null){ - return JSON.stringify(product); - } - else { - return null - } +let product = await item.LookUpbarcode(barcode); + if (product !== null){ + return JSON.stringify(product); + } else { + return null } +} router.get('/barcode', ensureAuthenticated, async (req: any, res: any) => { if (!req.isAuthenticated()) { const errors = []; res.post('Unauthenticated'); -} else { - const barcode = req.query.barcode; - if (barcode.length > 4) { - const result = await UPC_lookup(barcode); - res.send(result); -} else if (barcode.length === 4) { - const result = await PLU_lookup(barcode); - res.send(result); -} - else { - return res.status(404).send('Item not found'); + } else { + const barcode = req.query.barcode; + if (barcode.length > 4) { + const result = await UPC_lookup(barcode); + res.send(result); + } else if (barcode.length === 4) { + const result = await PLU_lookup(barcode); + res.send(result); + } else { + return res.status(404).send('Item not found'); + } } -} }); module.exports = router; \ No newline at end of file diff --git a/backend/src/routes/items.js b/backend/src/routes/items.js index a7336ce9..f5d79b7f 100644 --- a/backend/src/routes/items.js +++ b/backend/src/routes/items.js @@ -11,36 +11,41 @@ initModels(sequelize); router.get('/items/expired', ensureAuthenticated, function (req, res) { const today = new Date(); - return trans_items.findAll({ - where: { - expiration: { - [Op.lte]: today + + if (!req.isAuthenticated()) { + res.post("Unauthenticated"); + } else { + return trans_items.findAll({ + where: { + expiration: { + [Op.lte]: today + } + }, + include: [{ + model: transaction, + as: 'tran', + required: true, + attributes: ['date'] + }, { + model: item, + as: 'item', + required: true, + attributes: ['name'] + }], + attributes: ['trans_item_id', 'quantity', 'expiration'], + }).then((allItems) => { + if (allItems == null) { + console.log("There are no items to return"); + res.json(JSON.stringify([])); + } else { + res.json(JSON.stringify(allItems)); } - }, - include: [{ - model: transaction, - as: 'tran', - required: true, - attributes: ['date'] - }, { - model: item, - as: 'item', - required: true, - attributes: ['name'] - }], - attributes: ['trans_item_id', 'quantity', 'expiration'], - }).then((allItems) => { - if (allItems == null) { - console.log("There are no items to return"); - res.json(JSON.stringify([])); - } else { - res.json(JSON.stringify(allItems)); - } - }).catch(() => { - console.log("There was an error retrieving nearly-expired items"); - res.status(400); - res.send(); - }); + }).catch(() => { + console.log("There was an error retrieving nearly-expired items"); + res.status(400); + res.send(); + }); + } }); router.get('/items/nearly_expired', ensureAuthenticated, function (req, res) { @@ -48,37 +53,126 @@ router.get('/items/nearly_expired', ensureAuthenticated, function (req, res) { const twoDaysFromNow = new Date(); twoDaysFromNow.setTime(today.getTime() + (1000 * 60 * 60 * 24 * 2)); - return trans_items.findAll({ - where: { - expiration: { - [Op.lte]: twoDaysFromNow, - [Op.gte]: today, + if (!req.isAuthenticated()) { + res.post("Unauthenticated"); + } else { + return trans_items.findAll({ + where: { + expiration: { + [Op.lte]: twoDaysFromNow, + [Op.gte]: today, + } + }, + include: [{ + model: transaction, + as: 'tran', + required: true, + attributes: ['date'] + }, { + model: item, + as: 'item', + required: true, + attributes: ['name'] + }], + attributes: ['trans_item_id', 'quantity', 'expiration'], + }).then((allItems) => { + if (allItems == null) { + console.log("There are no items to return"); + res.json(JSON.stringify([])); + } else { + res.json(JSON.stringify(allItems)); + } + }).catch(() => { + console.log("There was an error retrieving nearly-expired items"); + res.status(400); + res.send(); + }); + } +}); + +router.get('/items/total_donations', ensureAuthenticated, function (req, res) { + + if (!req.isAuthenticated()) { + res.post("Unauthenticated"); + } else { + return trans_items.findAll({ + include: [{ + model: transaction, + as: 'tran', + required: true, + where: { + trans_type: 'donation' + } + }], + }).then((allItems) => { + if (allItems == null) { + console.log("There are no donated items to return"); + res.json(JSON.stringify([])); + } else { + res.json(allItems); + } + }).catch(() => { + console.log("There was an error retrieving total donations"); + res.status(400); + res.send(); + }); + } +}); + +router.get('/items/total_checkouts', ensureAuthenticated, function (req, res) { + + if (!req.isAuthenticated()) { + res.post("Unauthenticated"); + } else { + return trans_items.findAll({ + include: [{ + model: transaction, + as: 'tran', + required: true, + where: { + trans_type: 'purchase' + } + }], + }).then((allItems) => { + if (allItems == null) { + console.log("There are no checked-out items to return"); + res.json(JSON.stringify([])); + } else { + res.json(allItems); + } + }).catch(() => { + console.log("There was an error retrieving total checkouts"); + res.status(400); + res.send(); + }); + } +}); + +router.get('/items/unique_checkouts', ensureAuthenticated, function (req, res) { + + if (!req.isAuthenticated()) { + res.post("Unauthenticated"); + } else { + return transaction.findAll({ + attributes: [[Sequelize.fn('DISTINCT', Sequelize.col('person_id')) ,'person_id']], + distinct: true, + col: 'person_id', + where: { + trans_type: 'checkout' + } + }).then((allItems) => { + if (allItems == null) { + console.log("There are no student check-outs to return"); + res.json(JSON.stringify([])); + } else { + res.json(allItems); } - }, - include: [{ - model: transaction, - as: 'tran', - required: true, - attributes: ['date'] - }, { - model: item, - as: 'item', - required: true, - attributes: ['name'] - }], - attributes: ['trans_item_id', 'quantity', 'expiration'], - }).then((allItems) => { - if (allItems == null) { - console.log("There are no items to return"); - res.json(JSON.stringify([])); - } else { - res.json(JSON.stringify(allItems)); - } - }).catch(() => { - console.log("There was an error retrieving nearly-expired items"); - res.status(400); - res.send(); - }); + }).catch(() => { + console.log("There was an error retrieving unique student checkouts"); + res.status(400); + res.send(); + }); + } }); module.exports = router; \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index eafc8500..572da8ce 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,6 +9,7 @@ services: - ./backend/sql/dummy_data.sql:/docker-entrypoint-initdb.d/2_dummy_data.sql - ./backend/sql/PLU_data.sql:/docker-entrypoint-initdb.d/3_PLU_data.sql - ./backend/sql/dummy_data2.sql:/docker-entrypoint-initdb.d/4_dummy_data.sql + - ./backend/sql/dummy_data3.sql:/docker-entrypoint-initdb.d/5_dummy_data.sql networks: - local ports: