commit f677973feb96412a780c31b5bc2f7e500b109dff Author: tsb Date: Wed Dec 18 11:28:35 2024 -1000 initial commit diff --git a/baby_tracker/__init__.py b/baby_tracker/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/baby_tracker/__pycache__/__init__.cpython-313.pyc b/baby_tracker/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..b432471 Binary files /dev/null and b/baby_tracker/__pycache__/__init__.cpython-313.pyc differ diff --git a/baby_tracker/__pycache__/settings.cpython-313.pyc b/baby_tracker/__pycache__/settings.cpython-313.pyc new file mode 100644 index 0000000..7d8b316 Binary files /dev/null and b/baby_tracker/__pycache__/settings.cpython-313.pyc differ diff --git a/baby_tracker/__pycache__/urls.cpython-313.pyc b/baby_tracker/__pycache__/urls.cpython-313.pyc new file mode 100644 index 0000000..989e4ca Binary files /dev/null and b/baby_tracker/__pycache__/urls.cpython-313.pyc differ diff --git a/baby_tracker/__pycache__/wsgi.cpython-313.pyc b/baby_tracker/__pycache__/wsgi.cpython-313.pyc new file mode 100644 index 0000000..a275cf0 Binary files /dev/null and b/baby_tracker/__pycache__/wsgi.cpython-313.pyc differ diff --git a/baby_tracker/asgi.py b/baby_tracker/asgi.py new file mode 100644 index 0000000..fadaf40 --- /dev/null +++ b/baby_tracker/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for baby_tracker project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'baby_tracker.settings') + +application = get_asgi_application() diff --git a/baby_tracker/settings.py b/baby_tracker/settings.py new file mode 100644 index 0000000..18e8edd --- /dev/null +++ b/baby_tracker/settings.py @@ -0,0 +1,128 @@ +""" +Django settings for baby_tracker project. + +Generated by 'django-admin startproject' using Django 5.1.4. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.1/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-q_$lvms3r!+meum_0o&b^#vkf75+sy!kax9v^pyr@fipj6&2$a' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'main', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'baby_tracker.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'baby_tracker.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.1/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.1/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'Pacific/Honolulu' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.1/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +STATIC_URL = '/static/' +STATICFILES_DIRS = [BASE_DIR / "static"] + diff --git a/baby_tracker/urls.py b/baby_tracker/urls.py new file mode 100644 index 0000000..9d32d2d --- /dev/null +++ b/baby_tracker/urls.py @@ -0,0 +1,23 @@ +""" +URL configuration for baby_tracker project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.1/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, include + +urlpatterns = [ + path('admin/', admin.site.urls), + path('', include('main.urls')), +] diff --git a/baby_tracker/wsgi.py b/baby_tracker/wsgi.py new file mode 100644 index 0000000..74b55cc --- /dev/null +++ b/baby_tracker/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for baby_tracker project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'baby_tracker.settings') + +application = get_wsgi_application() diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000..74ecd2f Binary files /dev/null and b/db.sqlite3 differ diff --git a/main/__init__.py b/main/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main/__pycache__/__init__.cpython-313.pyc b/main/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..c844b6d Binary files /dev/null and b/main/__pycache__/__init__.cpython-313.pyc differ diff --git a/main/__pycache__/admin.cpython-313.pyc b/main/__pycache__/admin.cpython-313.pyc new file mode 100644 index 0000000..b674aec Binary files /dev/null and b/main/__pycache__/admin.cpython-313.pyc differ diff --git a/main/__pycache__/apps.cpython-313.pyc b/main/__pycache__/apps.cpython-313.pyc new file mode 100644 index 0000000..21168b3 Binary files /dev/null and b/main/__pycache__/apps.cpython-313.pyc differ diff --git a/main/__pycache__/forms.cpython-313.pyc b/main/__pycache__/forms.cpython-313.pyc new file mode 100644 index 0000000..ca2d16c Binary files /dev/null and b/main/__pycache__/forms.cpython-313.pyc differ diff --git a/main/__pycache__/models.cpython-313.pyc b/main/__pycache__/models.cpython-313.pyc new file mode 100644 index 0000000..9b87ed9 Binary files /dev/null and b/main/__pycache__/models.cpython-313.pyc differ diff --git a/main/__pycache__/urls.cpython-313.pyc b/main/__pycache__/urls.cpython-313.pyc new file mode 100644 index 0000000..d2d79da Binary files /dev/null and b/main/__pycache__/urls.cpython-313.pyc differ diff --git a/main/__pycache__/views.cpython-313.pyc b/main/__pycache__/views.cpython-313.pyc new file mode 100644 index 0000000..3eb3c95 Binary files /dev/null and b/main/__pycache__/views.cpython-313.pyc differ diff --git a/main/admin.py b/main/admin.py new file mode 100644 index 0000000..97ce168 --- /dev/null +++ b/main/admin.py @@ -0,0 +1,23 @@ +# admin.py +from django.contrib import admin +from .models import Feeding, Potty + + + +class PottyAdmin(admin.ModelAdmin): + # Show timestamp in the list view + list_display = ('timestamp', 'consistency', 'notes') # Add timestamp here + + # Allow timestamp to be editable in the form view + fields = ('consistency', 'notes') # Add timestamp here to the edit form + +admin.site.register(Potty, PottyAdmin) + +class FeedingAdmin(admin.ModelAdmin): + # Show timestamp in the list view + list_display = ('timestamp', 'feeding_type', 'amount', 'duration' , 'notes') # Add timestamp here + + # Allow timestamp to be editable in the form view + fields = ('feeding_type', 'amount', 'duration' , 'notes') # Add timestamp here to the edit form + +admin.site.register(Feeding, FeedingAdmin) \ No newline at end of file diff --git a/main/apps.py b/main/apps.py new file mode 100644 index 0000000..167f044 --- /dev/null +++ b/main/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class MainConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'main' diff --git a/main/forms.py b/main/forms.py new file mode 100644 index 0000000..f501e0f --- /dev/null +++ b/main/forms.py @@ -0,0 +1,37 @@ +from django import forms +from .models import Feeding, Potty + +class FeedingForm(forms.ModelForm): + class Meta: + model = Feeding + fields = ['feeding_type', 'amount', 'duration', 'notes'] + widgets = { + 'feeding_type': forms.Select(attrs={'class': 'form-control'}), + 'amount': forms.NumberInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Enter amount (if bottle) in ounces' + }), + 'duration': forms.TextInput(attrs={ + 'class': 'form-control', + 'placeholder': 'Enter duration (if breast) in minutes' + }), + 'notes': forms.Textarea(attrs={ + 'class': 'form-control', + 'placeholder': 'Additional notes (optional)', + 'rows': 3 + }), + } + +class PottyForm(forms.ModelForm): + class Meta: + model = Potty + fields = ['consistency', 'notes'] + widgets = { + 'consistency': forms.Select(attrs={'class': 'form-control'}), + 'notes': forms.Textarea(attrs={ + 'class': 'form-control', + 'placeholder': 'Additional notes (optional)', + 'rows': 3 + }) + } + diff --git a/main/migrations/0001_initial.py b/main/migrations/0001_initial.py new file mode 100644 index 0000000..233a7f0 --- /dev/null +++ b/main/migrations/0001_initial.py @@ -0,0 +1,34 @@ +# Generated by Django 5.1.4 on 2024-12-07 10:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Feeding', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('feeding_type', models.CharField(choices=[('bottle', 'Bottle'), ('breast', 'Breast')], max_length=10)), + ('amount', models.FloatField(blank=True, null=True)), + ('duration', models.FloatField(blank=True, null=True)), + ('notes', models.TextField(blank=True)), + ], + ), + migrations.CreateModel( + name='Potty', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('consistency', models.CharField(choices=[('solid', 'Solid'), ('soft', 'Soft'), ('watery', 'Watery'), ('pee', 'Pee'), ('other', 'Other')], max_length=50)), + ('notes', models.TextField(blank=True)), + ], + ), + ] diff --git a/main/migrations/0002_alter_feeding_amount.py b/main/migrations/0002_alter_feeding_amount.py new file mode 100644 index 0000000..5dcbc53 --- /dev/null +++ b/main/migrations/0002_alter_feeding_amount.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.4 on 2024-12-07 18:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='feeding', + name='amount', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/main/migrations/0003_alter_feeding_amount_alter_feeding_duration.py b/main/migrations/0003_alter_feeding_amount_alter_feeding_duration.py new file mode 100644 index 0000000..6fb4fdf --- /dev/null +++ b/main/migrations/0003_alter_feeding_amount_alter_feeding_duration.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.4 on 2024-12-07 18:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0002_alter_feeding_amount'), + ] + + operations = [ + migrations.AlterField( + model_name='feeding', + name='amount', + field=models.FloatField(blank=True, null=True), + ), + migrations.AlterField( + model_name='feeding', + name='duration', + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/main/migrations/__init__.py b/main/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/main/migrations/__pycache__/0001_initial.cpython-313.pyc b/main/migrations/__pycache__/0001_initial.cpython-313.pyc new file mode 100644 index 0000000..5b3c70a Binary files /dev/null and b/main/migrations/__pycache__/0001_initial.cpython-313.pyc differ diff --git a/main/migrations/__pycache__/0002_alter_feeding_amount.cpython-313.pyc b/main/migrations/__pycache__/0002_alter_feeding_amount.cpython-313.pyc new file mode 100644 index 0000000..5edf86b Binary files /dev/null and b/main/migrations/__pycache__/0002_alter_feeding_amount.cpython-313.pyc differ diff --git a/main/migrations/__pycache__/0003_alter_feeding_amount_alter_feeding_duration.cpython-313.pyc b/main/migrations/__pycache__/0003_alter_feeding_amount_alter_feeding_duration.cpython-313.pyc new file mode 100644 index 0000000..49ba138 Binary files /dev/null and b/main/migrations/__pycache__/0003_alter_feeding_amount_alter_feeding_duration.cpython-313.pyc differ diff --git a/main/migrations/__pycache__/__init__.cpython-313.pyc b/main/migrations/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..c8ffdaf Binary files /dev/null and b/main/migrations/__pycache__/__init__.cpython-313.pyc differ diff --git a/main/models.py b/main/models.py new file mode 100644 index 0000000..acde26f --- /dev/null +++ b/main/models.py @@ -0,0 +1,34 @@ +from django.db import models + +# Create your models here. +from django.db import models + +class Feeding(models.Model): + FEEDING_TYPES = [ + ('bottle', 'Bottle'), + ('breast', 'Breast'), + ] + + timestamp = models.DateTimeField(auto_now_add=True) + feeding_type = models.CharField(max_length=10, choices=FEEDING_TYPES) + amount = models.FloatField(null=True, blank=True) # Store ounces for bottle feed + duration = models.IntegerField(null=True, blank=True) # Store duration in minutes for breastfeed + notes = models.TextField(blank=True) + + def __str__(self): + return f"{self.get_feeding_type_display()} - {self.timestamp}" + + +class Potty(models.Model): + timestamp = models.DateTimeField(auto_now_add=True) + consistency = models.CharField(max_length=50, choices=[ + ('solid', 'Solid'), + ('soft', 'Soft'), + ('watery', 'Watery'), + ('pee', 'Pee'), + ('other', 'Other'), + ]) + notes = models.TextField(blank=True) + + def __str__(self): + return f"{self.get_potty_type_display()} - {self.timestamp}" diff --git a/main/templates/main/base.html b/main/templates/main/base.html new file mode 100644 index 0000000..36b426f --- /dev/null +++ b/main/templates/main/base.html @@ -0,0 +1,36 @@ + + + + + + + Baby Tracker + + + + + + + + + + +
+
+

Caelum's Poop and Feed Tracker

+
+
+ {% block content %}{% endblock %} +
+
+ + + + + + + + \ No newline at end of file diff --git a/main/templates/main/index.html b/main/templates/main/index.html new file mode 100644 index 0000000..a90c408 --- /dev/null +++ b/main/templates/main/index.html @@ -0,0 +1,5 @@ +{% extends 'main/base.html' %} +{% block content %} +{% include 'main/partials/feed_section.html' %} +{% include 'main/partials/potty_section.html' %} +{% endblock %} \ No newline at end of file diff --git a/main/templates/main/partials/add_feed.html b/main/templates/main/partials/add_feed.html new file mode 100644 index 0000000..9ede11e --- /dev/null +++ b/main/templates/main/partials/add_feed.html @@ -0,0 +1,40 @@ +
+ {% csrf_token %} +
+ {{ form.feeding_type.label_tag }} + {{ form.feeding_type }} +
+ + +
+ {{ form.notes.label_tag }} + {{ form.notes }} +
+ + +
+ + diff --git a/main/templates/main/partials/add_potty.html b/main/templates/main/partials/add_potty.html new file mode 100644 index 0000000..43b593c --- /dev/null +++ b/main/templates/main/partials/add_potty.html @@ -0,0 +1,13 @@ +
+ {% csrf_token %} +
+ {{form.consistency.label_tag}} + {{form.consistency}} +
+
+ {{form.notes.label_tag}} + {{form.notes}} +
+ + +
\ No newline at end of file diff --git a/main/templates/main/partials/edit_feed_row.html b/main/templates/main/partials/edit_feed_row.html new file mode 100644 index 0000000..45f907d --- /dev/null +++ b/main/templates/main/partials/edit_feed_row.html @@ -0,0 +1,37 @@ + + + + + + {% if feeding.feeding_type == 'bottle' %} + + {% elif feeding.feeding_type == 'breast' %} + + {% else %} + + {% endif %} + + + + + + + + + + + diff --git a/main/templates/main/partials/edit_potty_row.html b/main/templates/main/partials/edit_potty_row.html new file mode 100644 index 0000000..9d50c33 --- /dev/null +++ b/main/templates/main/partials/edit_potty_row.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + diff --git a/main/templates/main/partials/feed_row.html b/main/templates/main/partials/feed_row.html new file mode 100644 index 0000000..3edce0d --- /dev/null +++ b/main/templates/main/partials/feed_row.html @@ -0,0 +1,30 @@ + + {{ feeding.timestamp }} + + {% if feeding.feeding_type == 'breast' %} + {{ feeding.duration }} min + {% else %} + {{ feeding.amount }} oz + {% endif %} + + {{ feeding.notes }} + + + + + + + + + diff --git a/main/templates/main/partials/feed_section.html b/main/templates/main/partials/feed_section.html new file mode 100644 index 0000000..bfd7300 --- /dev/null +++ b/main/templates/main/partials/feed_section.html @@ -0,0 +1,58 @@ +
+
+

Feed Records

+ +
+ +
+ + + + + + + + + + + + {% for feeding in feedings %} + + + + + + + {% endfor %} + + +
DateAmount or DurationNotesActions
{{ feeding.timestamp }}{% if feeding.feeding_type == 'breast' %}{{feeding.duration}} min{% else %}{{feeding.amount}} oz{% endif %}{{feeding.notes}} + + +
+
+ + + + +
+
+ diff --git a/main/templates/main/partials/potty_row.html b/main/templates/main/partials/potty_row.html new file mode 100644 index 0000000..6e28b83 --- /dev/null +++ b/main/templates/main/partials/potty_row.html @@ -0,0 +1,21 @@ + + {{ potty.timestamp }} + {{ potty.consistency }} + {{ potty.notes }} + + + + + \ No newline at end of file diff --git a/main/templates/main/partials/potty_section.html b/main/templates/main/partials/potty_section.html new file mode 100644 index 0000000..d350739 --- /dev/null +++ b/main/templates/main/partials/potty_section.html @@ -0,0 +1,51 @@ +
+
+

Potty Records

+ +
+
+ + + + + + + + + + + + {% for potty in potties %} + + + + + + + {% endfor %} + + +
DateTypeNotesActions
{{ potty.timestamp }}{{ potty.consistency }}{{ potty.notes }} + + +
+
+ + + +
+
\ No newline at end of file diff --git a/main/tests.py b/main/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/main/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/main/urls.py b/main/urls.py new file mode 100644 index 0000000..decac41 --- /dev/null +++ b/main/urls.py @@ -0,0 +1,18 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('', views.index, name='feedings_list'), + path('add_feed', views.add_feed, name='add_feed'), + path('delete_feed/', views.delete_feed, name='delete_feed'), + path('edit_feed/', views.edit_feed, name='edit_feed'), + path('update_feed/', views.update_feed, name='update_feed'), + path('cancel_edit_feed/', views.cancel_edit_feed, name='cancel_edit_feed'), + path('add_potty', views.add_potty, name='add_potty'), + path('delete_potty/', views.delete_potty, name='delete_potty'), + path('edit_potty/', views.edit_potty, name='edit_potty'), + path('update_potty/', views.update_potty, name='update_potty'), + path('cancel_edit_potty/', views.cancel_edit_potty, name='cancel_edit_potty'), + path('export_feedings_csv', views.export_feedings_csv, name='export_feedings_csv'), + path('export_potties_csv', views.export_potties_csv, name='export_potties_csv'), +] diff --git a/main/views.py b/main/views.py new file mode 100644 index 0000000..71d972c --- /dev/null +++ b/main/views.py @@ -0,0 +1,191 @@ +from django.shortcuts import render, get_object_or_404 +from .models import Feeding, Potty +from django.http import HttpResponse +from django.views.decorators.csrf import csrf_exempt +from .forms import FeedingForm, PottyForm + + +def index(request): + context = {} + + feedings = Feeding.objects.all().order_by('-timestamp') + + context['feedings'] = feedings + + # Grab all objects sorted by timestamp (most recent first) + potties = Potty.objects.all().order_by('-timestamp') + + + context['potties'] = potties + + return render(request, 'main/index.html', context) + + +def add_feed(request): + context = {} + if request.method == 'POST': + form = FeedingForm(request.POST) + if form.is_valid(): + feeding_type = form.cleaned_data['feeding_type'] + amount = form.cleaned_data['amount'] + duration = form.cleaned_data['duration'] + # Validate based on feeding type + if feeding_type == 'bottle' and not amount: + form.add_error('amount', 'Amount is required for bottle feeding.') + elif feeding_type == 'breast' and not duration: + form.add_error('duration', 'Duration is required for breast feeding.') + else: + form.save() + feedings = Feeding.objects.all().order_by('-timestamp') + context['feedings'] = feedings + return render(request, 'main/partials/feed_section.html', context) + + else: + form = FeedingForm() + + return render(request, 'main/partials/add_feed.html', {'form': form}) + + +@csrf_exempt +def delete_feed(request, feed_id): + context = {} + if request.method == "DELETE": + feed = get_object_or_404(Feeding, id=int(feed_id)) + feed.delete() + feedings = Feeding.objects.all().order_by('-timestamp') + context['feedings'] = feedings + return render(request, 'main/partials/feed_section.html', context) # Return a success response + return HttpResponse(status=405) # Method not allowed + +from django.shortcuts import get_object_or_404, render + +@csrf_exempt +def edit_feed(request, feed_id): + feed = get_object_or_404(Feeding, id=feed_id) + return render(request, 'main/partials/edit_feed_row.html', {'feeding': feed}) + + +@csrf_exempt +def update_feed(request, feed_id): + + if request.method == "POST": + feed = get_object_or_404(Feeding, id=feed_id) + feed.timestamp = request.POST['timestamp'] + if feed.feeding_type == 'bottle': + feed.amount = float(request.POST['amount_or_duration']) + else: + feed.duration = int(float(request.POST['amount_or_duration'])) + feed.notes = request.POST['notes'] + feed.save() + return render(request, 'main/partials/feed_row.html', {'feeding': feed}) + +def cancel_edit_feed(request, feed_id): + feed = get_object_or_404(Feeding, id=feed_id) + return render(request, 'main/partials/feed_row.html', {'feeding': feed}) + +def add_potty(request): + context = {} + if request.method == 'POST': + form = PottyForm(request.POST) + if form.is_valid(): + form.save() + potties = Potty.objects.all().order_by('-timestamp') + context['potties'] = potties + return render(request, 'main/partials/potty_section.html', context) + else: + form = PottyForm() + + return render(request, 'main/partials/add_potty.html', {'form': form}) + +@csrf_exempt +def delete_potty(request, potty_id): + context = {} + if request.method == 'DELETE': + potty = get_object_or_404(Potty, id=int(potty_id)) + potty.delete() + potties = Potty.objects.all().order_by('-timestamp') + context['potties'] = potties + return render(request, 'main/partials/potty_section.html', context) + return HttpResponse(status=405) + +@csrf_exempt +def edit_potty(request, potty_id): + context = {} + potty = get_object_or_404(Potty, id=potty_id) + context['potty'] = potty + form = PottyForm(potty) + context['form'] = form + return render(request, 'main/partials/edit_potty_row.html', context) + + +@csrf_exempt +def update_potty(request, potty_id): + context = {} + if request.method == 'POST': + potty = get_object_or_404(Potty, id=potty_id) + potty.timestamp = request.POST['timestamp'] + potty.consistency = request.POST['consistency'] + potty.notes = request.POST['notes'] + potty.save() + return render(request, 'main/partials/potty_row.html', {'potty': potty}) + + +def cancel_edit_potty(request, potty_id): + potty = get_object_or_404(Potty, id=potty_id) + return render(request, 'main/partials/potty_row.html', {'potty': potty}) + +import csv +from django.http import HttpResponse + +def export_feedings_csv(request): + # Create the HTTP response with CSV headers + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = 'attachment; filename="feedings.csv"' + + # Create a CSV writer + writer = csv.writer(response) + # Write the header row + writer.writerow(['Timestamp', 'Feeding Type', 'Amount', 'Duration', 'Notes']) + + # Fetch the data from the database + feedings = Feeding.objects.all().order_by('-timestamp') + for feeding in feedings: + writer.writerow([ + feeding.timestamp, + feeding.get_feeding_type_display(), # Use readable choices if applicable + feeding.amount if feeding.amount else "", + feeding.duration if feeding.duration else "", + feeding.notes, + ]) + + return response + +def export_potties_csv(request): + # Create the HTTP response with CSV headers + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = 'attachment; filename="potties.csv"' + + # Create a CSV writer + writer = csv.writer(response) + # Write the header row + writer.writerow(['Timestamp', 'Consistency', 'Notes']) + + # Fetch the potty data from the database + potties = Potty.objects.all().order_by('-timestamp') + for potty in potties: + writer.writerow([ + potty.timestamp, + potty.get_consistency_display() if potty.consistency else "", + potty.notes, + ]) + + return response + + + + + + + + + diff --git a/manage.py b/manage.py new file mode 100644 index 0000000..6c1bf8a --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'baby_tracker.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..fe37b57 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +asgiref==3.8.1 +Django==5.1.4 +sqlparse==0.5.2 +tzdata==2024.2