Working list of messages

This commit is contained in:
Gabriel Augendre 2021-01-24 15:43:51 +01:00
parent 17030fb850
commit 4dbde11a0b
13 changed files with 379 additions and 12 deletions

46
poetry.lock generated
View file

@ -80,6 +80,30 @@ sqlparse = ">=0.2.2"
argon2 = ["argon2-cffi (>=16.1.0)"]
bcrypt = ["bcrypt"]
[[package]]
name = "django-cleanup"
version = "5.1.0"
description = "Deletes old files."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "django-phonenumber-field"
version = "5.0.0"
description = "An international phone number field for django models."
category = "main"
optional = false
python-versions = ">=3.5"
[package.dependencies]
Django = ">=2.2"
phonenumbers = {version = ">=7.0.2", optional = true, markers = "extra == \"phonenumbers\""}
[package.extras]
phonenumbers = ["phonenumbers (>=7.0.2)"]
phonenumberslite = ["phonenumberslite (>=7.0.2)"]
[[package]]
name = "filelock"
version = "3.0.12"
@ -141,6 +165,14 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
[package.dependencies]
pyparsing = ">=2.0.2"
[[package]]
name = "phonenumbers"
version = "8.12.16"
description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "pluggy"
version = "0.13.1"
@ -286,7 +318,7 @@ testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake
[metadata]
lock-version = "1.1"
python-versions = "^3.8"
content-hash = "bb2539651a82ba0577d1d9f1a70d2777f12479cffd8ed4f1d9fd8cdffdbbbef1"
content-hash = "ebea8c626e42840de0ccf13d5cdb190d18b04028b1115d0ff639eb37dd93cfb6"
[metadata.files]
appdirs = [
@ -321,6 +353,14 @@ django = [
{file = "Django-3.1.5-py3-none-any.whl", hash = "sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9"},
{file = "Django-3.1.5.tar.gz", hash = "sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7"},
]
django-cleanup = [
{file = "django-cleanup-5.1.0.tar.gz", hash = "sha256:8976aec12a22913afb3d1fcb541b1aedde2f5ec243e4260c5ff78bb6aa75a089"},
{file = "django_cleanup-5.1.0-py2.py3-none-any.whl", hash = "sha256:71e098c7d9ac3f3da40b95cff9c0bc51218d40ef419261232f46ba3141c50acc"},
]
django-phonenumber-field = [
{file = "django-phonenumber-field-5.0.0.tar.gz", hash = "sha256:1eb7af3a108744665f7c3939d38aa15b3728c57d13d45d656b0a2aa11e8cdc3c"},
{file = "django_phonenumber_field-5.0.0-py3-none-any.whl", hash = "sha256:adb46905cc4ecb19d8494424e1c4352f24946bb472340a2a17257d44bf8228e6"},
]
filelock = [
{file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"},
{file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
@ -345,6 +385,10 @@ packaging = [
{file = "packaging-20.8-py2.py3-none-any.whl", hash = "sha256:24e0da08660a87484d1602c30bb4902d74816b6985b93de36926f5bc95741858"},
{file = "packaging-20.8.tar.gz", hash = "sha256:78598185a7008a470d64526a8059de9aaa449238f280fc9eb6b13ba6c4109093"},
]
phonenumbers = [
{file = "phonenumbers-8.12.16-py2.py3-none-any.whl", hash = "sha256:56ad29025b8f885945506350b06d77afbc506c5463141d77a5df767280a7ee0b"},
{file = "phonenumbers-8.12.16.tar.gz", hash = "sha256:a820ab08c980ef24a2d2a1ead4f8d7016fdf008e484d1aecf7ff0b32cc475e16"},
]
pluggy = [
{file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
{file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},

View file

@ -7,6 +7,8 @@ authors = ["Gabriel Augendre <gabriel@augendre.info>"]
[tool.poetry.dependencies]
python = "^3.8"
Django = "^3.1.5"
django-phonenumber-field = {extras = ["phonenumbers"], version = "^5.0.0"}
django-cleanup = "^5.1.0"
[tool.poetry.dev-dependencies]
pytest = "^6.2"

View file

@ -25,21 +25,32 @@ SECRET_KEY = "-kza5t&!y!4tt9t0ha+ggadil@bz9wca2f$!=@@efx*&-!4+@t"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
ALLOWED_HOSTS = [
"*",
]
# Application definition
CUSTOM_APPS = ["pictures"]
CUSTOM_APPS = [
"pictures",
]
INSTALLED_APPS = [
DJANGO_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
] + CUSTOM_APPS
]
EXTERNAL_APPS = [
"phonenumber_field",
"django_cleanup.apps.CleanupConfig",
]
INSTALLED_APPS = DJANGO_APPS + CUSTOM_APPS + EXTERNAL_APPS
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
@ -112,9 +123,9 @@ AUTH_PASSWORD_VALIDATORS = [
# USE_TZ = True
LANGUAGE_CODE = "fr-fr"
TIME_ZONE = None
USE_I18N = False
USE_L10N = False
TIME_ZONE = "Europe/Paris"
USE_I18N = True
USE_L10N = True
USE_TZ = False
# Static files (CSS, JavaScript, Images)
@ -123,3 +134,6 @@ USE_TZ = False
STATIC_URL = "/static/"
AUTH_USER_MODEL = "pictures.User"
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"

View file

@ -1,6 +1,37 @@
from django.contrib import admin
from django.contrib.admin import register
from django.contrib.auth.admin import UserAdmin
from pictures.models import User
from pictures.models import Contact, Media, Message, User
admin.site.register(User, UserAdmin)
@register(Contact)
class ContactAdmin(admin.ModelAdmin):
list_display = [
"display_name",
"phone_number",
]
class MediaInline(admin.TabularInline):
model = Media
@register(Message)
class MessageAdmin(admin.ModelAdmin):
list_display = [
"sender",
"content",
]
inlines = [
MediaInline,
]
@register(Media)
class MediaAdmin(admin.ModelAdmin):
list_display = [
"sender",
]

View file

@ -0,0 +1,80 @@
# Generated by Django 3.1.5 on 2021-01-24 13:07
import django.db.models.deletion
import phonenumber_field.modelfields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pictures", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="Contact",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"phone_number",
phonenumber_field.modelfields.PhoneNumberField(
max_length=128, region=None
),
),
("display_name", models.CharField(max_length=250)),
],
),
migrations.CreateModel(
name="Message",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("content", models.TextField(blank=True)),
(
"sender",
models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
to="pictures.contact",
),
),
],
),
migrations.CreateModel(
name="Media",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("file", models.FileField(upload_to="")),
(
"message",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="pictures.message",
),
),
],
),
]

View file

@ -0,0 +1,19 @@
# Generated by Django 3.1.5 on 2021-01-24 13:56
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pictures", "0002_contact_media_message"),
]
operations = [
migrations.AddField(
model_name="message",
name="received_at",
field=models.DateTimeField(default=django.utils.timezone.now),
),
]

View file

@ -0,0 +1,32 @@
# Generated by Django 3.1.5 on 2021-01-24 14:07
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pictures", "0003_message_received_at"),
]
operations = [
migrations.AlterField(
model_name="media",
name="message",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="media_files",
to="pictures.message",
),
),
migrations.AlterField(
model_name="message",
name="sender",
field=models.ForeignKey(
on_delete=django.db.models.deletion.PROTECT,
related_name="messages",
to="pictures.contact",
),
),
]

View file

@ -1,6 +1,50 @@
import datetime
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils import timezone
from django.utils.text import Truncator
from phonenumber_field.modelfields import PhoneNumberField
class User(AbstractUser):
pass
class Contact(models.Model):
phone_number = PhoneNumberField()
display_name = models.CharField(max_length=250)
def __str__(self):
return self.display_name
class Message(models.Model):
sender = models.ForeignKey(
Contact,
on_delete=models.PROTECT,
related_name="messages",
)
content = models.TextField(blank=True)
received_at = models.DateTimeField(default=timezone.now)
def __str__(self):
truncator = Truncator(self.content)
truncated_content = truncator.words(15)
return f"De {self.sender}: {truncated_content}"
class Media(models.Model):
message = models.ForeignKey(
Message,
on_delete=models.CASCADE,
related_name="media_files",
)
file = models.FileField()
def __str__(self):
return f"Envoyé avec '{self.message}'"
@property
def sender(self):
return self.message.sender

View file

@ -0,0 +1,48 @@
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
* {
margin: 0;
padding: 0;
}
main {
margin: 1em auto;
max-width: 750px;
}
.message {
display: flex;
flex-direction: row;
margin-bottom: 2em;
}
.message .picture-preview {
flex-grow: 0;
}
.picture-preview > * {
height: 200px;
width: 200px;
object-fit: cover;
background-color: lightgray;
border-radius: 5px;
}
.message .content-block {
flex-grow: 1;
margin-left: 1em;
}
.message .content-block .metadata .name {
font-size: 120%;
}
.message .content-block .metadata .date {
color: #626262;
}
.message .content-block .text {
margin-top: 1em;
}

View file

@ -0,0 +1,20 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Picture Display</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static "pictures/style.css" %}">
</head>
<body>
<nav>
{% block buttons %}{% endblock %}
</nav>
<main>
{% block content %}
{% endblock %}
</main>
</body>
</html>

View file

@ -0,0 +1,22 @@
{% extends "pictures/base.html" %}
{% block content %}
{% for message in messages %}
<div class="message">
<div class="picture-preview">
{% if message.media_files.first %}
<img src="{{ message.media_files.first.file.url }}" alt="">
{% else %}
<div class="filler"></div>
{% endif %}
</div>
<div class="content-block">
<div class="metadata">
<div class="name">De: {{ message.sender.display_name }}</div>
<div class="date">{{ message.received_at }}</div>
</div>
<div class="text">{{ message.content | linebreaks }}</div>
</div>
</div>
{% endfor %}
{% endblock %}

View file

@ -1,5 +1,9 @@
from django.conf import settings
from django.conf.urls.static import static
from django.urls import path
from pictures.views import MessageListView
urlpatterns = [
# path("", PicturesView.as_view(), name="pictures-list"),
]
path("", MessageListView.as_view(), name="messages-list"),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -1,3 +1,10 @@
from django.shortcuts import render
from django.views import generic
# Create your views here.
from pictures.models import Message
class MessageListView(generic.ListView):
model = Message
template_name = "pictures/messages-list.html"
context_object_name = "messages"