This commit is contained in:
Holger Sielaff
2024-07-03 21:41:03 +02:00
commit 030d3c8059
45 changed files with 2025 additions and 0 deletions

0
content/__init__.py Normal file
View File

73
content/admin.py Normal file
View File

@@ -0,0 +1,73 @@
from django.contrib import admin
from django.utils.safestring import mark_safe
from content.models import Link, MediaFile, Question, QuestionVersion, Level, Label
from lib.utils import color_label
@admin.register(Level)
class LevelAdmin(admin.ModelAdmin):
search_fields = ('name', 'value',)
list_display = ('name', 'value', 'color')
list_editable = ('color',)
@admin.register(Label)
class LabelAdmin(admin.ModelAdmin):
search_fields = ('name',)
list_display = ('name', 'color')
list_editable = ('color',)
@admin.register(Question)
class QuestionAdmin(admin.ModelAdmin):
autocomplete_fields = ('medias', 'links', 'labels',)
list_display = ('name', 'list_labels', 'list_level', 'author')
search_fields = ('name', 'question', 'awnser', 'description', 'label__name', 'level__value', 'level__name',)
def list_labels(self, instance):
return mark_safe(', '.join([color_label(l, value=l.name) for l in instance.labels.all()]))
def list_level(self, instance):
return mark_safe(color_label(instance.level, value=str(instance.level)))
list_level.short_description = 'Level'
list_labels.short_description = 'Labels'
"""
fieldsets = [
('Basic', {'fields': [('name', 'author',)]}),
('Question', {'fields': ('question', 'awnser',), }),
('Meta', {'fields': ('level', 'labels', 'description'), }),
('Media and Links', {'fields': ('medias', 'links'), },
)
]
"""
@admin.register(MediaFile)
class MediaFileAdmin(admin.ModelAdmin):
search_fields = ('name', 'file__name')
list_display = ('name', 'file')
@admin.register(Link)
class LinkAdmin(admin.ModelAdmin):
search_fields = ('url',)
@admin.register(QuestionVersion)
class QuestionVersionAdmin(admin.ModelAdmin):
def has_add_permission(self, request):
"""If a group right is not set :)
Allways be able to create a version
"""
return True
def has_change_permission(self, request, obj=None):
"""Versions are immutable """
return False
def has_delete_permission(self, request, obj=None):
"""Versions are immutable """
return False

6
content/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ContentConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'content'

View File

@@ -0,0 +1,46 @@
# Generated by Django 4.2.13 on 2024-06-29 12:58
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Link',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('url', models.URLField(unique=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
),
migrations.CreateModel(
name='MediaFile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('file', models.FileField(upload_to='content/media')),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
),
migrations.CreateModel(
name='Question',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=500, unique=True)),
('question', models.TextField()),
('awnser', models.TextField()),
('description', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('links', models.ManyToManyField(to='content.link')),
('medias', models.ManyToManyField(to='content.mediafile')),
],
),
]

View File

@@ -0,0 +1,100 @@
# Generated by Django 4.2.13 on 2024-06-30 07:20
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('content', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='mediafile',
name='author',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='question',
name='author',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='mediafile',
name='file',
field=models.FileField(upload_to='filestore/media'),
),
migrations.AlterField(
model_name='question',
name='awnser',
field=models.TextField(db_index=True),
),
migrations.AlterField(
model_name='question',
name='links',
field=models.ManyToManyField(blank=True, to='content.link'),
),
migrations.AlterField(
model_name='question',
name='medias',
field=models.ManyToManyField(blank=True, to='content.mediafile'),
),
migrations.AlterField(
model_name='question',
name='name',
field=models.CharField(db_index=True, max_length=500, unique=True),
),
migrations.AlterField(
model_name='question',
name='question',
field=models.TextField(db_index=True),
),
migrations.CreateModel(
name='QuestionVersion',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('data', models.JSONField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('author', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
('question', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='content.question')),
],
),
migrations.CreateModel(
name='Level',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(db_index=True, max_length=25, unique=True)),
('value', models.IntegerField(db_index=True, unique=True)),
('description', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('author', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Label',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(db_index=True, max_length=25, unique=True)),
('description', models.TextField(blank=True, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('author', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='question',
name='labels',
field=models.ManyToManyField(blank=True, null=True, to='content.label'),
),
migrations.AddField(
model_name='question',
name='level',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='content.level'),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.2.13 on 2024-06-30 07:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('content', '0002_mediafile_author_question_author_and_more'),
]
operations = [
migrations.AlterField(
model_name='question',
name='labels',
field=models.ManyToManyField(blank=True, to='content.label'),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 4.2.13 on 2024-06-30 17:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('content', '0003_alter_question_labels'),
]
operations = [
migrations.AddField(
model_name='mediafile',
name='name',
field=models.CharField(db_index=True, default='N.N.', max_length=25, unique=True),
preserve_default=False,
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.2.13 on 2024-07-01 08:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('content', '0004_mediafile_name'),
]
operations = [
migrations.AddField(
model_name='question',
name='buzzword',
field=models.CharField(blank=True, max_length=25, null=True),
),
]

View File

@@ -0,0 +1,48 @@
# Generated by Django 4.2.13 on 2024-07-02 06:53
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import filer.fields.file
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('filer', '0017_image__transparent'),
('content', '0005_question_buzzword'),
]
operations = [
migrations.AddField(
model_name='link',
name='author',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='link',
name='description',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='link',
name='name',
field=models.CharField(blank=True, db_index=True, max_length=25, null=True),
),
migrations.AddField(
model_name='mediafile',
name='description',
field=models.TextField(blank=True, null=True),
),
migrations.AlterField(
model_name='link',
name='url',
field=models.URLField(db_index=True, unique=True),
),
migrations.AlterField(
model_name='mediafile',
name='file',
field=filer.fields.file.FilerFileField(on_delete=django.db.models.deletion.RESTRICT, related_name='media_file', to='filer.file'),
),
]

View File

@@ -0,0 +1,27 @@
# Generated by Django 4.2.13 on 2024-07-02 08:04
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import lib.middleware.current_user
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('content', '0006_link_author_link_description_link_name_and_more'),
]
operations = [
migrations.AlterField(
model_name='label',
name='author',
field=models.ForeignKey(default=lib.middleware.current_user.get_current_user, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='level',
name='author',
field=models.ForeignKey(default=lib.middleware.current_user.get_current_user, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
]

View File

@@ -0,0 +1,27 @@
# Generated by Django 4.2.13 on 2024-07-02 08:09
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import lib.middleware.current_user
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('content', '0007_alter_label_author_alter_level_author'),
]
operations = [
migrations.AlterField(
model_name='link',
name='author',
field=models.ForeignKey(default=lib.middleware.current_user.get_current_user, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='mediafile',
name='author',
field=models.ForeignKey(default=lib.middleware.current_user.get_current_user, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 4.2.13 on 2024-07-02 12:27
import colorfield.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('content', '0008_alter_link_author_alter_mediafile_author'),
]
operations = [
migrations.AddField(
model_name='label',
name='color',
field=colorfield.fields.ColorField(default='#666666', image_field=None, max_length=25, samples=None),
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 4.2.13 on 2024-07-02 12:28
import colorfield.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('content', '0009_label_color'),
]
operations = [
migrations.AddField(
model_name='level',
name='color',
field=colorfield.fields.ColorField(default='#F90F90', image_field=None, max_length=25, samples=None),
),
]

View File

150
content/models.py Normal file
View File

@@ -0,0 +1,150 @@
import json
import logging
import os
from colorfield.fields import ColorField
from django.contrib.auth.models import User
from django.db import models
from django.dispatch import receiver
from django.forms.models import model_to_dict
from django.utils.safestring import mark_safe
from filer.fields.file import FilerFileField
from lib import get_current_user
class MediaFile(models.Model):
name = models.CharField(max_length=25, unique=True, db_index=True)
# https://django-filer.readthedocs.io/en/latest/extending_filer.html
# https://pypi.org/project/django-thumbnails/
file = FilerFileField(on_delete=models.RESTRICT, related_name='media_file')
description = models.TextField(null=True, blank=True)
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, default=get_current_user)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
def __repr__(self):
return self.file.name
class Link(models.Model):
name = models.CharField(max_length=25, null=True, blank=True, db_index=True)
url = models.URLField(unique=True, db_index=True)
description = models.TextField(null=True, blank=True)
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, default=get_current_user)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.url
def __repr__(self):
return self.url
class Level(models.Model):
name = models.CharField(max_length=25, unique=True, db_index=True)
value = models.IntegerField(unique=True, db_index=True)
description = models.TextField(null=True, blank=True)
color = ColorField(default='#F90F90')
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, default=get_current_user)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f'{self.value} - {self.name}'
def __repr__(self):
return f'{self.value}'
class Label(models.Model):
name = models.CharField(max_length=25, unique=True, db_index=True)
description = models.TextField(null=True, blank=True)
color = ColorField(default='#666666')
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, default=get_current_user)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
def __repr__(self):
return self.name
class Question(models.Model):
name = models.CharField(max_length=500, unique=True, db_index=True)
question = models.TextField(db_index=True)
buzzword = models.CharField(max_length=25, null=True, blank=True)
awnser = models.TextField(db_index=True)
level = models.ForeignKey(Level, on_delete=models.SET_NULL, null=True)
description = models.TextField(null=True, blank=True)
labels = models.ManyToManyField(Label, blank=True)
medias = models.ManyToManyField(MediaFile, blank=True)
links = models.ManyToManyField(Link, blank=True)
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
def __repr__(self):
return self.name
def to_view(self, for_players: bool = False):
ret = {}
ret['name'] = '{}{}'.format(self.name, f'({self.buzzword})' if self.buzzword else '')
ret['question'] = self.question
if self.description:
ret['question'] += ' \n\n------------------------------\n\n' + self.description
ret['awnser'] = self.awnser
ret['level'] = self.level.value
ret['medias'] = []
ret['links'] = []
for media in self.medias.all():
ret.setdefault('medias', []).append(os.path.basename(media.file.path))
for link in self.links.all():
ret.setdefault('links', []).append(link.url)
ret['question'] = mark_safe(ret['question'].replace('\n', '\n<br />'))
logging.error(ret)
return ret
def default_json(o):
if isinstance(o, (MediaFile,)):
return str(o)
return json.dumps(model_to_dict(o))
class QuestionVersion(models.Model):
question = models.ForeignKey(Question, on_delete=models.SET_NULL, null=True)
data = models.JSONField()
author = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f'{self.question} - {self.created_at}' if self.question else f'{self.data.name} - {self.created_at}'
@receiver(models.signals.post_save, sender=Question)
def versionize_question(sender, instance: Question, *args, **kwargs):
data = model_to_dict(instance)
# to be able to json.dumps in model
data['medias'] = [{'id': m.id, '__str__': str(m)} for m in data['medias']]
data['links'] = [{'id': m.id, '__str__': str(m)} for m in data['links']]
data['labels'] = [{'id': m.id, '__str__': str(m)} for m in data['labels']]
QuestionVersion.objects.create(
question=instance,
data=data,
# https://django-crum.readthedocs.io/en/latest/
# from crum import get_current_user
# author=get_current_user(),
)

3
content/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

3
content/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.