Compare commits

...

1 Commits

Author SHA1 Message Date
Flatlogic Bot
565f6b39a8 h 2025-12-16 01:57:12 +00:00
22 changed files with 382 additions and 197 deletions

View File

@ -149,11 +149,12 @@ 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 = [ STATICFILES_DIRS = [BASE_DIR / "static"]
BASE_DIR / 'static',
BASE_DIR / 'assets', MEDIA_URL = '/media/'
BASE_DIR / 'node_modules', MEDIA_ROOT = BASE_DIR / 'media'
]
# 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 include, path from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from django.conf import settings
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")

Binary file not shown.

7
core/forms.py Normal file
View File

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

View File

@ -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)),
],
),
]

View File

@ -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)),
],
),
]

View File

@ -1,3 +1,21 @@
from django.db import models 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}'

View File

@ -3,7 +3,10 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>{% block title %}Knowledge Base{% endblock %}</title> <title>{% block title %}Savage Ratings{% 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,145 +1,18 @@
{% extends "base.html" %} {% extends 'base.html' %}
{% load static %}
{% block title %}{{ project_name }}{% endblock %} {% block title %}Savage Ratings{% 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 %}
<main> <div class="hero">
<div class="card"> <div class="container">
<h1>Analyzing your requirements and generating your app…</h1> <h1>Get Brutally Honest Ratings</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes"> <p>Upload your images and videos to get savage ratings from our panel.</p>
<span class="sr-only">Loading…</span> <form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Upload</button>
</form>
</div> </div>
<p class="hint">AppWizzy AI is collecting your requirements and applying the first changes.</p>
<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> </div>
</main>
<footer>
Page updated: {{ current_time|date:"Y-m-d H:i:s" }} (UTC)
</footer>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,58 @@
{% 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,7 +1,8 @@
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("", home, name="home"), path('', index, name='index'),
path('media/<int:pk>/', media_detail, name='media_detail'),
path('media/<int:pk>/rate/', rate_media, name='rate_media'),
] ]

View File

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

View File

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