mirror of https://github.com/snachodog/mybuddy.git
Add api-serializer
This commit is contained in:
parent
56ebbd3181
commit
5046b754e5
|
@ -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},
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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",)
|
||||
|
||||
|
|
|
@ -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={
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -22,12 +22,16 @@
|
|||
<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>
|
||||
<input
|
||||
type="hidden"
|
||||
name="tags"
|
||||
value="{% for t in widget.value %}"{{ t.name }}"{% 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>
|
||||
|
|
Loading…
Reference in New Issue