diff --git a/config/__pycache__/settings.cpython-311.pyc b/config/__pycache__/settings.cpython-311.pyc index 5be02db..ad5c32a 100644 Binary files a/config/__pycache__/settings.cpython-311.pyc and b/config/__pycache__/settings.cpython-311.pyc differ diff --git a/config/__pycache__/urls.cpython-311.pyc b/config/__pycache__/urls.cpython-311.pyc index 28817aa..38479ac 100644 Binary files a/config/__pycache__/urls.cpython-311.pyc and b/config/__pycache__/urls.cpython-311.pyc differ diff --git a/config/settings.py b/config/settings.py index 291d043..0d1c507 100644 --- a/config/settings.py +++ b/config/settings.py @@ -149,11 +149,12 @@ STATIC_URL = 'static/' # Collect static into a separate folder; avoid overlapping with STATICFILES_DIRS. STATIC_ROOT = BASE_DIR / 'staticfiles' -STATICFILES_DIRS = [ - BASE_DIR / 'static', - BASE_DIR / 'assets', - BASE_DIR / 'node_modules', -] +STATICFILES_DIRS = [BASE_DIR / "static"] + +MEDIA_URL = '/media/' +MEDIA_ROOT = BASE_DIR / 'media' + + # Email EMAIL_BACKEND = os.getenv( diff --git a/config/urls.py b/config/urls.py index bcfc074..cb9e1cf 100644 --- a/config/urls.py +++ b/config/urls.py @@ -15,14 +15,14 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import include, path -from django.conf import settings +from django.urls import path, include from django.conf.urls.static import static +from django.conf import settings urlpatterns = [ path("admin/", admin.site.urls), path("", include("core.urls")), -] +] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) if settings.DEBUG: urlpatterns += static("/assets/", document_root=settings.BASE_DIR / "assets") diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000..d64a49e Binary files /dev/null and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 9aa598b..07bf217 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 1f807fa..586bc21 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 6867ddf..1a7d9bc 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..686e88b --- /dev/null +++ b/core/forms.py @@ -0,0 +1,7 @@ +from django import forms +from .models import MediaUpload + +class MediaUploadForm(forms.ModelForm): + class Meta: + model = MediaUpload + fields = ['title', 'image', 'measurements'] diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py new file mode 100644 index 0000000..46aef45 --- /dev/null +++ b/core/migrations/0001_initial.py @@ -0,0 +1,24 @@ +# Generated by Django 5.2.7 on 2025-12-16 01:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='MediaUpload', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(blank=True, max_length=255)), + ('image', models.ImageField(upload_to='uploads/')), + ('measurements', models.CharField(blank=True, max_length=255)), + ('uploaded_at', models.DateTimeField(auto_now_add=True)), + ], + ), + ] diff --git a/core/migrations/0002_rating.py b/core/migrations/0002_rating.py new file mode 100644 index 0000000..6b1ea0c --- /dev/null +++ b/core/migrations/0002_rating.py @@ -0,0 +1,27 @@ +# Generated by Django 5.2.7 on 2025-12-16 01:54 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Rating', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('score', models.IntegerField(choices=[(1, '1'), (2, '2'), (3, '3'), (4, '4'), (5, '5')])), + ('comment', models.TextField(blank=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('media_upload', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ratings', to='core.mediaupload')), + ('rater', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/core/migrations/__pycache__/0001_initial.cpython-311.pyc b/core/migrations/__pycache__/0001_initial.cpython-311.pyc new file mode 100644 index 0000000..49e8098 Binary files /dev/null and b/core/migrations/__pycache__/0001_initial.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0002_rating.cpython-311.pyc b/core/migrations/__pycache__/0002_rating.cpython-311.pyc new file mode 100644 index 0000000..1d16b68 Binary files /dev/null and b/core/migrations/__pycache__/0002_rating.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 71a8362..93de57f 100644 --- a/core/models.py +++ b/core/models.py @@ -1,3 +1,21 @@ from django.db import models +from django.contrib.auth.models import User -# Create your models here. +class MediaUpload(models.Model): + title = models.CharField(max_length=255, blank=True) + image = models.ImageField(upload_to='uploads/') + measurements = models.CharField(max_length=255, blank=True) + uploaded_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return self.title or str(self.uploaded_at) + +class Rating(models.Model): + media_upload = models.ForeignKey(MediaUpload, on_delete=models.CASCADE, related_name='ratings') + score = models.IntegerField(choices=[(i, str(i)) for i in range(1, 6)]) + comment = models.TextField(blank=True) + rater = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f'{self.media_upload.title or self.media_upload.id} - {self.score}' \ No newline at end of file diff --git a/core/templates/base.html b/core/templates/base.html index 1e7e5fb..aaf6432 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -3,7 +3,10 @@ - {% block title %}Knowledge Base{% endblock %} + {% block title %}Savage Ratings{% endblock %} + + + {% if project_description %} diff --git a/core/templates/core/index.html b/core/templates/core/index.html index faec813..9bd28db 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -1,145 +1,18 @@ -{% extends "base.html" %} +{% extends 'base.html' %} +{% load static %} -{% block title %}{{ project_name }}{% endblock %} - -{% block head %} - - - - -{% endblock %} +{% block title %}Savage Ratings{% endblock %} {% block content %} -
-
-

Analyzing your requirements and generating your app…

-
- Loading… +
+
+

Get Brutally Honest Ratings

+

Upload your images and videos to get savage ratings from our panel.

+
+ {% csrf_token %} + {{ form.as_p }} + +
-

AppWizzy AI is collecting your requirements and applying the first changes.

-

This page will refresh automatically as the plan is implemented.

-

- Runtime: Django {{ django_version }} · Python {{ python_version }} - — UTC {{ current_time|date:"Y-m-d H:i:s" }} -

-
-
- -{% endblock %} \ No newline at end of file + +{% endblock %} diff --git a/core/templates/core/media_detail.html b/core/templates/core/media_detail.html new file mode 100644 index 0000000..d40ea1a --- /dev/null +++ b/core/templates/core/media_detail.html @@ -0,0 +1,58 @@ +{% extends 'base.html' %} +{% load static %} + +{% block title %}{{ media.title|default:"Media Detail" }}{% endblock %} + +{% block content %} +
+

{{ media.title }}

+ {{ media.title }} + {% if media.measurements %} +

Measurements: {{ media.measurements }}

+ {% endif %} + Back to home + +
+

Rate this media

+
+ {% csrf_token %} +
+ + +
+
+ + +
+ +
+ + {% if average_rating is not None %} +

Average Rating: {{ average_rating|floatformat:1 }} / 5

+ {% else %} +

No ratings yet.

+ {% endif %} + + {% if ratings %} +
+

Comments:

+ {% for rating in ratings %} +
+
+
Score: {{ rating.score }}
+ {% if rating.comment %} +

{{ rating.comment }}

+ {% endif %} +

By {% if rating.rater %}{{ rating.rater.username }}{% else %}Anonymous{% endif %} on {{ rating.created_at|date:"M d, Y H:i" }}

+
+
+ {% endfor %} +
+ {% endif %} +
+
+{% endblock %} diff --git a/core/urls.py b/core/urls.py index 6299e3d..95d94d5 100644 --- a/core/urls.py +++ b/core/urls.py @@ -1,7 +1,8 @@ from django.urls import path - -from .views import home +from .views import index, media_detail, rate_media urlpatterns = [ - path("", home, name="home"), -] + path('', index, name='index'), + path('media//', media_detail, name='media_detail'), + path('media//rate/', rate_media, name='rate_media'), +] \ No newline at end of file diff --git a/core/views.py b/core/views.py index c9aed12..b49e5db 100644 --- a/core/views.py +++ b/core/views.py @@ -1,25 +1,34 @@ -import os -import platform +from django.shortcuts import render, redirect +from django.db.models import Avg +from .forms import MediaUploadForm +from .models import MediaUpload, Rating -from django import get_version as django_version -from django.shortcuts import render -from django.utils import timezone +def index(request): + if request.method == 'POST': + form = MediaUploadForm(request.POST, request.FILES) + if form.is_valid(): + media_upload = form.save() + return redirect('media_detail', pk=media_upload.pk) + else: + form = MediaUploadForm() + return render(request, 'core/index.html', {'form': form}) +def media_detail(request, pk): + media = MediaUpload.objects.get(pk=pk) + ratings = media.ratings.all() + average_rating = ratings.aggregate(Avg('score'))['score__avg'] + return render(request, 'core/media_detail.html', {'media': media, 'ratings': ratings, 'average_rating': average_rating}) -def home(request): - """Render the landing screen with loader and environment details.""" - host_name = request.get_host().lower() - agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic" - now = timezone.now() +def rate_media(request, pk): + if request.method == 'POST': + media_upload = MediaUpload.objects.get(pk=pk) + score = request.POST.get('score') + comment = request.POST.get('comment', '') + if request.user.is_authenticated: + rater = request.user + else: + rater = None - context = { - "project_name": "New Style", - "agent_brand": agent_brand, - "django_version": django_version(), - "python_version": platform.python_version(), - "current_time": now, - "host_name": host_name, - "project_description": os.getenv("PROJECT_DESCRIPTION", ""), - "project_image_url": os.getenv("PROJECT_IMAGE_URL", ""), - } - return render(request, "core/index.html", context) + if score: + Rating.objects.create(media_upload=media_upload, score=score, comment=comment, rater=rater) + return redirect('media_detail', pk=pk) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e22994c..e51ac05 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ Django==5.2.7 +Pillow==12.0.0 mysqlclient==2.2.7 python-dotenv==1.1.1 diff --git a/static/css/custom.css b/static/css/custom.css index 925f6ed..d531ff1 100644 --- a/static/css/custom.css +++ b/static/css/custom.css @@ -1,4 +1,94 @@ -/* Custom styles for the application */ body { - font-family: system-ui, -apple-system, sans-serif; + background-color: #1A1A1A; + color: #F5F5F5; + font-family: 'Lato', sans-serif; } + +h1, h2, h3, h4, h5, h6 { + font-family: 'Roboto', sans-serif; +} + +.hero { + background: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)); + padding: 100px 0; + text-align: center; +} + +.hero h1 { + font-size: 4rem; + font-weight: 700; +} + +.btn-primary { + background-color: #FF3B30; + border-color: #FF3B30; +} + +.btn-primary:hover { + background-color: #E6352A; + border-color: #E6352A; +} + +.container { + max-width: 800px; + margin: 0 auto; + padding: 20px; +} + +form p { + margin-bottom: 15px; +} + +form input[type='text'], form input[type='file'] { + width: 100%; + padding: 10px; + border-radius: 5px; + border: 1px solid #ccc; +} + +.rating-section { + margin-top: 40px; + padding: 20px; + background-color: #2a2a2a; + border-radius: 8px; +} + +.rating-section h2, .rating-section h3 { + color: #F5F5F5; + margin-bottom: 20px; +} + +.form-label { + color: #F5F5F5; +} + +.form-control { + background-color: #3a3a3a; + color: #F5F5F5; + border: 1px solid #555; +} + +.form-control:focus { + background-color: #4a4a4a; + color: #F5F5F5; + border-color: #FF3B30; + box-shadow: 0 0 0 0.25rem rgba(255, 59, 48, 0.25); +} + +.comments-section { + margin-top: 30px; +} + +.comments-section .card { + background-color: #333; + border: 1px solid #444; + color: #F5F5F5; +} + +.comments-section .card-title { + color: #FF3B30; +} + +.comments-section .text-muted { + color: #bbb !important; +} \ No newline at end of file diff --git a/staticfiles/css/custom.css b/staticfiles/css/custom.css index 108056f..d531ff1 100644 --- a/staticfiles/css/custom.css +++ b/staticfiles/css/custom.css @@ -1,21 +1,94 @@ - -:root { - --bg-color-start: #6a11cb; - --bg-color-end: #2575fc; - --text-color: #ffffff; - --card-bg-color: rgba(255, 255, 255, 0.01); - --card-border-color: rgba(255, 255, 255, 0.1); -} body { - margin: 0; - font-family: 'Inter', sans-serif; - background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end)); - color: var(--text-color); - display: flex; - justify-content: center; - align-items: center; - min-height: 100vh; - text-align: center; - overflow: hidden; - position: relative; + background-color: #1A1A1A; + color: #F5F5F5; + font-family: 'Lato', sans-serif; } + +h1, h2, h3, h4, h5, h6 { + font-family: 'Roboto', sans-serif; +} + +.hero { + background: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7)); + padding: 100px 0; + text-align: center; +} + +.hero h1 { + font-size: 4rem; + font-weight: 700; +} + +.btn-primary { + background-color: #FF3B30; + border-color: #FF3B30; +} + +.btn-primary:hover { + background-color: #E6352A; + border-color: #E6352A; +} + +.container { + max-width: 800px; + margin: 0 auto; + padding: 20px; +} + +form p { + margin-bottom: 15px; +} + +form input[type='text'], form input[type='file'] { + width: 100%; + padding: 10px; + border-radius: 5px; + border: 1px solid #ccc; +} + +.rating-section { + margin-top: 40px; + padding: 20px; + background-color: #2a2a2a; + border-radius: 8px; +} + +.rating-section h2, .rating-section h3 { + color: #F5F5F5; + margin-bottom: 20px; +} + +.form-label { + color: #F5F5F5; +} + +.form-control { + background-color: #3a3a3a; + color: #F5F5F5; + border: 1px solid #555; +} + +.form-control:focus { + background-color: #4a4a4a; + color: #F5F5F5; + border-color: #FF3B30; + box-shadow: 0 0 0 0.25rem rgba(255, 59, 48, 0.25); +} + +.comments-section { + margin-top: 30px; +} + +.comments-section .card { + background-color: #333; + border: 1px solid #444; + color: #F5F5F5; +} + +.comments-section .card-title { + color: #FF3B30; +} + +.comments-section .text-muted { + color: #bbb !important; +} \ No newline at end of file