Add the log app
This commit is contained in:
parent
fc0ebef9cb
commit
2d40a57613
18 changed files with 272 additions and 0 deletions
|
@ -30,3 +30,5 @@ You can set the following variables in the `.env` file:
|
|||
* `KHAGANAT_TIME_ZONE`: Time zone, default is `Europe/Paris`.
|
||||
* `KHAGANAT_STATIC_URL`: URL for static files, default is `/static/`.
|
||||
* `KHAGANAT_STATIC_ROOT`: Absolute path to the directory where static files should be collected.
|
||||
* `KHAGANAT_LOGS_MIN_DAYS`: Numbers of days before logs are hidden, default is 7.
|
||||
* `KHAGANAT_LOGS_MAX_DAYS`: Number of days before logs are published, default is 0.
|
||||
|
|
|
@ -41,6 +41,7 @@ INSTALLED_APPS = [
|
|||
'khaganat',
|
||||
'pages.apps.PagesConfig',
|
||||
'navbar.apps.NavbarConfig',
|
||||
'logs.apps.LogsConfig',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -131,3 +132,7 @@ USE_TZ = True
|
|||
|
||||
STATIC_URL = os.getenv('KHAGANAT_STATIC_URL', default='/static/')
|
||||
STATIC_ROOT = os.getenv('KHAGANAT_STATIC_ROOT', default='') or None
|
||||
|
||||
# Logs configuration
|
||||
KHAGANAT_LOGS_MIN_DAYS = int(os.getenv('KHAGANAT_LOGS_MIN_DAYS', default='0'))
|
||||
KHAGANAT_LOGS_MAX_DAYS = int(os.getenv('KHAGANAT_LOGS_MAX_DAYS', default='7'))
|
||||
|
|
|
@ -27,4 +27,5 @@ urlpatterns = [
|
|||
urlpatterns += i18n_patterns(
|
||||
path('', index),
|
||||
path('page/', include('pages.urls')),
|
||||
path('logs/', include('logs.urls')),
|
||||
)
|
||||
|
|
0
logs/__init__.py
Normal file
0
logs/__init__.py
Normal file
5
logs/admin.py
Normal file
5
logs/admin.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from django.contrib import admin
|
||||
from .models import Source,Entry
|
||||
|
||||
admin.site.register(Source)
|
||||
admin.site.register(Entry)
|
5
logs/apps.py
Normal file
5
logs/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class LogsConfig(AppConfig):
|
||||
name = 'logs'
|
25
logs/locale/en/LC_MESSAGES/django.po
Normal file
25
logs/locale/en/LC_MESSAGES/django.po
Normal file
|
@ -0,0 +1,25 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-01-27 18:32+0100\n"
|
||||
"PO-Revision-Date: 2018-01-27 18:35+1\n"
|
||||
"Last-Translator: Rodolphe Bréard <rodolphe@what.tf>\n"
|
||||
"Language-Team: Khaganat <assoc@khaganat.net>\n"
|
||||
"Language: English\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: templates/logs/entries.html:10 templates/logs/entries.html:18
|
||||
msgid "logs_back"
|
||||
msgstr "Back"
|
||||
|
||||
#: templates/logs/index.html:4 templates/logs/index.html:8
|
||||
msgid "logs_title"
|
||||
msgstr "Khaganat's logs"
|
||||
|
||||
#: templates/logs/index.html:33
|
||||
msgid "logs_no_logs_available"
|
||||
msgstr "No logs available."
|
25
logs/locale/fr/LC_MESSAGES/django.po
Normal file
25
logs/locale/fr/LC_MESSAGES/django.po
Normal file
|
@ -0,0 +1,25 @@
|
|||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2018-01-27 18:32+0100\n"
|
||||
"PO-Revision-Date: 2018-01-27 18:35+1\n"
|
||||
"Last-Translator: Rodolphe Bréard <rodolphe@what.tf>\n"
|
||||
"Language-Team: Khaganat <assoc@khaganat.net>\n"
|
||||
"Language: Français\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: templates/logs/entries.html:10 templates/logs/entries.html:18
|
||||
msgid "logs_back"
|
||||
msgstr "Retour"
|
||||
|
||||
#: templates/logs/index.html:4 templates/logs/index.html:8
|
||||
msgid "logs_title"
|
||||
msgstr "Journaux de conversation de Khaganat"
|
||||
|
||||
#: templates/logs/index.html:33
|
||||
msgid "logs_no_logs_available"
|
||||
msgstr "Aucun journal de conversation disponible."
|
38
logs/migrations/0001_initial.py
Normal file
38
logs/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Generated by Django 2.0.1 on 2018-01-27 13:35
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Entry',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('hidden', models.BooleanField(default=False)),
|
||||
('created', models.DateTimeField()),
|
||||
('nick', models.CharField(max_length=128)),
|
||||
('content', models.CharField(max_length=2048)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Source',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=128)),
|
||||
('hidden', models.BooleanField(default=False)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='entry',
|
||||
name='source',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='logs.Source'),
|
||||
),
|
||||
]
|
19
logs/migrations/0002_source_slug.py
Normal file
19
logs/migrations/0002_source_slug.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.0.1 on 2018-01-27 15:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('logs', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='source',
|
||||
name='slug',
|
||||
field=models.CharField(default='', max_length=128),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
18
logs/migrations/0003_auto_20180127_1700.py
Normal file
18
logs/migrations/0003_auto_20180127_1700.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.0.1 on 2018-01-27 16:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('logs', '0002_source_slug'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='source',
|
||||
name='slug',
|
||||
field=models.CharField(max_length=128, unique=True),
|
||||
),
|
||||
]
|
0
logs/migrations/__init__.py
Normal file
0
logs/migrations/__init__.py
Normal file
17
logs/models.py
Normal file
17
logs/models.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from django.db import models
|
||||
|
||||
class Source(models.Model):
|
||||
name = models.CharField(max_length=128)
|
||||
slug = models.CharField(max_length=128, unique=True)
|
||||
hidden = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Entry(models.Model):
|
||||
source = models.ForeignKey(Source, on_delete=models.CASCADE)
|
||||
hidden = models.BooleanField(default=False)
|
||||
created = models.DateTimeField()
|
||||
nick = models.CharField(max_length=128)
|
||||
content = models.CharField(max_length=2048)
|
21
logs/templates/logs/entries.html
Normal file
21
logs/templates/logs/entries.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
{% extends "khaganat/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{{ source_channel.name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content-bloc">
|
||||
<h2>{{ source_channel.name }} ({{ date }})</h2>
|
||||
<div>
|
||||
<a class="btn btn-primary" href="{% url 'log_index' %}" role="button">{% trans "logs_back" %}</a>
|
||||
</div>
|
||||
<div>
|
||||
{% for entry in entries%}
|
||||
{{ entry.created|date:"H:i:s" }} <{{ entry.nick }}> {{ entry.content }}<br />
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div>
|
||||
<a class="btn btn-primary" href="{% url 'log_index' %}" role="button">{% trans "logs_back" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
36
logs/templates/logs/index.html
Normal file
36
logs/templates/logs/index.html
Normal file
|
@ -0,0 +1,36 @@
|
|||
{% extends "khaganat/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "logs_title" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content-bloc">
|
||||
<h2>{% trans "logs_title" %}</h2>
|
||||
{% if sources %}
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<div class="list-group" id="list-tab" role="tablist">
|
||||
{% for source in sources %}
|
||||
<a class="list-group-item list-group-item-action" id="list-{{ source.source }}-list" data-toggle="list" href="#list-{{ source.source }}" role="tab" aria-controls="home">{{ source.source__name }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
{% for source in sources %}
|
||||
<div class="tab-pane fade show" id="list-{{ source.source }}" role="tabpanel" aria-labelledby="list-{{ source.source }}-list">
|
||||
<div class="list-group">
|
||||
{% for date in source.stats %}
|
||||
<a href="{% url 'log_day' source=source.source__slug year=date.date.year month=date.date.month day=date.date.day %}" class="list-group-item list-group-item-action">{{ date.date }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>{% trans "logs_no_logs_available" %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
3
logs/tests.py
Normal file
3
logs/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
8
logs/urls.py
Normal file
8
logs/urls.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.IndexView.as_view(), name='log_index'),
|
||||
path('<slug:source>/<int:year>/<int:month>/<int:day>/', views.EntriesView.as_view(), name='log_day'),
|
||||
]
|
44
logs/views.py
Normal file
44
logs/views.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
from django.db.models.functions import TruncDate
|
||||
from django.db.models import Count
|
||||
from django.views import generic
|
||||
from django.conf import settings
|
||||
from .models import Source,Entry
|
||||
import datetime
|
||||
|
||||
class IndexView(generic.ListView):
|
||||
template_name = 'logs/index.html'
|
||||
context_object_name = 'sources'
|
||||
|
||||
def get_queryset(self):
|
||||
now = datetime.date.today()
|
||||
begin_date = now - datetime.timedelta(days=settings.KHAGANAT_LOGS_MAX_DAYS)
|
||||
end_date = now - datetime.timedelta(days=settings.KHAGANAT_LOGS_MIN_DAYS - 1)
|
||||
out = []
|
||||
for src in Entry.objects.filter(hidden=False, source__hidden=False, created__range=(begin_date, end_date)).values('source', 'source__name', 'source__slug').annotate(nb=Count('id')):
|
||||
src['stats'] = Entry.objects.filter(hidden=False, source=src['source'], created__range=(begin_date, end_date)).annotate(date=TruncDate('created')).values('date').annotate(nb=Count('date')).order_by('-date')
|
||||
out.append(src)
|
||||
return out
|
||||
|
||||
|
||||
class EntriesView(generic.ListView):
|
||||
context_object_name = 'entries'
|
||||
template_name = 'logs/entries.html'
|
||||
allow_empty = False
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['date'] = datetime.date(self.kwargs['year'], self.kwargs['month'], self.kwargs['day'])
|
||||
context['source_channel'] = Source.objects.get(slug=self.kwargs['source']),
|
||||
if isinstance(context['source_channel'], tuple):
|
||||
context['source_channel'] = context['source_channel'][0]
|
||||
return context
|
||||
|
||||
def get_queryset(self):
|
||||
dt = datetime.date(self.kwargs['year'], self.kwargs['month'], self.kwargs['day'])
|
||||
now = datetime.date.today()
|
||||
if (now - dt).days > settings.KHAGANAT_LOGS_MAX_DAYS or (now - dt).days < settings.KHAGANAT_LOGS_MIN_DAYS:
|
||||
return Entry.objects.none()
|
||||
src = Source.objects.get(slug=self.kwargs['source'])
|
||||
if src is None or src.hidden:
|
||||
return Entry.objects.none()
|
||||
return Entry.objects.filter(hidden=False, source=src, created__year=dt.year, created__month=dt.month, created__day=dt.day).order_by('created')
|
Loading…
Reference in a new issue