Initial commit
This commit is contained in:
0
idescriptor/__init__.py
Normal file
0
idescriptor/__init__.py
Normal file
5
idescriptor/admin.py
Normal file
5
idescriptor/admin.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from idescriptor.models import Image
|
||||
|
||||
admin.site.register(Image)
|
6
idescriptor/apps.py
Normal file
6
idescriptor/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class IdescriptorConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'idescriptor'
|
23
idescriptor/forms.py
Normal file
23
idescriptor/forms.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Submit, Hidden, HTML
|
||||
from django import forms
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from idescriptor.models import Image
|
||||
|
||||
|
||||
class ImageForm(forms.ModelForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_id = 'form-describe-image'
|
||||
self.helper.form_class = ''
|
||||
self.helper.form_method = 'post'
|
||||
self.helper.add_input(Hidden("action", "add_image_details"))
|
||||
self.helper.add_input(Submit('submit', _('Submit')))
|
||||
|
||||
class Meta:
|
||||
model = Image
|
||||
fields = ('title', 'alt_text')
|
28
idescriptor/migrations/0001_initial.py
Normal file
28
idescriptor/migrations/0001_initial.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-21 11:41
|
||||
|
||||
import uuid
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Image',
|
||||
fields=[
|
||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(blank=True, help_text='Image title that will be posted', max_length=200, null=True)),
|
||||
('alt_text', models.CharField(blank=True, help_text='Describe the image', max_length=2000, null=True)),
|
||||
('file_path', models.CharField(max_length=1500)),
|
||||
('image', models.ImageField(upload_to='')),
|
||||
('file_hash', models.CharField(blank=True, max_length=64, null=True, unique=True, verbose_name='File hash')),
|
||||
('last_posted', models.DateTimeField(blank=True, null=True)),
|
||||
('number_times_posted', models.IntegerField(default=0)),
|
||||
],
|
||||
),
|
||||
]
|
18
idescriptor/migrations/0002_alter_image_alt_text.py
Normal file
18
idescriptor/migrations/0002_alter_image_alt_text.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-21 18:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('idescriptor', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='image',
|
||||
name='alt_text',
|
||||
field=models.TextField(blank=True, help_text='Describe the image', null=True),
|
||||
),
|
||||
]
|
0
idescriptor/migrations/__init__.py
Normal file
0
idescriptor/migrations/__init__.py
Normal file
63
idescriptor/models.py
Normal file
63
idescriptor/models.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import hashlib
|
||||
import os
|
||||
import uuid
|
||||
from os.path import isfile, join
|
||||
|
||||
from django.db import models
|
||||
from django.urls import reverse
|
||||
from django.core.files import File
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from datetime import datetime
|
||||
|
||||
from imagebot import settings
|
||||
|
||||
|
||||
def get_hash(filepath):
|
||||
with open(filepath, 'rb') as file:
|
||||
# read contents of the file
|
||||
data = file.read()
|
||||
sha256 = hashlib.sha256(data).hexdigest()
|
||||
return sha256
|
||||
|
||||
|
||||
class Image(models.Model):
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, verbose_name=_('ID'))
|
||||
title = models.CharField(max_length=200,
|
||||
help_text=_("Image title that will be posted"),
|
||||
blank=True, null=True)
|
||||
alt_text = models.TextField(help_text=_("Describe the image"),
|
||||
blank=True, null=True)
|
||||
file_path = models.CharField(max_length=1500)
|
||||
image = models.ImageField()
|
||||
file_hash = models.CharField(verbose_name=_('File hash'), max_length=64, blank=True, null=True, unique=True)
|
||||
last_posted = models.DateTimeField(blank=True, null=True)
|
||||
number_times_posted = models.IntegerField(default=0)
|
||||
|
||||
def __str__(self):
|
||||
if self.title:
|
||||
return self.title
|
||||
else:
|
||||
return f"{self.file_hash}"
|
||||
|
||||
def get_absolute_url(self):
|
||||
reverse("image-update", pk=self.id)
|
||||
|
||||
def set_image_posted(self):
|
||||
self.number_times_posted += 1
|
||||
self.last_posted = datetime.now()
|
||||
|
||||
@staticmethod
|
||||
def consume():
|
||||
files = [f for f in settings.MEDIA_CONSUME_DIR.iterdir() if f.is_file()]
|
||||
|
||||
for path in files:
|
||||
file_hash = get_hash(path)
|
||||
with open(path, 'rb') as f:
|
||||
django_file = File(f, name=settings.MEDIA_PROCESSED_DIR_RELATIVE / path.name)
|
||||
|
||||
img = Image.objects.create(
|
||||
file_path=str(path),
|
||||
image=django_file,
|
||||
file_hash=file_hash
|
||||
)
|
||||
os.remove(path)
|
593
idescriptor/static/idescriptor/css/styles.css
Normal file
593
idescriptor/static/idescriptor/css/styles.css
Normal file
@@ -0,0 +1,593 @@
|
||||
/***************/
|
||||
/* MAIN COLORS */
|
||||
/***************/
|
||||
|
||||
:root {
|
||||
--primary-light-one: #5daa68;
|
||||
--primary-light-two: #4a9455;
|
||||
--primary-semidark-one: #356c3c;
|
||||
--primary-dark-one: #17311b;
|
||||
--secondary-light-one: #faf1cf;
|
||||
--secondary-light-two: #e1d7b5;
|
||||
--background-one: var(--primary-light-one);
|
||||
--background-two: var(--primary-light-two);
|
||||
--background-three: var(--secondary-light-one);
|
||||
--background-four: var(--primary-dark-one);
|
||||
--highlight-one: var(--primary-dark-one);
|
||||
--highlight-one-text: var(--secondary-light-one);
|
||||
--highlight-two: var(--primary-semidark-one);
|
||||
--text-one: var(--secondary-light-one);
|
||||
--shadow-one: var(--primary-dark-one);
|
||||
--text-two: var(--primary-dark-one);
|
||||
--text-three: var(--primary-light-one);
|
||||
--shadow-three: var(--primary-dark-one);
|
||||
}
|
||||
/**************************/
|
||||
/* TAG SETTINGS (GENERAL) */
|
||||
/**************************/
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background-one);
|
||||
color: var(--text-two);
|
||||
}
|
||||
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
table {
|
||||
border: none;
|
||||
border-collapse: collapse;
|
||||
background-color: var(--secondary-light-one);
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
table {
|
||||
font-size: small;
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
border: 2px solid black;
|
||||
border-collapse: collapse;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
th {
|
||||
border: 3px solid black;
|
||||
border-collapse: collapse;
|
||||
padding: 8px;
|
||||
background-color: var(--secondary-light-two);
|
||||
}
|
||||
|
||||
h1, h2 {
|
||||
word-wrap: break-word;
|
||||
color: var(--text-one);
|
||||
text-shadow: 2px 2px var(--shadow-one);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
textarea {
|
||||
border-radius: 10px;
|
||||
width: 100%;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
|
||||
/**************/
|
||||
/* CONTAINERS */
|
||||
/**************/
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.container-cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.card {
|
||||
flex: 1 25%;
|
||||
margin: 10px;
|
||||
border-radius: 8px;
|
||||
padding: 5px;
|
||||
background: var(--background-three);
|
||||
color: var(--text-two);
|
||||
}
|
||||
|
||||
.container-edit-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.btn {
|
||||
margin: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************/
|
||||
/* PARTIAL SPECIFIC CONTAINERS */
|
||||
/*******************************/
|
||||
|
||||
|
||||
/***********/
|
||||
/* BUTTONS */
|
||||
/***********/
|
||||
|
||||
.btn {
|
||||
background-color: var(--primary-light-one);
|
||||
color: var(--secondary-light-one);
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn2 {
|
||||
background-color: var(--secondary-light-one);
|
||||
color: var(--primary-dark-one);
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
/*********************/
|
||||
/* UNIQUE COMPONENTS */
|
||||
/*********************/
|
||||
|
||||
.content-box {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
overflow: hidden;
|
||||
background-color: var(--background-two);
|
||||
border-bottom-left-radius: 15px;
|
||||
border-bottom-right-radius: 15px;
|
||||
}
|
||||
|
||||
|
||||
.nav-link {
|
||||
color: var(--text-one);
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.header a, .header form {
|
||||
float: left;
|
||||
padding: 5px 12px 5px 12px;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.header a:hover {
|
||||
background-color: var(--highlight-one);
|
||||
color: var(--highlight-one-text);
|
||||
}
|
||||
|
||||
.header a.active {
|
||||
background-color: dodgerblue;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.header-right select.option {
|
||||
color: #000;
|
||||
background-color: var(--highlight-one);
|
||||
border: 1px;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
float: right;
|
||||
display: flex;
|
||||
border-radius: 0px 0px 15px 15px;
|
||||
background-color: var(--highlight-two);
|
||||
color: var(--highlight-one-text);
|
||||
padding: 5px 5px 0px 5px;
|
||||
height: 67px;
|
||||
}
|
||||
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.header a {
|
||||
float: none;
|
||||
display: block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
||||
.logo img {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.form-button, .link-button a:link, .link-button a:visited {
|
||||
background-color: #4ba3cd;
|
||||
color: white;
|
||||
padding: 14px 25px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
margin: 10px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
.form-button:hover, .link-button a:hover, .link-button a:active {
|
||||
background-color: #4090b6;
|
||||
}
|
||||
|
||||
.delete-button, .delete-button a:link, .delete-button a:visited {
|
||||
border: none;
|
||||
margin: 10px;
|
||||
background-color: #a3380a;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.delete-button:hover, .delete-button a:hover, .delete-button a:active {
|
||||
background-color: #8f2f06;
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search_result {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.action-menu a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.action-menu ul {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
margin: auto;
|
||||
max-width: 1000px;
|
||||
justify-content: space-between;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.action-menu li {
|
||||
padding: 1rem 2rem 1.15rem;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
color: #ebebeb;
|
||||
min-width: 80px;
|
||||
margin: 10px;
|
||||
font-weight: bold;
|
||||
list-style-type: none;
|
||||
background-color: #4ba3cd;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.action-menu li:hover {
|
||||
background-color: #4090b6;
|
||||
animation: spring 300ms ease-out;
|
||||
}
|
||||
|
||||
.action-menu li:active {
|
||||
transform: translateY(4px);
|
||||
}
|
||||
|
||||
.action-button ul {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
margin: auto;
|
||||
max-width: 1000px;
|
||||
justify-content: space-between;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.action-button li {
|
||||
text-decoration: none;
|
||||
padding: 1rem 2rem 1.15rem;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
color: #ebebeb;
|
||||
min-width: 80px;
|
||||
margin: 10px;
|
||||
font-weight: bold;
|
||||
list-style-type: none;
|
||||
background-color: #4ba3cd;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.action-button li:hover {
|
||||
background-color: #4090b6;
|
||||
animation: spring 300ms ease-out;
|
||||
}
|
||||
|
||||
.action-button li:active {
|
||||
transform: translateY(4px);
|
||||
}
|
||||
|
||||
|
||||
@keyframes spring {
|
||||
15% {
|
||||
-webkit-transform-origin: center center;
|
||||
-webkit-transform: scale(1.1, 1.05);
|
||||
}
|
||||
40% {
|
||||
-webkit-transform-origin: center center;
|
||||
-webkit-transform: scale(0.95, 0.95);
|
||||
}
|
||||
75% {
|
||||
-webkit-transform-origin: center center;
|
||||
-webkit-transform: scale(1.025, 1);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform-origin: center center;
|
||||
-webkit-transform: scale(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.tag {
|
||||
border: black 1px solid;
|
||||
border-radius: 0.3rem;
|
||||
padding: 2px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.sex {
|
||||
background: #F0DEDE;
|
||||
}
|
||||
|
||||
.species {
|
||||
background: #F8ECD7;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.photos {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.card-photo {
|
||||
flex: 1 16%;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.card-photo img {
|
||||
max-width: 100%;
|
||||
border-radius: 10%;
|
||||
}
|
||||
|
||||
|
||||
.card h1 {
|
||||
color: var(--text-three);
|
||||
text-shadow: 1px 1px var(--shadow-three);
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
color: var(--text-three);
|
||||
text-shadow: 1px 1px var(--shadow-three);
|
||||
}
|
||||
|
||||
.card img {
|
||||
max-width: 100%;
|
||||
border-radius: 10%;
|
||||
}
|
||||
|
||||
|
||||
.header-card-adoption-notice {
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
}
|
||||
|
||||
.table-adoption-notice-info {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 920px) {
|
||||
.card {
|
||||
flex: 1 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.adoption-notice-img img, img {
|
||||
max-width: 100%;
|
||||
border-radius: 10%;
|
||||
margin: 5px;
|
||||
max-height: 250px;
|
||||
}
|
||||
|
||||
.img-small img {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
.btn-notification {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Make the badge float in the top right corner of the button */
|
||||
.button__badge {
|
||||
background-color: #fa3e3e;
|
||||
border-radius: 2px;
|
||||
color: white;
|
||||
|
||||
padding: 1px 3px;
|
||||
font-size: 10px;
|
||||
|
||||
position: absolute; /* Position the badge within the relatively positioned button */
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.adoption-card-report-link, .notification-card-mark-read {
|
||||
margin-left: auto;
|
||||
font-size: 2rem;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.notification-card-mark-read {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.heading-card-adoption-notice {
|
||||
word-wrap: anywhere;
|
||||
}
|
||||
|
||||
.tags {
|
||||
margin-right: auto;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#form-adoption-notice, #form-registration {
|
||||
.form-group {
|
||||
margin: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.detail-adoption-notice-header h1 {
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.detail-adoption-notice-header a {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
}
|
||||
|
||||
@media (max-width: 920px) {
|
||||
.detail-adoption-notice-header h1 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.detail-adoption-notice-header a {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
}
|
||||
}
|
||||
|
||||
.container-comment-headers, .container-cards {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
background: var(--background-two);
|
||||
border-radius: 8px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.comment, .notification {
|
||||
flex: 1 100%;
|
||||
margin: 10px;
|
||||
border-radius: 8px;
|
||||
padding: 5px;
|
||||
background: var(--background-three);
|
||||
color: var(--text-two);
|
||||
}
|
||||
|
||||
|
||||
|
||||
.form-comments {
|
||||
.btn {
|
||||
margin: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.announcement {
|
||||
flex: 1 100%;
|
||||
margin: 10px;
|
||||
border-radius: 8px;
|
||||
padding: 5px;
|
||||
background: var(--background-three);
|
||||
color: var(--text-two);
|
||||
|
||||
h1 {
|
||||
font-size: 1.2rem;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
color: var(--text-two);
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.form-search {
|
||||
select, input {
|
||||
background-color: var(--primary-light-one);
|
||||
color: var(--text-one);
|
||||
border-radius: 3px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/************************/
|
||||
/* GENERAL HIGHLIGHTING */
|
||||
/************************/
|
||||
|
||||
.important {
|
||||
border: #e01137 4px solid;
|
||||
}
|
||||
|
||||
.warning {
|
||||
border: #e09e11 4px solid;
|
||||
}
|
||||
|
||||
.info {
|
||||
border: rgba(17, 58, 224, 0.51) 4px solid;
|
||||
}
|
||||
|
||||
|
||||
/*******/
|
||||
/* MAP */
|
||||
/*******/
|
||||
|
||||
.marker {
|
||||
background-image: url('../img/logo_transparent.png');
|
||||
background-size: cover;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.maplibregl-popup {
|
||||
max-width: 600px !important;
|
||||
}
|
||||
|
||||
.maplibregl-popup-content {
|
||||
background-color: var(--background-three) !important;
|
||||
border-radius: 8px !important;
|
||||
}
|
||||
|
||||
.map-in-content #map {
|
||||
height: 500px;
|
||||
width: 90%;
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
After Width: | Height: | Size: 61 KiB |
BIN
idescriptor/static/idescriptor/favicon/apple-touch-icon.png
Normal file
BIN
idescriptor/static/idescriptor/favicon/apple-touch-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
idescriptor/static/idescriptor/favicon/favicon-16x16.png
Normal file
BIN
idescriptor/static/idescriptor/favicon/favicon-16x16.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 432 B |
BIN
idescriptor/static/idescriptor/favicon/favicon-32x32.png
Normal file
BIN
idescriptor/static/idescriptor/favicon/favicon-32x32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
BIN
idescriptor/static/idescriptor/favicon/favicon.ico
Normal file
BIN
idescriptor/static/idescriptor/favicon/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
1
idescriptor/static/idescriptor/favicon/site.webmanifest
Normal file
1
idescriptor/static/idescriptor/favicon/site.webmanifest
Normal file
@@ -0,0 +1 @@
|
||||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
32
idescriptor/templates/idescriptor/base_generic.html
Normal file
32
idescriptor/templates/idescriptor/base_generic.html
Normal file
@@ -0,0 +1,32 @@
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
{% block title %}{% endblock %}
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="{% translate 'Describe images' %}">
|
||||
<!-- Add additional CSS in static file -->
|
||||
<link rel="stylesheet" href="{% static 'idescriptor/css/styles.css' %}">
|
||||
<link href="{% static 'fontawesomefree/css/fontawesome.css' %}" rel="stylesheet" type="text/css">
|
||||
<link href="{% static 'fontawesomefree/css/brands.css' %}" rel="stylesheet" type="text/css">
|
||||
<link href="{% static 'fontawesomefree/css/solid.css' %}" rel="stylesheet" type="text/css">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'idescriptor/favicon/apple-touch-icon.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'idescriptor/favicon/favicon-32x32.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'idescriptor/favicon/favicon-16x16.png' %}">
|
||||
</head>
|
||||
<body>
|
||||
{% block header %}
|
||||
{% include "idescriptor/header.html" %}
|
||||
{% endblock %}
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-10 content-box">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
12
idescriptor/templates/idescriptor/header.html
Normal file
12
idescriptor/templates/idescriptor/header.html
Normal file
@@ -0,0 +1,12 @@
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
<div class="header">
|
||||
<nav id="nav" class="nav justify-content-center">
|
||||
<a class="nav-link " href=""><i class="fas fa-search"></i> {% translate 'Suchen' %}</a>
|
||||
<a class="nav-link " href=""><i
|
||||
class="fas fa-feather"></i> {% translate 'Vermittlung hinzufügen' %}</a>
|
||||
<a class="nav-link " href="}"><i class="fas fa-info"></i> {% translate 'Über uns' %}</a>
|
||||
|
||||
</nav>
|
||||
</div>
|
16
idescriptor/templates/idescriptor/image-form.html
Normal file
16
idescriptor/templates/idescriptor/image-form.html
Normal file
@@ -0,0 +1,16 @@
|
||||
{% extends "idescriptor/base_generic.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}<title>{% translate "Organize images for a bot" %}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
<h1>{{ image.title }}</h1>
|
||||
<img src="{{ image.image.url }}" alt="{{ image.alt_text }}">
|
||||
<div class="container">
|
||||
{% crispy form %}
|
||||
<a class="btn" href="{% url 'index' %}">{% trans 'Continue' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
28
idescriptor/templates/idescriptor/index.html
Normal file
28
idescriptor/templates/idescriptor/index.html
Normal file
@@ -0,0 +1,28 @@
|
||||
{% extends "idescriptor/base_generic.html" %}
|
||||
{% load i18n %}
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}<title>{% translate "Organize images for a bot" %}</title>{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card">
|
||||
{% if image_to_describe %}
|
||||
<img src="{{ image_to_describe.image.url }}" alt="{{ image_to_describe.alt_text }}">
|
||||
<div class="container-form">
|
||||
{% crispy form %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p>All done</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card">
|
||||
<h1>{% trans "Controls" %}</h1>
|
||||
<form class="" method="post">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="consume">
|
||||
<button class="btn" type="submit" id="submit">
|
||||
<i class="fa-solid fa-broom"></i> {% translate "Consume" %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
3
idescriptor/tests.py
Normal file
3
idescriptor/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
8
idescriptor/urls.py
Normal file
8
idescriptor/urls.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="index"),
|
||||
path("image/<uuid:pk>/", views.ImageFormView.as_view(), name="image-update"),
|
||||
]
|
51
idescriptor/views.py
Normal file
51
idescriptor/views.py
Normal file
@@ -0,0 +1,51 @@
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from django.views.generic import FormView, UpdateView
|
||||
from django.urls import reverse
|
||||
|
||||
from idescriptor.forms import ImageForm
|
||||
from idescriptor.models import Image
|
||||
from django.db.models import Q
|
||||
|
||||
|
||||
def get_image_for_descriptor():
|
||||
image = Image.objects.filter(Q(alt_text=None) | Q(title=None)).first()
|
||||
return image
|
||||
|
||||
|
||||
class ImageFormView(UpdateView):
|
||||
model = Image
|
||||
form_class = ImageForm
|
||||
template_name = "idescriptor/image-form.html"
|
||||
success_url = "."
|
||||
|
||||
def form_valid(self, form):
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
def index(request):
|
||||
"""View function for home page of site."""
|
||||
image_to_describe = get_image_for_descriptor()
|
||||
|
||||
if request.method == "POST":
|
||||
action = request.POST.get("action")
|
||||
if action == "consume":
|
||||
Image.consume()
|
||||
|
||||
if action == "add_image_details":
|
||||
form = ImageForm(request.POST, instance=image_to_describe)
|
||||
|
||||
if form.is_valid():
|
||||
instance = form.save()
|
||||
print("Didi")
|
||||
return HttpResponseRedirect(reverse("image-update", args=(instance.id,)))
|
||||
else:
|
||||
print("Dodo")
|
||||
else:
|
||||
form = ImageForm(instance=image_to_describe)
|
||||
else:
|
||||
form = ImageForm(instance=image_to_describe)
|
||||
|
||||
context = {"image_to_describe": image_to_describe, "form": form}
|
||||
|
||||
return render(request, 'idescriptor/index.html', context=context)
|
Reference in New Issue
Block a user