Add api-serializer

This commit is contained in:
Paul Konstantin Gerke 2022-02-15 22:24:13 +01:00
parent 56ebbd3181
commit 5046b754e5
6 changed files with 77 additions and 33 deletions

View File

@ -202,3 +202,13 @@ class BMISerializer(CoreModelSerializer):
class Meta:
model = models.BMI
fields = ("id", "child", "bmi", "date", "notes")
class TagsSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.BabyBuddyTag
fields = ("slug", "name", "color", "last_used")
extra_kwargs = {
"slug": {"required": False, "read_only": True},
"color": {"required": False},
"last_used": {"required": False, "read_only": True},
}

View File

@ -18,6 +18,7 @@ router.register(r"weight", views.WeightViewSet)
router.register(r"height", views.HeightViewSet)
router.register(r"head-circumference", views.HeadCircumferenceViewSet)
router.register(r"bmi", views.BMIViewSet)
router.register(r"tags", views.TagsViewSet)
app_name = "api"

View File

@ -91,3 +91,10 @@ class BMIViewSet(viewsets.ModelViewSet):
queryset = models.BMI.objects.all()
serializer_class = serializers.BMISerializer
filterset_fields = ("child", "date")
class TagsViewSet(viewsets.ModelViewSet):
queryset = models.BabyBuddyTag.objects.all()
serializer_class = serializers.TagsSerializer
lookup_field = "slug"
filterset_fields = ("last_used",)

View File

@ -1,6 +1,7 @@
# Generated by Django 4.0.2 on 2022-02-15 14:43
# Generated by Django 4.0.2 on 2022-02-15 16:33
import core.models
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
@ -21,7 +22,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True, verbose_name='name')),
('slug', models.SlugField(max_length=100, unique=True, verbose_name='slug')),
('color', models.CharField(default='#7F7F7F', max_length=32, validators=[core.models.validate_html_color], verbose_name='Color')),
('color', models.CharField(default=core.models.random_color, max_length=32, validators=[django.core.validators.RegexValidator('^#[0-9A-F]{6}$')], verbose_name='Color')),
('last_used', models.DateTimeField(default=django.utils.timezone.now)),
],
options={

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
import re
import time
from datetime import timedelta
from django.conf import settings
@ -11,10 +12,15 @@ from django.utils import timezone
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _
from django.utils.timezone import now
from django.core.validators import RegexValidator
import random
from taggit.managers import TaggableManager
from taggit.models import TagBase, GenericTaggedItemBase, TaggedItemBase
random.seed(time.time())
def validate_date(date, field_name):
"""
Confirm that a date is not in the future.
@ -74,8 +80,14 @@ def validate_time(time, field_name):
{field_name: _("Date/time can not be in the future.")}, code="time_invalid"
)
def validate_html_color(s: str):
return re.match(r"^#[0-9A-F]{6}$", s) is not None
TAG_COLORS = [
"#FF0000", "#00FF00", "#0000FF", "#FF00FF", "#FFFF00", "#00FFFF",
"#FF7F7F", "#7FFF7F", "#7F7FFF", "#FF7FFF", "#FFFF7F", "#7FFFFF",
"#7F0000", "#007F00", "#00007F", "#7F007F", "#7F7F00", "#007F7F",
]
def random_color():
return TAG_COLORS[random.randrange(0, len(TAG_COLORS))]
class BabyBuddyTag(TagBase):
class Meta:
@ -85,8 +97,8 @@ class BabyBuddyTag(TagBase):
color = models.CharField(
"Color",
max_length=32,
default="#7F7F7F",
validators=[validate_html_color]
default=random_color,
validators=[RegexValidator(r"^#[0-9A-F]{6}$")]
)
last_used = models.DateTimeField(

View File

@ -22,13 +22,17 @@
<span>Suggestions:</span>
{% for t in widget.tag_suggestions.quick %}
<span data-value="{{ t.name }}" data-color="{{ t.color }}" class="tag btn badge badge-pill cursor-pointer" style="background-color: {{ t.color }};">
{% if not forloop.first %},{% endif %}
{{ t.name }}
<span class="add-remove-icon pl-1 pr-1">+</span>
</span>
{% endfor %}
</div>
<script>
<input
type="hidden"
name="tags"
value="{% for t in widget.value %}&quot;{{ t.name }}&quot;{% if not forloop.last %},{% endif %}{% endfor %}"
>
<script>
window.addEventListener('load', () => {
const widget = document.getElementById('{{ widget.attrs.id }}');
@ -38,12 +42,39 @@
const inputElement = widget.querySelector('input');
function hexParse(x) {
return parseInt(x, 16);
}
function computeComplementaryColor(colorStr) {
let avgColor = 0.0;
avgColor += hexParse(colorStr.substring(1, 3)) * -0.5;
avgColor += hexParse(colorStr.substring(3, 5)) * 1.5;
avgColor += hexParse(colorStr.substring(5, 7)) * 1.0;
console.log(`C ${colorStr} -> ${avgColor}`);
if (avgColor > 200) {
return "#101010";
} else {
return "#E0E0E0";
}
}
function updateTag(tag, name, color, actionSymbol) {
const actionTextNode = tag.querySelector('.add-remove-icon').childNodes[0];
name = name || tag.getAttribute("data-value");
color = color || tag.getAttribute("data-color");
actionSymbol = actionSymbol || actionTextNode.textContent;
tag.childNodes[0].textContent = name;
tag.setAttribute("data-value", name);
tag.setAttribute("data-color", color);
tag.setAttribute('style', `background-color: ${color};`);
tag.querySelector('.add-remove-icon').childNodes[0].textContent = actionSymbol;
const textColor = computeComplementaryColor(color);
tag.setAttribute('style', `background-color: ${color}; color: ${textColor};`);
actionTextNode.textContent = actionSymbol;
}
function createNewTag(name, color, actionSymbol) {
@ -52,28 +83,12 @@
return tag;
}
function addTagCallback(event) {
const tag = event.target;
newTags.removeChild(tag);
updateTag(
tag,
tag.getAttribute("data-value"),
tag.getAttribute("data-color"),
"-"
)
currentTags.appendChild(tag);
tag.removeEventListener('click', addTagCallback);
tag.addEventListener('click', removeTagCallback);
}
function registerNewCallback(tag, newParent, newSymbol, onClicked) {
console.log(tag);
function callback(event) {
tag.parentNode.removeChild(tag);
updateTag(
tag,
tag.getAttribute("data-value"),
null,
tag.getAttribute("data-color"),
newSymbol
);
@ -88,7 +103,8 @@
function updateInputList() {
const names = [];
for (const tag of currentTags.querySelectorAll(".tag")) {
names.push(tag.getAttribute("data-value"));
const name = tag.getAttribute("data-value");
names.push(`"${name}"`);
}
inputElement.value = names.join(",");
}
@ -104,16 +120,13 @@
}
for (const tag of newTags.querySelectorAll(".tag")) {
updateTag(tag);
addTagCallback(tag);
}
for (const tag of currentTags.querySelectorAll(".tag")) {
updateTag(tag);
removeTagCallback(tag);
}
});
</script>
<input
type="hidden"
name="tags"
value="{% for t in widget.value %}{{ t.name }}{% endfor %}"
>
</div>