added things

This commit is contained in:
Sid Alapati 2024-05-18 13:49:02 -04:00
parent 0c10365da1
commit 880c3377f3
11 changed files with 2527 additions and 1098 deletions

View File

@ -1,3 +1,3 @@
# Fridge2Fit
Fridge2Fit is an AI-powered recipe generator that helps individuals with obesity take control of their diet. Simply upload a picture of your fridge contents, and our algorithm will suggest healthy recipes tailored to your ingredients. Our goal is to empower users to make informed food choices, reduce food waste, and promote a healthier lifestyle.
Fridge2Fit is an AI-powered recipe generator that helps individuals with obesity take control of their diet. Simply upload a picture of your fridge contents, and our algorithm will suggest healthy recipes tailored to your ingredients. Our goal is to empower users to make informed food choices, reduce food waste, and promote a healthier lifestyle.

33
app.js
View File

@ -7,44 +7,13 @@ const port = 3000;
const upload = multer({ dest: 'uploads/' });
const GROQ_API_KEY = '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';
const model = 'llama3-8b-8192';
app.use(express.static("static"));
app.use(express.json());
app.get('/', (req, res) => {
res.sendFile(__dirname + '/static/index.html');
res.sendFile(__dirname + '/static/index.html');
});
app.post('/completions', async (req, res) => {
try {
const query = req.body.query;
if (!query) {
return res.status(400).json({ error: 'Prompt is required' });
}
const data = {
messages: [{ role: 'user', content: query }],
model,
};
const config = {
headers: {
Authorization: `Bearer ${GROQ_API_KEY}`,
'Content-Type': 'application/json',
},
};
const response = await axios.post('https://api.groq.com/openai/v1/chat/completions', data, config);
res.json(response.data);
} catch (error) {
console.error(error);
res.status(500).json({ error: 'Error completing prompt' });
}
});
app.post('/upload', upload.single('image'), async (req, res) => {
try {
const image = fs.readFileSync(req.file.path, { encoding: 'base64' });

3152
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,13 @@
{
"dependencies": {
"axios": "^1.6.8",
"express": "^4.19.2",
"multer": "^1.4.5-lts.1"
}
"dependencies": {
"axios": "^1.6.8",
"express": "^4.19.2",
"multer": "^1.4.5-lts.1"
},
"devDependencies": {
"tailwindcss": "^3.4.3"
},
"scripts": {
"start": "node app.js"
}
}

1
static/dist.css Normal file

File diff suppressed because one or more lines are too long

BIN
static/header.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

View File

@ -1,162 +1,176 @@
<!DOCTYPE html>
<!-- <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload Image</title>
</head>
<body>
<input type="file" id="imageInput" accept="image/*" required=""> <button onclick="uploadImage()">Upload Image</button>
<br><br>
<div id="editable-classes"></div>
<button id="add-class-btn">Add Class</button>
<div id="suggested-classes"></div>
<br>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Upload Image</title>
</head>
<body>
<input type="file" id="imageInput" accept="image/*" required />
<button onclick="uploadImage()">Upload Image</button> <br /><br />
<div id="editable-classes"></div>
<button id="add-class-btn">Add Class</button>
<div id="suggested-classes"></div>
</body>
</html> -->
<button onclick="generateIngredients()">Generate Ingredients</button>
<script>
const editableClassesDiv = document.getElementById("editable-classes");
const suggestedClassesDiv = document.getElementById("suggested-classes");
const addClassBtn = document.getElementById("add-class-btn");
const uploadImage = async () => {
const input = document.getElementById('imageInput');
const file = input.files[0];
const formData = new FormData();
formData.append('image', file);
try {
const response = await fetch('../upload', {
method: 'POST',
body: formData,
});
if (!response.ok) {
throw new Error('Failed to upload image');
}
const jsonResponse = await response.json();
console.log(jsonResponse);
const classes = jsonResponse.predictions;
// Remove all underscores
classes.forEach(str => {
str.class = str.class.replace(/_/g, ' ');
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Fridge2Fit</title>
<meta
name="description"
content="Fridge2Fit is an AI-powered recipe generator that helps individuals with obesity take control of their diet. Simply upload a picture of your fridge contents, and our algorithm will suggest healthy recipes tailored to your ingredients. Our goal is to empower users to make informed food choices, reduce food waste, and promote a healthier lifestyle."
/>
<meta
name="keywords"
content="ai artificial intelligence fridge fit food fat obesity health healthy"
/>
// Create input fields for classes with confidence >= 0.5
<link href="/dist.css" rel="stylesheet" />
<script src="/load.js" defer></script>
</head>
classes.filter((cls) => cls.confidence >= 0.5).forEach((cls) => {
addInputField(cls.class);
});
// Create buttons for classes with confidence < 0.5
classes.filter((cls) => cls.confidence < 0.5).forEach((cls) => {
addSuggestion(cls.class);
});
addSuggestion("salt and pepper");
// Add event listener to "Add Class" button
addClassBtn.onclick = () => {
addInputField("");
};
} catch ( exception) {
console.log(exception);
}
};
function addInputField(content) {
const inputField = document.createElement("input");
inputField.type = "text";
inputField.value = content;
const closeButton = document.createElement("button");
closeButton.style.border = "none";
closeButton.style.background = "transparent";
closeButton.style.cursor = "pointer";
closeButton.innerHTML = "x";
closeButton.addEventListener("click", () => {
editableClassesDiv.removeChild(inputField);
editableClassesDiv.removeChild(br);
editableClassesDiv.removeChild(closeButton);
});
editableClassesDiv.appendChild(inputField);
editableClassesDiv.appendChild(closeButton);
const br = document.createElement("br");
editableClassesDiv.appendChild(br);
}
<body
class="leading-normal tracking-normal text-indigo-400 m-6 bg-cover bg-fixed overflow-hidden"
style="background-image: url('header.png')"
>
<div class="h-full">
<!--Nav-->
<div class="w-full container mx-auto">
<div class="w-full flex items-center justify-between">
<a
class="flex items-center text-indigo-400 no-underline hover:no-underline font-bold text-2xl lg:text-4xl"
href="#"
>
Fridge<span
class="bg-clip-text text-transparent bg-gradient-to-r from-green-400 via-pink-500 to-purple-500"
>2Fit</span
>
</a>
function addSuggestion(content) {
const button = document.createElement("button");
button.textContent = content;
button.onclick = () => {
addInputField(content);
button.remove();
};
suggestedClassesDiv.appendChild(button);
}
<div class="flex w-1/2 justify-end content-center">
<a
class="inline-block text-blue-300 no-underline hover:text-pink-500 hover:text-underline text-center h-10 p-2 md:h-auto md:p-4 transform hover:scale-125 duration-300 ease-in-out"
href="https://twitter.com/intent/tweet?url=#"
>
<svg
class="fill-current h-6"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
>
<path
d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"
></path>
</svg>
</a>
<a
class="inline-block text-blue-300 no-underline hover:text-pink-500 hover:text-underline text-center h-10 p-2 md:h-auto md:p-4 transform hover:scale-125 duration-300 ease-in-out"
href="https://www.facebook.com/sharer/sharer.php?u=#"
>
<svg
class="fill-current h-6"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
>
<path
d="M19 6h5V0h-5c-3.86 0-7 3.14-7 7v3H8v6h4v16h6V16h5l1-6h-6V7c0-.542.458-1 1-1z"
></path>
</svg>
</a>
</div>
</div>
</div>
async function createInference(query) {
try {
const response = await fetch("/completions", {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ query })
});
<!--Main-->
<div
class="container pt-24 md:pt-36 mx-auto flex flex-wrap flex-col md:flex-row items-center"
>
<!--Left Col-->
<div
class="flex flex-col w-full xl:w-2/5 justify-center lg:items-start overflow-y-hidden"
>
<h1
class="my-4 text-3xl md:text-5xl text-white opacity-75 font-bold leading-tight text-center md:text-left"
>
Main
<span
class="bg-clip-text text-transparent bg-gradient-to-r from-green-400 via-pink-500 to-purple-500"
>
Hero Message
</span>
to sell yourself!
</h1>
<p
class="leading-normal text-base md:text-2xl mb-8 text-center md:text-left"
>
An AI-powered recipe generator that helps individuals
with obesity take control of their diet.
</p>
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
<form
class="bg-gray-800 opacity-75 w-full shadow-lg rounded-lg px-8 pt-6 pb-8 mb-4 h-64"
>
<button
class="text-4xl w-full h-full bg-gradient-to-r from-purple-800 to-green-500 hover:from-pink-500 hover:to-green-500 text-white font-bold py-2 px-4 rounded-3xl focus:ring transform transition hover:scale-105 duration-300 ease-in-out flex items-center"
onclick="document.getElementById('imageInput').click();"
>
<input
type="file"
id="imageInput"
accept="image/*"
required
hidden
/>
<img
src="upload.svg"
class="h-1/2 pr-8 brightness-150"
/>
Upload your fridge
</button>
</form>
</div>
const data = await response.json();
const content = data.choices[0].message.content;
return `<p>${content}</p>`;
} catch (error) {
return `<p>Error: ${error.message}</p>`;
}
}
<!--Right Col-->
<div class="w-full xl:w-3/5 p-12 overflow-hidden">
<img
class="mx-auto w-full md:w-4/5 transform -rotate-6 transition hover:scale-105 duration-700 ease-in-out hover:rotate-6"
src="macbook.svg"
/>
</div>
async function generateIngredients() {
// Get all input elements inside the div
const inputs = editableClassesDiv.querySelectorAll("input");
<div class="mx-auto md:pt-16">
<p
class="text-blue-400 font-bold pb-8 lg:pb-6 text-center"
></p>
<div
class="flex w-full justify-center md:justify-start pb-24 lg:pb-0 fade-in"
>
<img
class="h-12 pr-12 transform hover:scale-125 duration-300 ease-in-out"
alt=""
/>
<img
class="h-12 transform hover:scale-125 duration-300 ease-in-out"
alt=""
/>
</div>
</div>
// Initialize an empty string to hold the input values
let values = "";
// Check if there are any input elements
if (inputs.length === 0) {
return; // Exit the function silently if there are no input elements
}
// Loop through the input elements and add their values to the string
inputs.forEach((input) => {
if (values !== "") {
values += ", ";
}
values += input.value;
});
// Alert the input values
const output = await createInference(values);
// Create a new div element to hold the output
const outputDiv = document.createElement("div");
outputDiv.textContent = output;
// Add the output div to the page
document.body.appendChild(outputDiv);
}
</script>
</body>
<!--Footer-->
<div
class="w-full pt-16 pb-6 text-sm text-center md:text-left fade-in"
>
<a
class="text-gray-500 no-underline hover:no-underline"
href="#"
>&copy; Fridge2Fit 2024</a
>
</div>
</div>
</div>
</body>
</html>

83
static/load.js Normal file
View File

@ -0,0 +1,83 @@
const uploadImage = async () => {
const input = document.getElementById('imageInput');
const file = input.files[0];
const formData = new FormData();
formData.append('image', file);
try {
const response = await fetch('../upload', {
method: 'POST',
body: formData,
});
if (!response.ok) {
throw new Error('Failed to upload image');
}
const jsonResponse = await response.json();
console.log(jsonResponse);
const classes = jsonResponse.predictions;
// classes[12] = { "class": "fishy fish", "confidence": 0.1 }
const editableClassesDiv = document.getElementById('editable-classes');
const suggestedClassesDiv =
document.getElementById('suggested-classes');
const addClassBtn = document.getElementById('add-class-btn');
// Create input fields for classes with confidence >= 0.5
classes
.filter((cls) => cls.confidence >= 0.5)
.forEach((cls) => {
const inputField = document.createElement('input');
inputField.type = 'text';
inputField.value = cls.class;
const closeButton = document.createElement('button');
closeButton.style.border = 'none';
closeButton.style.background = 'transparent';
closeButton.style.cursor = 'pointer';
closeButton.innerHTML = 'x';
closeButton.addEventListener('click', () => {
editableClassesDiv.removeChild(inputField);
editableClassesDiv.removeChild(br);
editableClassesDiv.removeChild(closeButton);
});
editableClassesDiv.appendChild(closeButton);
editableClassesDiv.appendChild(inputField);
const br = document.createElement('br');
editableClassesDiv.appendChild(br);
});
// Create buttons for classes with confidence < 0.5
classes
.filter((cls) => cls.confidence < 0.5)
.forEach((cls) => {
const button = document.createElement('button');
button.textContent = cls.class;
button.onclick = () => {
const inputField = document.createElement('input');
inputField.type = 'text';
inputField.value = cls.class;
editableClassesDiv.appendChild(inputField);
button.remove();
};
suggestedClassesDiv.appendChild(button);
});
// Add event listener to "Add Class" button
addClassBtn.onclick = () => {
const inputField = document.createElement('input');
inputField.type = 'text';
editableClassesDiv.appendChild(inputField);
};
} catch (exception) {
console.log(exception);
}
};

3
static/upload.svg Normal file
View File

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 1024 1024" class="icon" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M736.68 435.86a173.773 173.773 0 0 1 172.042 172.038c0.578 44.907-18.093 87.822-48.461 119.698-32.761 34.387-76.991 51.744-123.581 52.343-68.202 0.876-68.284 106.718 0 105.841 152.654-1.964 275.918-125.229 277.883-277.883 1.964-152.664-128.188-275.956-277.883-277.879-68.284-0.878-68.202 104.965 0 105.842zM285.262 779.307A173.773 173.773 0 0 1 113.22 607.266c-0.577-44.909 18.09-87.823 48.461-119.705 32.759-34.386 76.988-51.737 123.58-52.337 68.2-0.877 68.284-106.721 0-105.842C132.605 331.344 9.341 454.607 7.379 607.266 5.417 759.929 135.565 883.225 285.262 885.148c68.284 0.876 68.2-104.965 0-105.841z" fill="#4A5699" /><path d="M339.68 384.204a173.762 173.762 0 0 1 172.037-172.038c44.908-0.577 87.822 18.092 119.698 48.462 34.388 32.759 51.743 76.985 52.343 123.576 0.877 68.199 106.72 68.284 105.843 0-1.964-152.653-125.231-275.917-277.884-277.879-152.664-1.962-275.954 128.182-277.878 277.879-0.88 68.284 104.964 68.199 105.841 0z" fill="#C45FA0" /><path d="M545.039 473.078c16.542 16.542 16.542 43.356 0 59.896l-122.89 122.895c-16.542 16.538-43.357 16.538-59.896 0-16.542-16.546-16.542-43.362 0-59.899l122.892-122.892c16.537-16.542 43.355-16.542 59.894 0z" fill="#F39A2B" /><path d="M485.17 473.078c16.537-16.539 43.354-16.539 59.892 0l122.896 122.896c16.538 16.533 16.538 43.354 0 59.896-16.541 16.538-43.361 16.538-59.898 0L485.17 532.979c-16.547-16.543-16.547-43.359 0-59.901z" fill="#F39A2B" /><path d="M514.045 634.097c23.972 0 43.402 19.433 43.402 43.399v178.086c0 23.968-19.432 43.398-43.402 43.398-23.964 0-43.396-19.432-43.396-43.398V677.496c0.001-23.968 19.433-43.399 43.396-43.399z" fill="#E5594F" /></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

11
styles/style.css Normal file
View File

@ -0,0 +1,11 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');
html {
font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
}

8
tailwind.config.js Normal file
View File

@ -0,0 +1,8 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['static/*.{html,js}'],
theme: {
extend: {},
},
plugins: [],
};