mirror of
https://github.com/sstent/foodplanner.git
synced 2025-12-05 23:51:46 +00:00
starting templates
This commit is contained in:
35
GEMINI.md
Normal file
35
GEMINI.md
Normal file
@@ -0,0 +1,35 @@
|
||||
## Project Overview
|
||||
|
||||
This is a Meal Planner application built with FastAPI. It allows users to manage a food database, create meals from foods, and generate 2-week meal plans. The application uses SQLAlchemy for database interactions and Jinja2 for templating the user interface.
|
||||
|
||||
**Key Features:**
|
||||
* **Foods** - Manage a database of food items with nutritional information.
|
||||
* **Meals** - Create custom meals by combining various food items.
|
||||
* **Plans** - Generate and manage 2-week meal plans, including daily nutritional totals.
|
||||
* **Detailed Planner** - View a detailed breakdown of meals for a specific day.
|
||||
|
||||
## Building and Running
|
||||
|
||||
This project uses `uvicorn` to serve the FastAPI application.
|
||||
|
||||
1. **Install Dependencies:**
|
||||
Ensure you have Python 3.7+ installed. Install the required Python packages using pip:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. **Run the Application:**
|
||||
Start the FastAPI server using uvicorn. The `--reload` flag enables auto-reloading on code changes, which is useful for development.
|
||||
```bash
|
||||
uvicorn main:app --reload
|
||||
```
|
||||
The application will typically be accessible at `http://127.0.0.1:8000` (or the port specified in `main.py` if different, e.g., `8999`).
|
||||
|
||||
## Development Conventions
|
||||
|
||||
* **Framework:** FastAPI is used for the backend API and serving HTML templates.
|
||||
* **Database:** SQLAlchemy is used as the ORM for interacting with the SQLite database (`meal_planner.db`).
|
||||
* **Templating:** Jinja2 is used for rendering HTML pages. Templates are located in the `templates/` directory.
|
||||
* **Dependency Injection:** Database sessions are managed using FastAPI's dependency injection system (`Depends(get_db)`).
|
||||
* **Pydantic Models:** Pydantic models are used for data validation and serialization of API requests and responses.
|
||||
* **Static Files:** Static files (CSS, JS, images) are served from the `static/` directory (though not explicitly shown in the provided file list, it's a common FastAPI convention).
|
||||
83
templates/imports.html
Normal file
83
templates/imports.html
Normal file
@@ -0,0 +1,83 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h3>Food Import</h3>
|
||||
<form action="/foods/upload" method="post" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">CSV File</label>
|
||||
<input type="file" class="form-control" name="file" accept=".csv" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary mb-4">Upload Foods CSV</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h3>Meal Import</h3>
|
||||
<form action="/meals/upload" method="post" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">CSV File</label>
|
||||
<input type="file" class="form-control" name="file" accept=".csv" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-secondary mb-4">Upload Meals CSV</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4" id="upload-results" style="display: none;">
|
||||
<div class="alert alert-success">
|
||||
<strong>Upload Results:</strong>
|
||||
<span id="created-count"></span> created,
|
||||
<span id="updated-count"></span> updated
|
||||
<div id="error-list" class="mt-2 text-danger"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// CSV upload handling (copied from foods.html)
|
||||
document.querySelectorAll('form').forEach(form => {
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const submitBtn = form.querySelector('button[type="submit"]');
|
||||
const resultsDiv = document.getElementById('upload-results');
|
||||
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status"></span> Uploading...';
|
||||
|
||||
try {
|
||||
const formData = new FormData(form);
|
||||
const response = await fetch(form.action, {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
||||
|
||||
const results = await response.json();
|
||||
resultsDiv.style.display = 'block';
|
||||
document.getElementById('created-count').textContent = results.created || 0;
|
||||
document.getElementById('updated-count').textContent = results.updated || 0;
|
||||
|
||||
if (results.errors?.length > 0) {
|
||||
document.getElementById('error-list').innerHTML =
|
||||
`<strong>Errors (${results.errors.length}):</strong><br>` + results.errors.join('<br>');
|
||||
} else {
|
||||
document.getElementById('error-list').innerHTML = '';
|
||||
}
|
||||
|
||||
if (results.created || results.updated) {
|
||||
setTimeout(() => window.location.reload(), 2000);
|
||||
}
|
||||
} catch (error) {
|
||||
resultsDiv.style.display = 'block';
|
||||
resultsDiv.querySelector('.alert').className = 'alert alert-danger';
|
||||
document.getElementById('error-list').innerHTML =
|
||||
`<strong>Upload Failed:</strong> ${error.message}`;
|
||||
} finally {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = submitBtn.textContent.replace('Uploading...', 'Upload CSV');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
4
templates/plans.html
Normal file
4
templates/plans.html
Normal file
@@ -0,0 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<h1>Templates Page</h1>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user