From: VGoncalo Date: Sun, 3 May 2026 23:10:32 +0000 (+0100) Subject: core functionality for webapp X-Git-Url: https://vgcfreebox.myrthtech.pt/gitweb/ue-cc-donkeysponsor.git/commitdiff_plain/73e00a9ac22c09337251fa247fb22a20349dc501 core functionality for webapp --- 73e00a9ac22c09337251fa247fb22a20349dc501 diff --git a/core/.DS_Store b/core/.DS_Store new file mode 100644 index 0000000..82af984 Binary files /dev/null and b/core/.DS_Store differ diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/__pycache__/__init__.cpython-314.pyc b/core/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000..fcc4396 Binary files /dev/null and b/core/__pycache__/__init__.cpython-314.pyc differ diff --git a/core/__pycache__/admin.cpython-314.pyc b/core/__pycache__/admin.cpython-314.pyc new file mode 100644 index 0000000..3ae7eb3 Binary files /dev/null and b/core/__pycache__/admin.cpython-314.pyc differ diff --git a/core/__pycache__/apps.cpython-314.pyc b/core/__pycache__/apps.cpython-314.pyc new file mode 100644 index 0000000..b21d0bc Binary files /dev/null and b/core/__pycache__/apps.cpython-314.pyc differ diff --git a/core/__pycache__/forms.cpython-314.pyc b/core/__pycache__/forms.cpython-314.pyc new file mode 100644 index 0000000..cdbf206 Binary files /dev/null and b/core/__pycache__/forms.cpython-314.pyc differ diff --git a/core/__pycache__/models.cpython-314.pyc b/core/__pycache__/models.cpython-314.pyc new file mode 100644 index 0000000..f4864a9 Binary files /dev/null and b/core/__pycache__/models.cpython-314.pyc differ diff --git a/core/__pycache__/urls.cpython-314.pyc b/core/__pycache__/urls.cpython-314.pyc new file mode 100644 index 0000000..562f226 Binary files /dev/null and b/core/__pycache__/urls.cpython-314.pyc differ diff --git a/core/__pycache__/views.cpython-314.pyc b/core/__pycache__/views.cpython-314.pyc new file mode 100644 index 0000000..c3cc480 Binary files /dev/null and b/core/__pycache__/views.cpython-314.pyc differ diff --git a/core/admin.py b/core/admin.py new file mode 100644 index 0000000..0b786f6 --- /dev/null +++ b/core/admin.py @@ -0,0 +1,25 @@ +from django.contrib import admin +from .models import Profile, Donkey, Sponsorship, Post + +@admin.register(Profile) +class ProfileAdmin(admin.ModelAdmin): + list_display = ('name', 'description') + + +@admin.register(Donkey) +class DonkeyAdmin(admin.ModelAdmin): + list_display = ('name', 'race', 'location', 'owner') + search_fields = ('name', 'race') + +@admin.register(Sponsorship) +class SponsorshipAdmin(admin.ModelAdmin): + list_display = ('user', 'donkey', 'amount', 'start_date') + list_filter = ('start_date', 'type') + +@admin.register(Post) +class PostAdmin(admin.ModelAdmin): + list_display = ('donkey', 'date', 'description_snippet') + readonly_fields = ('date',) + + def description_snippet(self, obj): + return obj.description[:50] + "..." \ No newline at end of file diff --git a/core/apps.py b/core/apps.py new file mode 100644 index 0000000..26f78a8 --- /dev/null +++ b/core/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CoreConfig(AppConfig): + name = 'core' diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..0a76df2 --- /dev/null +++ b/core/forms.py @@ -0,0 +1,41 @@ +from django import forms +from django.contrib.auth.models import User +from django.contrib.auth.forms import UserCreationForm, AuthenticationForm +from .models import UserExtension + +# Form 1: Secure Registration +# Combines standard Django User creation with sketch fields +class UnifiedRegisterForm(UserCreationForm): + # Required standard User fields as per sketch + email = forms.EmailField(required=True) + first_name = forms.CharField(required=True, label="* Name") + + # Required UserExtension fields from sketch + user_type = forms.ChoiceField(choices=UserExtension.USER_TYPE_CHOICES, label="* TIPO") + gender = forms.CharField(required=True, label="* SEXO") + # DateField with HTML5 widget for better UI/Security + date_of_birth = forms.DateField(required=True, label="* DATA DE NASC.", widget=forms.DateInput(attrs={'type': 'date'})) + + class Meta(UserCreationForm.Meta): + # We must manually list the fields we want to extend beyond standard UserCreationForm + fields = UserCreationForm.Meta.fields + ('email', 'first_name', 'user_type', 'gender', 'date_of_birth',) + + def save(self, commit=True): + # 1. Save standard User object + user = super().save(commit=False) + user.email = self.cleaned_data['email'] + user.first_name = self.cleaned_data['first_name'] + if commit: + user.save() + # 2. Create and Save linked UserExtension with sketch data + UserExtension.objects.create( + user=user, + user_type=self.cleaned_data['user_type'], + gender=self.cleaned_data['gender'], + date_of_birth=self.cleaned_data['date_of_birth'] + ) + return user + +# Form 2: Simple Login Form (Standard Django) +class SketchLoginForm(AuthenticationForm): + username = forms.EmailField(label="Email") # Overriding label for sketch consistency \ No newline at end of file diff --git a/core/migrations/.DS_Store b/core/migrations/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/core/migrations/.DS_Store differ diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..7d8f7be --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,69 @@ +# Generated by Django 6.0.4 on 2026-05-02 23:30 + +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='Profile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('description', models.TextField()), + ], + ), + migrations.CreateModel( + name='Donkey', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('race', models.CharField(max_length=100)), + ('location', models.CharField(max_length=255)), + ('date_of_birth', models.DateField(blank=True, null=True)), + ('cover_image', models.ImageField(upload_to='donkey_covers/')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='Post', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField(auto_now_add=True)), + ('description', models.TextField()), + ('image_url', models.ImageField(upload_to='posts/')), + ('donkey', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.donkey')), + ], + ), + migrations.CreateModel( + name='Sponsorship', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('type', models.CharField(max_length=50)), + ('start_date', models.DateField()), + ('amount', models.DecimalField(decimal_places=2, max_digits=10)), + ('donkey', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.donkey')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.CreateModel( + name='UserExtension', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('gender', models.CharField(blank=True, help_text='*SEXO', max_length=10)), + ('date_of_birth', models.DateField(help_text='*DATA DE NASC.')), + ('user_type', models.CharField(choices=[('PRODUCER', 'Produtor'), ('NORMAL', 'Normal (Sponsor)')], default='NORMAL', max_length=20)), + ('profile', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='core.profile')), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/core/migrations/__init__.py b/core/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/migrations/__pycache__/0001_initial.cpython-314.pyc b/core/migrations/__pycache__/0001_initial.cpython-314.pyc new file mode 100644 index 0000000..a0b0434 Binary files /dev/null and b/core/migrations/__pycache__/0001_initial.cpython-314.pyc differ diff --git a/core/migrations/__pycache__/0002_remove_post_image_remove_profile_role_and_more.cpython-314.pyc b/core/migrations/__pycache__/0002_remove_post_image_remove_profile_role_and_more.cpython-314.pyc new file mode 100644 index 0000000..349aebd Binary files /dev/null and b/core/migrations/__pycache__/0002_remove_post_image_remove_profile_role_and_more.cpython-314.pyc differ diff --git a/core/migrations/__pycache__/0003_donkey_date_of_birth.cpython-314.pyc b/core/migrations/__pycache__/0003_donkey_date_of_birth.cpython-314.pyc new file mode 100644 index 0000000..8940de8 Binary files /dev/null and b/core/migrations/__pycache__/0003_donkey_date_of_birth.cpython-314.pyc differ diff --git a/core/migrations/__pycache__/0004_remove_donkey_date_of_birth.cpython-314.pyc b/core/migrations/__pycache__/0004_remove_donkey_date_of_birth.cpython-314.pyc new file mode 100644 index 0000000..47f8d69 Binary files /dev/null and b/core/migrations/__pycache__/0004_remove_donkey_date_of_birth.cpython-314.pyc differ diff --git a/core/migrations/__pycache__/0005_donkey_date_of_birth.cpython-314.pyc b/core/migrations/__pycache__/0005_donkey_date_of_birth.cpython-314.pyc new file mode 100644 index 0000000..9623489 Binary files /dev/null and b/core/migrations/__pycache__/0005_donkey_date_of_birth.cpython-314.pyc differ diff --git a/core/migrations/__pycache__/__init__.cpython-314.pyc b/core/migrations/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000..e4b7c41 Binary files /dev/null and b/core/migrations/__pycache__/__init__.cpython-314.pyc differ diff --git a/core/models.py b/core/models.py new file mode 100644 index 0000000..2912305 --- /dev/null +++ b/core/models.py @@ -0,0 +1,48 @@ +from django.db import models +from django.contrib.auth.models import User + + +class Profile(models.Model): + name = models.CharField(max_length=100) + description = models.TextField() + + def __str__(self): + return self.name + +class UserExtension(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE) + gender = models.CharField(max_length=10, blank=True, help_text="*SEXO") + date_of_birth = models.DateField(help_text="*DATA DE NASC.") + USER_TYPE_CHOICES = [ + ('PRODUCER', 'Produtor'), + ('NORMAL', 'Normal (Sponsor)'), + ] + user_type = models.CharField(max_length=20, choices=USER_TYPE_CHOICES, default='NORMAL') + profile = models.ForeignKey(Profile, on_delete=models.CASCADE, null=True, blank=True) + + def __str__(self): + return f"{self.user.username} ({self.user_type})" + +class Donkey(models.Model): + name = models.CharField(max_length=100) + race = models.CharField(max_length=100) + location = models.CharField(max_length=255) + date_of_birth = models.DateField(null=True, blank=True) + owner = models.ForeignKey(User, on_delete=models.CASCADE) + cover_image = models.ImageField(upload_to='donkey_covers/') + + def __str__(self): + return self.name + +class Sponsorship(models.Model): + type = models.CharField(max_length=50) + start_date = models.DateField() + amount = models.DecimalField(max_digits=10, decimal_places=2) + user = models.ForeignKey(User, on_delete=models.CASCADE) + donkey = models.ForeignKey(Donkey, on_delete=models.CASCADE) + +class Post(models.Model): + donkey = models.ForeignKey(Donkey, on_delete=models.CASCADE) + date = models.DateTimeField(auto_now_add=True) + description = models.TextField() + image_url = models.ImageField(upload_to='posts/') \ No newline at end of file diff --git a/core/templates/.DS_Store b/core/templates/.DS_Store new file mode 100644 index 0000000..20e40c2 Binary files /dev/null and b/core/templates/.DS_Store differ diff --git a/core/templates/core/home.html b/core/templates/core/home.html new file mode 100644 index 0000000..da46389 --- /dev/null +++ b/core/templates/core/home.html @@ -0,0 +1,23 @@ + + + + + Burros de Miranda - Home + + + + +
+

Bem-vindo à plataforma Burros de Miranda!

+

You have successfully reached the homepage.

+ + +

+ Sair / Voltar ao Login +
+ + + \ No newline at end of file diff --git a/core/templates/core/index.html b/core/templates/core/index.html new file mode 100644 index 0000000..9564e9d --- /dev/null +++ b/core/templates/core/index.html @@ -0,0 +1,111 @@ + + + + + Burros de Miranda - Acesso + + + + +
+

Burros de Miranda

+ + +
+ + + + Entrar como Anónimo +
+ + +
+ + +
+ + {% csrf_token %} + + + {% if login_form.non_field_errors %}
{{ login_form.non_field_errors }}
{% endif %} + +
{{ login_form.username }}
+
{{ login_form.password }}
+ +
+ +
+
+ + +
+ {% csrf_token %} + + {% if register_form.non_field_errors %}
{{ register_form.non_field_errors }}
{% endif %} + + +
+
{{ register_form.email }}
+
{{ register_form.password }}
+
{{ register_form.gender }}
+
+ + +
+
{{ register_form.first_name }}
+
{{ register_form.user_type }}
+
{{ register_form.date_of_birth }}
+
+ +
+ +
+
+ +
+
+ + + + + + \ No newline at end of file diff --git a/core/tests.py b/core/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/core/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/core/urls.py b/core/urls.py new file mode 100644 index 0000000..7e0db12 --- /dev/null +++ b/core/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from .views import login_register_view, homepage_view + +urlpatterns = [ + path('', login_register_view, name='login_register'), # Root URL serves login/register + path('home/', homepage_view, name='homepage'), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py new file mode 100644 index 0000000..b59fafc --- /dev/null +++ b/core/views.py @@ -0,0 +1,40 @@ +from django.shortcuts import render, redirect +from django.contrib.auth import login, authenticate +from .forms import UnifiedRegisterForm, SketchLoginForm + +def login_register_view(request): + # Prepare standard empty forms for GET requests + login_form = SketchLoginForm() + register_form = UnifiedRegisterForm() + + # Track which section should be visible on reload/error + active_form = 'login' + + if request.method == 'POST': + # Scenario 1: User submitted the REGISTER form + if 'register_submit' in request.POST: + active_form = 'register' + register_form = UnifiedRegisterForm(request.POST) + if register_form.is_valid(): + user = register_form.save() + login(request, user) # Auto-login after registration + return redirect('homepage') # Define this later + # else will reload page with errors displayed + + # Scenario 2: User submitted the LOGIN form + elif 'login_submit' in request.POST: + active_form = 'login' + login_form = SketchLoginForm(request, data=request.POST) + if login_form.is_valid(): + login(request, login_form.get_user()) + return redirect('homepage') # Define this later + # else reloads page displaying authentication errors + + return render(request, 'core/index.html', { + 'login_form': login_form, + 'register_form': register_form, + 'active_form': active_form, # Tells template which tab is active + }) + +def homepage_view(request): + return render(request, 'core/home.html', {}) \ No newline at end of file diff --git a/donkeys/__init__.py b/donkeys/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/donkeys/__pycache__/__init__.cpython-314.pyc b/donkeys/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000..8b95bc5 Binary files /dev/null and b/donkeys/__pycache__/__init__.cpython-314.pyc differ diff --git a/donkeys/__pycache__/settings.cpython-314.pyc b/donkeys/__pycache__/settings.cpython-314.pyc new file mode 100644 index 0000000..a00a360 Binary files /dev/null and b/donkeys/__pycache__/settings.cpython-314.pyc differ diff --git a/donkeys/__pycache__/urls.cpython-314.pyc b/donkeys/__pycache__/urls.cpython-314.pyc new file mode 100644 index 0000000..96b1e70 Binary files /dev/null and b/donkeys/__pycache__/urls.cpython-314.pyc differ diff --git a/donkeys/__pycache__/wsgi.cpython-314.pyc b/donkeys/__pycache__/wsgi.cpython-314.pyc new file mode 100644 index 0000000..4ccba30 Binary files /dev/null and b/donkeys/__pycache__/wsgi.cpython-314.pyc differ diff --git a/donkeys/asgi.py b/donkeys/asgi.py new file mode 100644 index 0000000..fb8b643 --- /dev/null +++ b/donkeys/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for donkeys 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/4.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'donkeys.settings') + +application = get_asgi_application() diff --git a/donkeys/settings.py b/donkeys/settings.py new file mode 100644 index 0000000..c2edb70 --- /dev/null +++ b/donkeys/settings.py @@ -0,0 +1,124 @@ +""" +Django settings for donkeys project. + +Generated by 'django-admin startproject' using Django 4.2.5. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.2/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/4.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-_k)esovjb!+f4d4x5nekf#i71n57i5%&u1g_3@lm4--mh(poc2' + +# 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', + 'core', +] + +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 = 'donkeys.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 = 'donkeys.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/4.2/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.2/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/4.2/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.2/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' diff --git a/donkeys/urls.py b/donkeys/urls.py new file mode 100644 index 0000000..93237b4 --- /dev/null +++ b/donkeys/urls.py @@ -0,0 +1,24 @@ +""" +URL configuration for donkeys project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/4.2/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), + # This tells Django: "Forward any web traffic to the 'core' app's routing system" + path('', include('core.urls')), +] \ No newline at end of file diff --git a/donkeys/wsgi.py b/donkeys/wsgi.py new file mode 100644 index 0000000..b1d75f0 --- /dev/null +++ b/donkeys/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for donkeys 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/4.2/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'donkeys.settings') + +application = get_wsgi_application() diff --git a/posts/daciapost.jpg b/posts/daciapost.jpg new file mode 100644 index 0000000..18a158f Binary files /dev/null and b/posts/daciapost.jpg differ