Compare commits

..

No commits in common. "ai-dev" and "master" have entirely different histories.

22 changed files with 196 additions and 381 deletions

View File

@ -149,12 +149,11 @@ STATIC_URL = 'static/'
# Collect static into a separate folder; avoid overlapping with STATICFILES_DIRS. # Collect static into a separate folder; avoid overlapping with STATICFILES_DIRS.
STATIC_ROOT = BASE_DIR / 'staticfiles' STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [BASE_DIR / "static"] STATICFILES_DIRS = [
BASE_DIR / 'static',
MEDIA_URL = '/media/' BASE_DIR / 'assets',
MEDIA_ROOT = BASE_DIR / 'media' BASE_DIR / 'node_modules',
]
# Email # Email
EMAIL_BACKEND = os.getenv( EMAIL_BACKEND = os.getenv(

View File

@ -15,14 +15,14 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import path, include from django.urls import include, path
from django.conf.urls.static import static
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [ urlpatterns = [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("", include("core.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: if settings.DEBUG:
urlpatterns += static("/assets/", document_root=settings.BASE_DIR / "assets") urlpatterns += static("/assets/", document_root=settings.BASE_DIR / "assets")

View File

@ -1,7 +0,0 @@
from django import forms
from .models import MediaUpload
class MediaUploadForm(forms.ModelForm):
class Meta:
model = MediaUpload
fields = ['title', 'image', 'measurements']

View File

@ -1,24 +0,0 @@
# 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)),
],
),
]

View File

@ -1,27 +0,0 @@
# 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)),
],
),
]

View File

@ -1,21 +1,3 @@
from django.db import models from django.db import models
from django.contrib.auth.models import User
class MediaUpload(models.Model): # Create your models here.
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}'

View File

@ -3,10 +3,7 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{% block title %}Savage Ratings{% endblock %}</title> <title>{% block title %}Knowledge Base{% endblock %}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lato:wght@400;700&family=Roboto:wght@400;700&display=swap" rel="stylesheet">
{% if project_description %} {% if project_description %}
<meta name="description" content="{{ project_description }}"> <meta name="description" content="{{ project_description }}">
<meta property="og:description" content="{{ project_description }}"> <meta property="og:description" content="{{ project_description }}">

View File

@ -1,18 +1,145 @@
{% extends 'base.html' %} {% extends "base.html" %}
{% load static %}
{% block title %}Savage Ratings{% endblock %} {% block title %}{{ project_name }}{% endblock %}
{% block head %}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
<style>
: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);
}
* {
box-sizing: border-box;
}
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;
}
body::before {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'><path d='M-10 10L110 10M10 -10L10 110' stroke-width='1' stroke='rgba(255,255,255,0.05)'/></svg>");
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% {
background-position: 0% 0%;
}
100% {
background-position: 100% 100%;
}
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2.5rem 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 12px 36px rgba(0, 0, 0, 0.25);
}
h1 {
font-size: clamp(2.2rem, 3vw + 1.2rem, 3.2rem);
font-weight: 700;
margin: 0 0 1.2rem;
letter-spacing: -0.02em;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
opacity: 0.92;
}
.loader {
margin: 1.5rem auto;
width: 56px;
height: 56px;
border: 4px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.runtime code {
background: rgba(0, 0, 0, 0.25);
padding: 0.15rem 0.45rem;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
footer {
position: absolute;
bottom: 1rem;
width: 100%;
text-align: center;
font-size: 0.85rem;
opacity: 0.75;
}
</style>
{% endblock %}
{% block content %} {% block content %}
<div class="hero"> <main>
<div class="container"> <div class="card">
<h1>Get Brutally Honest Ratings</h1> <h1>Analyzing your requirements and generating your app…</h1>
<p>Upload your images and videos to get savage ratings from our panel.</p> <div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<form method="post" enctype="multipart/form-data"> <span class="sr-only">Loading…</span>
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Upload</button>
</form>
</div> </div>
</div> <p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
{% endblock %} <p class="hint">This page will refresh automatically as the plan is implemented.</p>
<p class="runtime">
Runtime: Django <code>{{ django_version }}</code> · Python <code>{{ python_version }}</code>
— UTC <code>{{ current_time|date:"Y-m-d H:i:s" }}</code>
</p>
</div>
</main>
<footer>
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
</footer>
{% endblock %}

View File

@ -1,58 +0,0 @@
{% extends 'base.html' %}
{% load static %}
{% block title %}{{ media.title|default:"Media Detail" }}{% endblock %}
{% block content %}
<div class="container">
<h1>{{ media.title }}</h1>
<img src="{{ media.image.url }}" alt="{{ media.title }}" class="img-fluid">
{% if media.measurements %}
<p>Measurements: {{ media.measurements }}</p>
{% endif %}
<a href="{% url 'index' %}">Back to home</a>
<div class="rating-section">
<h2>Rate this media</h2>
<form method="post" action="{% url 'rate_media' media.id %}">
{% csrf_token %}
<div class="mb-3">
<label for="score" class="form-label">Score (1-5):</label>
<select name="score" id="score" class="form-control">
{% for i in "12345" %}
<option value="{{ i }}">{{ i }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label for="comment" class="form-label">Comment:</label>
<textarea name="comment" id="comment" class="form-control" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-primary">Submit Rating</button>
</form>
{% if average_rating is not None %}
<h3>Average Rating: {{ average_rating|floatformat:1 }} / 5</h3>
{% else %}
<h3>No ratings yet.</h3>
{% endif %}
{% if ratings %}
<div class="comments-section">
<h3>Comments:</h3>
{% for rating in ratings %}
<div class="card mb-2">
<div class="card-body">
<h5 class="card-title">Score: {{ rating.score }}</h5>
{% if rating.comment %}
<p class="card-text">{{ rating.comment }}</p>
{% endif %}
<p class="card-text"><small class="text-muted">By {% if rating.rater %}{{ rating.rater.username }}{% else %}Anonymous{% endif %} on {{ rating.created_at|date:"M d, Y H:i" }}</small></p>
</div>
</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -1,8 +1,7 @@
from django.urls import path from django.urls import path
from .views import index, media_detail, rate_media
from .views import home
urlpatterns = [ urlpatterns = [
path('', index, name='index'), path("", home, name="home"),
path('media/<int:pk>/', media_detail, name='media_detail'), ]
path('media/<int:pk>/rate/', rate_media, name='rate_media'),
]

View File

@ -1,34 +1,25 @@
from django.shortcuts import render, redirect import os
from django.db.models import Avg import platform
from .forms import MediaUploadForm
from .models import MediaUpload, Rating
def index(request): from django import get_version as django_version
if request.method == 'POST': from django.shortcuts import render
form = MediaUploadForm(request.POST, request.FILES) from django.utils import timezone
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 rate_media(request, pk): def home(request):
if request.method == 'POST': """Render the landing screen with loader and environment details."""
media_upload = MediaUpload.objects.get(pk=pk) host_name = request.get_host().lower()
score = request.POST.get('score') agent_brand = "AppWizzy" if host_name == "appwizzy.com" else "Flatlogic"
comment = request.POST.get('comment', '') now = timezone.now()
if request.user.is_authenticated:
rater = request.user
else:
rater = None
if score: context = {
Rating.objects.create(media_upload=media_upload, score=score, comment=comment, rater=rater) "project_name": "New Style",
return redirect('media_detail', pk=pk) "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)

View File

@ -1,4 +1,3 @@
Django==5.2.7 Django==5.2.7
Pillow==12.0.0
mysqlclient==2.2.7 mysqlclient==2.2.7
python-dotenv==1.1.1 python-dotenv==1.1.1

View File

@ -1,94 +1,4 @@
/* Custom styles for the application */
body { body {
background-color: #1A1A1A; font-family: system-ui, -apple-system, sans-serif;
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;
}

View File

@ -1,94 +1,21 @@
: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 { body {
background-color: #1A1A1A; margin: 0;
color: #F5F5F5; font-family: 'Inter', sans-serif;
font-family: 'Lato', sans-serif; background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
} color: var(--text-color);
display: flex;
h1, h2, h3, h4, h5, h6 { justify-content: center;
font-family: 'Roboto', sans-serif; align-items: center;
} min-height: 100vh;
.hero {
background: linear-gradient(rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7));
padding: 100px 0;
text-align: center; text-align: center;
overflow: hidden;
position: relative;
} }
.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;
}