diff options
| -rw-r--r-- | apps/cultivation/forms.py | 18 | ||||
| -rw-r--r-- | apps/cultivation/migrations/0001_initial.py | 111 | ||||
| -rw-r--r-- | apps/cultivation/migrations/0002_mindmaterial_user.py | 22 | ||||
| -rw-r--r-- | apps/cultivation/models.py | 17 | ||||
| -rw-r--r-- | apps/cultivation/templates/cultivation/mind-event-add.html | 18 | ||||
| -rw-r--r-- | apps/cultivation/templates/cultivation/mind-materials.html | 69 | ||||
| -rw-r--r-- | apps/cultivation/templates/cultivation/mind-nav.html | 14 | ||||
| -rw-r--r-- | apps/cultivation/templates/cultivation/mind.html | 41 | ||||
| -rw-r--r-- | apps/cultivation/urls.py | 18 | ||||
| -rw-r--r-- | apps/cultivation/views.py | 121 | ||||
| -rw-r--r-- | fsweb/settings.py | 3 |
11 files changed, 426 insertions, 26 deletions
diff --git a/apps/cultivation/forms.py b/apps/cultivation/forms.py new file mode 100644 index 0000000..425f1d5 --- /dev/null +++ b/apps/cultivation/forms.py @@ -0,0 +1,18 @@ +from django import forms + + +from .models import MindMaterial +from .models import MindEvent + + +class MindMaterialForm(forms.ModelForm): + class Meta: + model = MindMaterial + fields = ["name"] + + +class MindEventForm(forms.ModelForm): + class Meta: + model = MindEvent + fields = ["date", "material", "duration"] + diff --git a/apps/cultivation/migrations/0001_initial.py b/apps/cultivation/migrations/0001_initial.py new file mode 100644 index 0000000..cb10caf --- /dev/null +++ b/apps/cultivation/migrations/0001_initial.py @@ -0,0 +1,111 @@ +# Generated by Django 5.2.7 on 2025-11-15 18:04 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='CardioExercise', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=32)), + ], + options={ + 'db_table': 'cardio_exercises', + }, + ), + migrations.CreateModel( + name='FlexibilityExercise', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=32)), + ], + options={ + 'db_table': 'flexibility_exercises', + }, + ), + migrations.CreateModel( + name='MindMaterial', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=64, unique=True)), + ], + options={ + 'db_table': 'mind_materials', + }, + ), + migrations.CreateModel( + name='StrengthExercise', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=32)), + ], + options={ + 'db_table': 'strength_exercises', + }, + ), + migrations.CreateModel( + name='CardioEvent', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateField()), + ('duration', models.DurationField()), + ('distance', models.DecimalField(decimal_places=3, max_digits=3)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('exercise', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cultivation.cardioexercise')), + ], + options={ + 'db_table': 'cardio_events', + }, + ), + migrations.CreateModel( + name='FlexibilityEvent', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateField()), + ('duration', models.DurationField()), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('exercise', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cultivation.flexibilityexercise')), + ], + options={ + 'db_table': 'flexibility_events', + }, + ), + migrations.CreateModel( + name='MindEvent', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateField()), + ('duration', models.DurationField()), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('material', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cultivation.mindmaterial')), + ], + options={ + 'db_table': 'mind_events', + }, + ), + migrations.CreateModel( + name='StrengthEvent', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateField()), + ('weight', models.DecimalField(decimal_places=3, max_digits=3)), + ('reps', models.SmallIntegerField()), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ('exercise', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='cultivation.strengthexercise')), + ], + options={ + 'db_table': 'strength_events', + }, + ), + ] diff --git a/apps/cultivation/migrations/0002_mindmaterial_user.py b/apps/cultivation/migrations/0002_mindmaterial_user.py new file mode 100644 index 0000000..1297de8 --- /dev/null +++ b/apps/cultivation/migrations/0002_mindmaterial_user.py @@ -0,0 +1,22 @@ +# Generated by Django 5.2.7 on 2025-11-27 01:13 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cultivation', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='mindmaterial', + name='user', + field=models.ForeignKey(default='', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + ] diff --git a/apps/cultivation/models.py b/apps/cultivation/models.py index d28cc79..0fc9705 100644 --- a/apps/cultivation/models.py +++ b/apps/cultivation/models.py @@ -2,26 +2,15 @@ from django.db import models from django.contrib.auth.models import User - -class MindSubject(models.Model): - name = models.CharField(max_length=32, unique=True) - - class Meta: - db_table = "mind_subjects" - - def __str__(self): - return f"{self.name}" - - class MindMaterial(models.Model): - subject = models.ForeignKey(MindSubject, on_delete=models.CASCADE) - material = models.CharField(max_length=32, unique=True) + user = models.ForeignKey(User, on_delete=models.CASCADE) + name = models.CharField(max_length=64, unique=True) class Meta: db_table = "mind_materials" def __str__(self): - return f"{self.subject},{self.material}" + return f"{self.name}" class MindEvent(models.Model): diff --git a/apps/cultivation/templates/cultivation/mind-event-add.html b/apps/cultivation/templates/cultivation/mind-event-add.html new file mode 100644 index 0000000..7f3cdab --- /dev/null +++ b/apps/cultivation/templates/cultivation/mind-event-add.html @@ -0,0 +1,18 @@ +{% extends 'base.html' %} + +{% block title %}Add Mind Event - fsweb{% endblock %} + +{% block content %} + + +{% include "cultivation/mind-nav.html" %} + + +<form action="{% url 'cultivation:mind_event_add' %}" method="post"> + {% csrf_token %} + {{ form.as_p }} + <button type="submit">Add Event</button> +</form> + + +{% endblock %} diff --git a/apps/cultivation/templates/cultivation/mind-materials.html b/apps/cultivation/templates/cultivation/mind-materials.html new file mode 100644 index 0000000..ba5ea06 --- /dev/null +++ b/apps/cultivation/templates/cultivation/mind-materials.html @@ -0,0 +1,69 @@ +{% extends 'base.html' %} + +{% block title %}Mind Materials - fsweb{% endblock %} + +{% block content %} + + +{% include "cultivation/mind-nav.html" %} + + +{% if form.instance.pk %} + +<form action="{% url 'cultivation:mind_materials_pk' form.instance.pk %}" method="post"> + {% csrf_token %} + {{ form.as_p }} + + <button type="submit"> + Submit + </button> +</form> + +{% else %} + +<form action="{% url 'cultivation:mind_materials' %}" method="post"> + {% csrf_token %} + {{ form.as_p }} + + <button type="submit"> + Submit + </button> +</form> + +{% endif %} + + +<p>Summary</p> +<table> + <tr> + <th>Material</th> + <th></th> + </tr> + {% for material in materials %} + <tr> + <td>{{material.name}}</td> + <td> + <form action="{% url 'cultivation:mind_materials_pk' material.pk %}" method="get"> + {% csrf_token %} + <button type="submit"> + Edit + </button> + </form> + </td> + <td> + <form action="{% url 'cultivation:mind_material_delete' material.pk %}" method="post"> + {% csrf_token %} + <button type="submit"> + Delete + </button> + </form> + </td> + </tr> + {% endfor %} +</table> +<ul> +</ul> + + +{% endblock %} + diff --git a/apps/cultivation/templates/cultivation/mind-nav.html b/apps/cultivation/templates/cultivation/mind-nav.html new file mode 100644 index 0000000..9f6cbf6 --- /dev/null +++ b/apps/cultivation/templates/cultivation/mind-nav.html @@ -0,0 +1,14 @@ +{% load static %} + +<a href="{% url 'cultivation:mind' %}"> + <button type="button">Mind Events</button> +</a> + +<a href="{% url 'cultivation:mind_materials' %}"> + <button type="button">Mind Materials</button> +</a> + +<a href="{% url 'cultivation:mind_event_add' %}"> + <button type="button">Add Event</button> +</a> + diff --git a/apps/cultivation/templates/cultivation/mind.html b/apps/cultivation/templates/cultivation/mind.html new file mode 100644 index 0000000..a9cbe31 --- /dev/null +++ b/apps/cultivation/templates/cultivation/mind.html @@ -0,0 +1,41 @@ +{% extends 'base.html' %} + +{% block title %}Cultivation - fsweb{% endblock %} + +{% block content %} + + +{% include "cultivation/mind-nav.html" %} + + +<table> + <caption>Summary</caption> + <tr> + <th width="150">Date</th> + <th width="100">Material</th> + <th width="100">Duration</th> + <th></th> + </tr> + {% for event in events %} + <tr> + <td>{{event.date|date:"Y-m-d"}}</td> + <td>{{event.material.name}}</td> + <td>{{event.duration}}</td> + <td> + <form action="{% url 'cultivation:mind_event_delete' event.pk %}" + method="post"> + {% csrf_token %} + <button type="submit" + aria-label="Delete" + onclick="return confirm('Are you sure you want to delete this?')"> + Delete + </button> + </form> + </td> + </tr> + {% endfor %} +</table> + + +{% endblock %} + diff --git a/apps/cultivation/urls.py b/apps/cultivation/urls.py index f56b4cf..617bdc7 100644 --- a/apps/cultivation/urls.py +++ b/apps/cultivation/urls.py @@ -4,9 +4,17 @@ from . import views app_name = 'cultivation' urlpatterns = [ - path("", views.view_index, name="index"), - path("cardio", views.view_cardio, name="cardio"), - path("strength", views.view_strength, name="strength"), - path("flexibility", views.view_flexibility, name="flexibility"), - path("mind", views.view_mind, name="mind"), + path("", views.index, name="index"), + path("cardio", views.cardio, name="cardio"), + path("strength", views.strength, name="strength"), + path("flexibility", views.flexibility, name="flexibility"), + + + path("mind", views.mind, name="mind"), + path("mind/event-add", views.mind_event_add, name="mind_event_add"), + path("mind/event-delete/<int:pk>", views.mind_event_delete, name="mind_event_delete"), + + path("mind/materials", views.mind_materials, name="mind_materials"), + path("mind/materials/<int:pk>", views.mind_materials, name="mind_materials_pk"), + path("mind/material_delete/<int:pk>", views.mind_material_delete, name="mind_material_delete") ] diff --git a/apps/cultivation/views.py b/apps/cultivation/views.py index 6db6307..9bd203a 100644 --- a/apps/cultivation/views.py +++ b/apps/cultivation/views.py @@ -1,17 +1,124 @@ -from django.shortcuts import render +from django.shortcuts import render, redirect, get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.contrib import messages + +from . import forms +from .models import * -def view_index(request): - return render(request, 'cultivation/index.html', {'form': None}) -def view_cardio(request): +def index(request): return render(request, 'cultivation/index.html', {'form': None}) -def view_strength(request): + +def cardio(request): return render(request, 'cultivation/index.html', {'form': None}) -def view_flexibility(request): + +def strength(request): return render(request, 'cultivation/index.html', {'form': None}) -def view_mind(request): + +def flexibility(request): return render(request, 'cultivation/index.html', {'form': None}) + +@login_required +def mind(request): + events = MindEvent.objects.all().order_by('-date') + + context = dict() + context['events'] = events + + return render(request, 'cultivation/mind.html', context) + + +@login_required +def mind_event_add(request): + context = dict() + context['form'] = forms.MindEventForm() + + if request.method == 'POST': + form = forms.MindEventForm(request.POST) + if form.is_valid(): + obj = form.save(commit=False) + obj.user = request.user + obj.save() + return redirect('cultivation:mind') + else: + print("MindEventForm is invalid!") + print(form.errors) + print(form.non_field_errors()) + print(form.errors.as_data()) + + + return render(request, 'cultivation/mind-event-add.html', context) + + +@login_required +def mind_event_delete(request, pk): + event = get_object_or_404(MindEvent, pk=pk) + + # security check + if event.user != request.user and not request.user.is_staff: + messages.error(request, "You can't delete this event.") + return redirect('cultivation:mind') + + # delete + if request.method == 'POST': + event.delete() + + return redirect('cultivation:mind') + + +@login_required +def mind_materials(request, pk=None): + materials = MindMaterial.objects.all() + context = dict() + context['materials'] = materials + + if pk: + edit_material = get_object_or_404(MindMaterial, pk=pk) + if edit_material.user != request.user and not request.user.is_staff: + messages.error(request, "You can't edit this material.") + return redirect('cultivation:mind_materials') + + if request.method == 'POST': + form = forms.MindMaterialForm(request.POST, instance=edit_material) + if form.is_valid(): + obj = form.save() + return redirect('cultivation:mind_materials') + else: + messages.error(request, "You can't edit this material.") + else: + context['form'] = forms.MindMaterialForm(instance=edit_material) + else: + if request.method == 'POST': + form = forms.MindMaterialForm(request.POST) + if form.is_valid(): + obj = form.save(commit=False) + obj.user = request.user + obj.save() + return redirect('cultivation:mind_materials') + else: + messages.error(request, "You can't delete this material.") + else: + context['form'] = forms.MindMaterialForm() + + return render(request, 'cultivation/mind-materials.html', context) + + +@login_required +def mind_material_delete(request, pk): + material = get_object_or_404(MindMaterial, pk=pk) + + # security check + if request.user != material.user and not request.user.is_staff: + messages.error(request, "You can't delete this material.") + return redirect('cultivation:mind_materials') + + # delete + if request.method == "POST": + material.delete() + + return redirect('cultivation:mind_materials') + diff --git a/fsweb/settings.py b/fsweb/settings.py index c86db78..0c2ca51 100644 --- a/fsweb/settings.py +++ b/fsweb/settings.py @@ -23,6 +23,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent # See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ + # Basic Settings is_env_production = os.getenv('DJANGO_ENV') == 'production' if is_env_production: @@ -47,6 +48,8 @@ if is_env_production: # Application definition +LOGIN_URL = '/accounts/login' # or '/login/' or 'accounts/login/' + INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', |
