aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorluxagraf <sng@luxagraf.net>2018-12-29 08:37:39 -0600
committerluxagraf <sng@luxagraf.net>2018-12-29 08:37:39 -0600
commit4f7b84194b056b5d6d9acca4cceb2cabc04fd8a5 (patch)
tree7fe109e7aeaddab7aa5e7f46f99414064a248e52 /apps
parent02f520038e3c6d5a01c9545e9b1c3eb91e4e016c (diff)
cleaned up JS and made modal handler.
Diffstat (limited to 'apps')
-rw-r--r--apps/notes/admin.py7
-rw-r--r--apps/notes/forms.py31
-rw-r--r--apps/notes/migrations/0002_auto_20181204_0620.py42
-rw-r--r--apps/notes/migrations/0003_auto_20181204_0641.py59
-rw-r--r--apps/notes/migrations/0004_auto_20181204_0653.py19
-rw-r--r--apps/notes/migrations/0005_luxtag_owner.py22
-rw-r--r--apps/notes/migrations/0006_auto_20181204_0957.py22
-rw-r--r--apps/notes/migrations/0007_auto_20181204_1050.py22
-rw-r--r--apps/notes/migrations/0008_auto_20181204_1311.py19
-rw-r--r--apps/notes/migrations/0009_remove_luxtag_owner.py17
-rw-r--r--apps/notes/migrations/0010_auto_20181204_2117.py22
-rw-r--r--apps/notes/migrations/0011_auto_20181221_1029.py39
-rw-r--r--apps/notes/migrations/0012_auto_20181221_1038.py22
-rw-r--r--apps/notes/migrations/0013_remove_luxtag_owner.py17
-rw-r--r--apps/notes/models.py29
-rw-r--r--apps/notes/notes_urls.py2
-rw-r--r--apps/notes/serializers.py4
-rw-r--r--apps/notes/tests/test_models.py2
-rw-r--r--apps/notes/views.py154
-rw-r--r--apps/utils/views.py27
-rw-r--r--apps/utils/widgets.py33
21 files changed, 545 insertions, 66 deletions
diff --git a/apps/notes/admin.py b/apps/notes/admin.py
index 3958d55..44915f2 100644
--- a/apps/notes/admin.py
+++ b/apps/notes/admin.py
@@ -1,6 +1,6 @@
from django.contrib import admin
-from .models import Note, Notebook
+from .models import Note, Notebook, LuxTag
@admin.register(Note)
@@ -8,6 +8,11 @@ class NoteAdmin(admin.ModelAdmin):
pass
+@admin.register(LuxTag)
+class TagAdmin(admin.ModelAdmin):
+ pass
+
+
@admin.register(Notebook)
class NotebookAdmin(admin.ModelAdmin):
pass
diff --git a/apps/notes/forms.py b/apps/notes/forms.py
index 5ef9f84..4dc79d2 100644
--- a/apps/notes/forms.py
+++ b/apps/notes/forms.py
@@ -1,10 +1,19 @@
from django import forms
from django.utils.translation import ugettext_lazy as _
+from django.core.exceptions import NON_FIELD_ERRORS
+
+from utils.widgets import RelatedFieldWidgetCanAdd
from .models import Note, Notebook
-class NoteForm(forms.ModelForm):
+class BaseNoteForm(forms.ModelForm):
+ def __init__(self, *args, **kwargs):
+ self.user = kwargs.pop("user", None)
+ super(BaseNoteForm, self).__init__(*args, **kwargs)
+
+
+class NoteForm(BaseNoteForm):
class Meta:
model = Note
fields = ['title', 'body_text', 'body_html', 'body_qjson', 'notebook', 'url', 'tags']
@@ -13,14 +22,28 @@ class NoteForm(forms.ModelForm):
}
def __init__(self, *args, **kwargs):
- self.user = kwargs.pop("user", None)
+ user = kwargs.pop("user", None)
super(NoteForm, self).__init__(*args, **kwargs)
+ self.fields['notebook'].widget = RelatedFieldWidgetCanAdd(Notebook, related_url="notebooks:list")
+ self.fields['notebook'].queryset = Notebook.objects.filter(owner__username=user)
-class NotebookForm(NoteForm):
+class NotebookForm(BaseNoteForm):
class Meta:
model = Notebook
- fields = ['name', 'color_rgb']
+ fields = ['owner', 'name', 'color_rgb']
+ widgets = {'owner': forms.HiddenInput()}
labels = {
"name": _("Notebook Name"),
+ "color_rgb": _("Notebook Color"),
+ }
+ error_messages = {
+ NON_FIELD_ERRORS: {
+ 'unique_together': "You already have a notebook by that name, please choose a different name",
+ }
}
+
+ def __init__(self, *args, **kwargs):
+ user = kwargs.pop("user", None)
+ super(NotebookForm, self).__init__(*args, **kwargs)
+ self.fields['owner'].initial = user
diff --git a/apps/notes/migrations/0002_auto_20181204_0620.py b/apps/notes/migrations/0002_auto_20181204_0620.py
new file mode 100644
index 0000000..e7cb38d
--- /dev/null
+++ b/apps/notes/migrations/0002_auto_20181204_0620.py
@@ -0,0 +1,42 @@
+# Generated by Django 2.1.2 on 2018-12-04 12: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),
+ ('notes', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Tag',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=40)),
+ ('slug', models.SlugField(blank=True)),
+ ('color_hex', models.CharField(max_length=6)),
+ ('date_created', models.DateTimeField(auto_now_add=True)),
+ ('date_updated', models.DateTimeField(auto_now=True)),
+ ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='parent_tag', to='notes.Tag')),
+ ],
+ ),
+ migrations.AlterModelOptions(
+ name='note',
+ options={'ordering': ('-date_created', '-date_updated')},
+ ),
+ migrations.AddField(
+ model_name='note',
+ name='tagstwo',
+ field=models.ManyToManyField(blank=True, to='notes.Tag'),
+ ),
+ migrations.AlterUniqueTogether(
+ name='tag',
+ unique_together={('owner', 'name')},
+ ),
+ ]
diff --git a/apps/notes/migrations/0003_auto_20181204_0641.py b/apps/notes/migrations/0003_auto_20181204_0641.py
new file mode 100644
index 0000000..9423058
--- /dev/null
+++ b/apps/notes/migrations/0003_auto_20181204_0641.py
@@ -0,0 +1,59 @@
+# Generated by Django 2.1.2 on 2018-12-04 12:41
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('contenttypes', '0002_remove_content_type_name'),
+ ('notes', '0002_auto_20181204_0620'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='LuxTag',
+ fields=[
+ ('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_hex', models.CharField(max_length=6)),
+ ],
+ options={
+ 'verbose_name': 'Tag',
+ 'verbose_name_plural': 'Tags',
+ },
+ ),
+ migrations.CreateModel(
+ name='TaggedNotes',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('object_id', models.IntegerField(db_index=True, verbose_name='Object id')),
+ ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes_taggednotes_tagged_items', to='contenttypes.ContentType', verbose_name='Content type')),
+ ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notes_taggednotes_items', to='notes.LuxTag')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.AlterUniqueTogether(
+ name='tag',
+ unique_together=set(),
+ ),
+ migrations.RemoveField(
+ model_name='tag',
+ name='owner',
+ ),
+ migrations.RemoveField(
+ model_name='tag',
+ name='parent',
+ ),
+ migrations.RemoveField(
+ model_name='note',
+ name='tagstwo',
+ ),
+ migrations.DeleteModel(
+ name='Tag',
+ ),
+ ]
diff --git a/apps/notes/migrations/0004_auto_20181204_0653.py b/apps/notes/migrations/0004_auto_20181204_0653.py
new file mode 100644
index 0000000..fc6d911
--- /dev/null
+++ b/apps/notes/migrations/0004_auto_20181204_0653.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.1.2 on 2018-12-04 12:53
+
+from django.db import migrations
+import taggit.managers
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('notes', '0003_auto_20181204_0641'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='note',
+ name='tags',
+ field=taggit.managers.TaggableManager(blank=True, help_text='Tags', through='notes.TaggedNotes', to='notes.LuxTag', verbose_name='Tags'),
+ ),
+ ]
diff --git a/apps/notes/migrations/0005_luxtag_owner.py b/apps/notes/migrations/0005_luxtag_owner.py
new file mode 100644
index 0000000..168bd0b
--- /dev/null
+++ b/apps/notes/migrations/0005_luxtag_owner.py
@@ -0,0 +1,22 @@
+# Generated by Django 2.1.2 on 2018-12-04 13:06
+
+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),
+ ('notes', '0004_auto_20181204_0653'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='luxtag',
+ name='owner',
+ field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ preserve_default=False,
+ ),
+ ]
diff --git a/apps/notes/migrations/0006_auto_20181204_0957.py b/apps/notes/migrations/0006_auto_20181204_0957.py
new file mode 100644
index 0000000..bf4293c
--- /dev/null
+++ b/apps/notes/migrations/0006_auto_20181204_0957.py
@@ -0,0 +1,22 @@
+# Generated by Django 2.1.2 on 2018-12-04 15:57
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('notes', '0005_luxtag_owner'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='notebook',
+ name='url',
+ ),
+ migrations.AddField(
+ model_name='notebook',
+ name='color_hex',
+ field=models.CharField(blank=True, max_length=6, null=True),
+ ),
+ ]
diff --git a/apps/notes/migrations/0007_auto_20181204_1050.py b/apps/notes/migrations/0007_auto_20181204_1050.py
new file mode 100644
index 0000000..a4bdc30
--- /dev/null
+++ b/apps/notes/migrations/0007_auto_20181204_1050.py
@@ -0,0 +1,22 @@
+# Generated by Django 2.1.2 on 2018-12-04 16:50
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('notes', '0006_auto_20181204_0957'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='notebook',
+ name='color_hex',
+ ),
+ migrations.AddField(
+ model_name='notebook',
+ name='color_rgb',
+ field=models.CharField(blank=True, max_length=20, null=True),
+ ),
+ ]
diff --git a/apps/notes/migrations/0008_auto_20181204_1311.py b/apps/notes/migrations/0008_auto_20181204_1311.py
new file mode 100644
index 0000000..02bf272
--- /dev/null
+++ b/apps/notes/migrations/0008_auto_20181204_1311.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.1.2 on 2018-12-04 19:11
+
+from django.conf import settings
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('notes', '0007_auto_20181204_1050'),
+ ]
+
+ operations = [
+ migrations.AlterUniqueTogether(
+ name='notebook',
+ unique_together={('owner', 'name')},
+ ),
+ ]
diff --git a/apps/notes/migrations/0009_remove_luxtag_owner.py b/apps/notes/migrations/0009_remove_luxtag_owner.py
new file mode 100644
index 0000000..18896f3
--- /dev/null
+++ b/apps/notes/migrations/0009_remove_luxtag_owner.py
@@ -0,0 +1,17 @@
+# Generated by Django 2.1.2 on 2018-12-05 03:15
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('notes', '0008_auto_20181204_1311'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='luxtag',
+ name='owner',
+ ),
+ ]
diff --git a/apps/notes/migrations/0010_auto_20181204_2117.py b/apps/notes/migrations/0010_auto_20181204_2117.py
new file mode 100644
index 0000000..69da825
--- /dev/null
+++ b/apps/notes/migrations/0010_auto_20181204_2117.py
@@ -0,0 +1,22 @@
+# Generated by Django 2.1.2 on 2018-12-05 03:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('notes', '0009_remove_luxtag_owner'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='luxtag',
+ name='color_hex',
+ ),
+ migrations.AddField(
+ model_name='luxtag',
+ name='color_rgb',
+ field=models.CharField(blank=True, max_length=20, null=True),
+ ),
+ ]
diff --git a/apps/notes/migrations/0011_auto_20181221_1029.py b/apps/notes/migrations/0011_auto_20181221_1029.py
new file mode 100644
index 0000000..7b88a62
--- /dev/null
+++ b/apps/notes/migrations/0011_auto_20181221_1029.py
@@ -0,0 +1,39 @@
+# Generated by Django 2.1.2 on 2018-12-21 16:29
+
+from django.conf import settings
+import django.contrib.postgres.fields.jsonb
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('notes', '0010_auto_20181204_2117'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Annotation',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('unique_id', models.UUIDField(default=uuid.uuid4, editable=False)),
+ ('date_created', models.DateTimeField(auto_now_add=True)),
+ ('date_updated', models.DateTimeField(auto_now=True)),
+ ('highlight_text', models.TextField(null=True)),
+ ('body_text', models.TextField(null=True)),
+ ('body_html', models.TextField(blank=True, null=True)),
+ ('body_qjson', django.contrib.postgres.fields.jsonb.JSONField(blank=True, null=True)),
+ ('is_public', models.BooleanField(default=False)),
+ ('note', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='notes.Note')),
+ ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.AddField(
+ model_name='luxtag',
+ name='owner',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ ),
+ ]
diff --git a/apps/notes/migrations/0012_auto_20181221_1038.py b/apps/notes/migrations/0012_auto_20181221_1038.py
new file mode 100644
index 0000000..f02cbeb
--- /dev/null
+++ b/apps/notes/migrations/0012_auto_20181221_1038.py
@@ -0,0 +1,22 @@
+# Generated by Django 2.1.2 on 2018-12-21 16:38
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+from django.contrib import auth
+User = auth.get_user_model()
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('notes', '0011_auto_20181221_1029'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='luxtag',
+ name='owner',
+ field=models.ForeignKey(default=User.objects.get(username='luxagraf').id, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
+ preserve_default=False,
+ ),
+ ]
diff --git a/apps/notes/migrations/0013_remove_luxtag_owner.py b/apps/notes/migrations/0013_remove_luxtag_owner.py
new file mode 100644
index 0000000..a96b105
--- /dev/null
+++ b/apps/notes/migrations/0013_remove_luxtag_owner.py
@@ -0,0 +1,17 @@
+# Generated by Django 2.1.2 on 2018-12-21 17:20
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('notes', '0012_auto_20181221_1038'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='luxtag',
+ name='owner',
+ ),
+ ]
diff --git a/apps/notes/models.py b/apps/notes/models.py
index 95c1c96..9dc4f13 100644
--- a/apps/notes/models.py
+++ b/apps/notes/models.py
@@ -25,6 +25,10 @@ class LuxTag(TagBase):
verbose_name = _("Tag")
verbose_name_plural = _("Tags")
+ @cached_property
+ def get_absolute_url(self):
+ return reverse("notes:tags", kwargs={"slug": self.slug})
+
class TaggedNotes(GenericTaggedItemBase):
tag = models.ForeignKey(LuxTag, related_name="%(app_label)s_%(class)s_items", on_delete=models.CASCADE)
@@ -55,6 +59,15 @@ class Notebook(models.Model):
def get_absolute_url(self):
return reverse("notebooks:detail", kwargs={"slug": self.slug})
+ @cached_property
+ def color_rgba(self, opacity=".5"):
+ try:
+ color = self.color_rgb.split('(')[1].split(')')[0]
+ rgba = "rgba(%s,%s)" % (color, opacity)
+ except AttributeError:
+ rgba = self.color_rgb
+ return rgba
+
class Note(models.Model):
unique_id = models.UUIDField(default=uuid.uuid4, editable=False)
@@ -88,3 +101,19 @@ class Note(models.Model):
if self._state.adding:
self.slug = unique_slug_generator(self)
super(Note, self).save()
+
+
+class Annotation(models.Model):
+ unique_id = models.UUIDField(default=uuid.uuid4, editable=False)
+ owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
+ date_created = models.DateTimeField(blank=True, auto_now_add=True, editable=False)
+ date_updated = models.DateTimeField(blank=True, auto_now=True, editable=False)
+ highlight_text = models.TextField(null=True)
+ body_text = models.TextField(null=True)
+ body_html = models.TextField(null=True, blank=True)
+ body_qjson = JSONField(null=True, blank=True)
+ note = models.ForeignKey(Note, null=True, blank=True, on_delete=models.SET_NULL)
+ is_public = models.BooleanField(default=False)
+
+ def __str__(self):
+ return self.body_text[:30]
diff --git a/apps/notes/notes_urls.py b/apps/notes/notes_urls.py
index f2573ce..55fb32b 100644
--- a/apps/notes/notes_urls.py
+++ b/apps/notes/notes_urls.py
@@ -11,7 +11,7 @@ app_name = "notes"
urlpatterns = [
path(r'create/', NoteCreateView.as_view(), name='create',),
- path(r'<slug>/<pk>', NoteDetailView.as_view(), name='detail',),
path(r't/<slug>', NoteTagView.as_view(), name='tags',),
+ path(r'<slug>/<pk>', NoteDetailView.as_view(), name='detail',),
path(r'', NoteListView.as_view(), name='list',),
]
diff --git a/apps/notes/serializers.py b/apps/notes/serializers.py
index f811edd..6bb08de 100644
--- a/apps/notes/serializers.py
+++ b/apps/notes/serializers.py
@@ -27,7 +27,7 @@ class NoteSerializer(TaggitSerializer, serializers.ModelSerializer):
class Meta:
model = Note
- fields = ('title', 'body_text', 'body_qjson', 'body_html', 'url', 'notebook', 'tags')
+ fields = ('id', 'title', 'body_text', 'body_qjson', 'body_html', 'url', 'notebook', 'tags')
class NotebookSerializer(serializers.HyperlinkedModelSerializer):
@@ -38,7 +38,7 @@ class NotebookSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Notebook
- fields = ('name', 'color_rgb', 'json_absolute_url', 'owner')
+ fields = ('id', 'name', 'color_rgb', 'json_absolute_url', 'owner')
class NoteTagSerializer(serializers.HyperlinkedModelSerializer):
diff --git a/apps/notes/tests/test_models.py b/apps/notes/tests/test_models.py
index 05f2618..0c53a25 100644
--- a/apps/notes/tests/test_models.py
+++ b/apps/notes/tests/test_models.py
@@ -3,7 +3,7 @@ from django.urls import reverse
from django.contrib import auth
from mixer.backend.django import mixer
-from notes.models import Note, Notebook
+from notes.models import Note, Notebook
User = auth.get_user_model()
diff --git a/apps/notes/views.py b/apps/notes/views.py
index 5d55720..6751340 100644
--- a/apps/notes/views.py
+++ b/apps/notes/views.py
@@ -1,6 +1,11 @@
from django.views.generic import CreateView, ListView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
-from django.views.generic.base import View, RedirectView
+from django.views.generic.edit import FormView, ModelFormMixin
+from django.http import JsonResponse
+from django.core import serializers
+from django.forms import modelformset_factory
+from django.db.models import Count
+from django.views.generic.base import RedirectView
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.shortcuts import get_object_or_404, render, redirect
@@ -14,10 +19,25 @@ from rest_framework import permissions
from .serializers import NoteSerializer, NotebookSerializer, NoteTagSerializer
from .models import Note, Notebook, LuxTag
from .forms import NoteForm, NotebookForm
+from utils.views import AjaxableResponseMixin
+
+##################
+# Base Views
+##################
+
+
+@method_decorator(login_required, name='dispatch')
+class BaseListView(ListView):
+ pass
+
+
+@method_decorator(login_required, name='dispatch')
+class BaseDetailView(DetailView):
+ pass
@method_decorator(login_required, name='dispatch')
-class LoggedInViewWithUser(View):
+class LoggedInViewWithUser(FormView):
def get_form_kwargs(self, **kwargs):
kwargs = super().get_form_kwargs(**kwargs)
@@ -25,12 +45,23 @@ class LoggedInViewWithUser(View):
return kwargs
-class NoteListView(LoggedInViewWithUser, ListView):
+##################
+# Note Views
+##################
+
+
+class NoteListView(BaseListView):
model = Note
def get_queryset(self):
if not self.request.user.is_anonymous:
- return Note.objects.filter(owner=self.request.user)
+ return Note.objects.prefetch_related('tags').filter(owner=self.request.user).select_related('notebook')
+
+ def get_context_data(self, **kwargs):
+ context = super(NoteListView, self).get_context_data(**kwargs)
+ context['notebook_list'] = Notebook.objects.filter(owner=self.request.user).exclude(name="Trash").annotate(note_count=Count('note'))
+ context['tag_list'] = LuxTag.objects.filter(note__owner=self.request.user).annotate(note_count=Count('note'))
+ return context
def get_template_names(self):
# print("IP Address for debug-toolbar: " + self.request.META['REMOTE_ADDR'])
@@ -40,36 +71,10 @@ class NoteListView(LoggedInViewWithUser, ListView):
return ['sell.html']
-class NoteTagView(LoggedInViewWithUser, ListView):
- model = Note
- template_name = 'notes/notes_list.html'
-
- def get_queryset(self):
- '''
- This can generate a crazy amount of joins if there's a lot of tags
- have to keep an eye on it. Would be better to do:
- from django.db.models import Q
- from functools import reduce
- from operator import and_, or_
- #query = reduce(and_, (Q(tags__slug=t) for t in self.tag_list))
- # Note.objects.filter(query, owner=self.request.user)
- But that doesn't work for some reason.
- '''
- if not self.request.user.is_anonymous:
- self.tag_list = [x.strip() for x in self.kwargs['slug'].split("+")]
- qs = Note.objects.filter(owner=self.request.user)
- for tag in self.tag_list:
- qs = qs.filter(tags__slug=tag)
- return qs
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context['notes_list'] = Note.objects.filter(owner=self.request.user)
- context['tags'] = self.tag_list
- return context
-
-
-class NoteDetailView(UpdateView, LoggedInViewWithUser):
+class NoteDetailView(LoggedInViewWithUser, AjaxableResponseMixin, UpdateView):
+ '''
+ POST only works as AJAX
+ '''
model = Note
form_class = NoteForm
template_name = 'notes/notes_detail.html'
@@ -80,11 +85,20 @@ class NoteDetailView(UpdateView, LoggedInViewWithUser):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- context['notes_list'] = Note.objects.filter(owner=self.request.user)
+ context['notebook_form'] = NotebookForm
return context
+ def form_valid(self, form):
+ self.object = form.save()
+ tags = serializers.serialize("json", self.object.tags.all())
+ data = {
+ 'tags': tags,
+ 'notebook': {'name': self.object.notebook.name, 'color': self.object.notebook.color_rgb}
+ }
+ return JsonResponse(data, safe=True)
+
-class NoteCreateView(CreateView, LoggedInViewWithUser):
+class NoteCreateView(LoggedInViewWithUser, CreateView):
model = Note
form_class = NoteForm
template_name = 'notes/notes_create.html'
@@ -99,27 +113,76 @@ class NoteCreateView(CreateView, LoggedInViewWithUser):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- context['notes_list'] = Note.objects.filter(owner=self.request.user).select_related()
+ context['notebook_form'] = NotebookForm
+ # context['notes_list'] = Note.objects.filter(owner=self.request.user).select_related()
+ return context
+
+
+class NoteTagView(BaseListView):
+ model = Note
+ template_name = 'notes/notes_list.html'
+
+ def get_queryset(self):
+ '''
+ This can generate a crazy amount of joins if there's a lot of tags
+ have to keep an eye on it. Would be better to do:
+ from django.db.models import Q
+ from functools import reduce
+ from operator import and_, or_
+ #query = reduce(and_, (Q(tags__slug=t) for t in self.tag_list))
+ # Note.objects.filter(query, owner=self.request.user)
+ But that doesn't work for some reason.
+ '''
+ if not self.request.user.is_anonymous:
+ try:
+ tags = self.kwargs['slug'].split("+")
+ except ValueError:
+ tags = self.kwargs['slug']
+ self.tag_list = [x.strip() for x in tags]
+ qs = Note.objects.prefetch_related('tags').filter(owner=self.request.user).select_related('notebook')
+ for tag in self.tag_list:
+ qs = qs.filter(tags__slug=tag)
+ return qs
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ #context['notes_list'] = Note.objects.filter(owner=self.request.user)
+ context['tags'] = LuxTag.objects.filter(slug__in=self.tag_list)
return context
-class NotebookListView(CreateView, LoggedInViewWithUser):
+##################
+# Notebook Views
+##################
+
+
+class NotebookListView(LoggedInViewWithUser, CreateView):
model = Notebook
form_class = NotebookForm
- template_name = 'notes/notebook_create.html'
+ template_name = 'notes/notebook_list.html'
def get_queryset(self):
if not self.request.user.is_anonymous:
return Notebook.objects.filter(owner=self.request.user)
+ def form_valid(self, form):
+ form.instance.owner = self.request.user
+ self.object = form.save()
+ return super(NotebookListView, self).form_valid(form)
+
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- context['notebook_list'] = Notebook.objects.filter(owner=self.request.user)
- context['notes_list'] = Note.objects.filter(owner=self.request.user).select_related()
+ NotebookFormSet = modelformset_factory(Notebook, form=NotebookForm, extra=0)
+ context['notebook_form_list'] = NotebookFormSet(queryset=Notebook.objects.filter(owner=self.request.user).exclude(name="Trash").select_related().annotate(note_count=Count('note')))
+ #context['notebook_list'] = Notebook.objects.filter(owner=self.request.user).exclude(name="Trash").select_related().annotate(note_count=Count('note'))
+ #context['notes_list'] = Note.objects.filter(owner=self.request.user).select_related()
return context
+ def get_success_url(self):
+ return reverse_lazy('notebooks:detail', kwargs={'slug': self.object.slug})
+
-class NotebookDetailView(DetailView, LoggedInViewWithUser):
+class NotebookDetailView(BaseDetailView):
model = Notebook
def get_object(self):
@@ -129,11 +192,16 @@ class NotebookDetailView(DetailView, LoggedInViewWithUser):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- context['notes_list'] = Note.objects.filter(owner=self.request.user).select_related()
+ #context['notes_list'] = Note.objects.filter(owner=self.request.user).select_related()
context['form'] = self.form
return context
+##################
+# API Views
+##################
+
+
class IsOwnerOrDeny(permissions.BasePermission):
"""
Custom permission to only allow owners to post to their endpoint
diff --git a/apps/utils/views.py b/apps/utils/views.py
index 595e102..ec3a902 100644
--- a/apps/utils/views.py
+++ b/apps/utils/views.py
@@ -1,6 +1,6 @@
from itertools import chain
import json
-from django.http import Http404, HttpResponse
+from django.http import Http404, HttpResponse, JsonResponse
from django.apps import apps
from django.views.generic import ListView
from django.views.generic.base import View, RedirectView
@@ -42,6 +42,31 @@ class LoggedInViewWithUser(View):
return kwargs
+
+class AjaxableResponseMixin:
+ """
+ Mixin to add AJAX support to a form.
+ Must be used with an object-based FormView (e.g. CreateView)
+ """
+ def form_invalid(self, form):
+ response = super().form_invalid(form)
+ if self.request.is_ajax():
+ return JsonResponse(form.errors, status=400)
+ else:
+ return response
+
+ def form_valid(self, form):
+ # We make sure to call the parent's form_valid() method because
+ # it might do some processing (in the case of CreateView, it will
+ # call form.save() for example).
+ response = super().form_valid(form)
+ if self.request.is_ajax():
+ data = {
+ 'pk': self.object.pk,
+ }
+ return JsonResponse(data)
+ else:
+ return response
'''
class TagAutocomplete(autocomplete.Select2QuerySetView):
def get_queryset(self):
diff --git a/apps/utils/widgets.py b/apps/utils/widgets.py
index f4a7a4a..2745932 100644
--- a/apps/utils/widgets.py
+++ b/apps/utils/widgets.py
@@ -2,8 +2,9 @@ import os
from django import forms
from django.contrib import admin
from django.contrib.admin.widgets import AdminFileWidget
-from django.contrib.gis.admin import OSMGeoAdmin
from django.utils.safestring import mark_safe
+from django.forms import widgets
+from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from django.template.loader import render_to_string
from django.template import Context
@@ -130,15 +131,21 @@ class LGEntryFormSmall(forms.ModelForm):
}
-class OLAdminBase(OSMGeoAdmin):
- default_lon = -9285175
- default_lat = 4025046
- default_zoom = 15
- units = True
- scrollable = False
- map_width = 700
- map_height = 425
- map_template = 'gis/admin/osm.html'
- openlayers_url = '/static/admin/js/OpenLayers.js'
-
-
+class RelatedFieldWidgetCanAdd(widgets.Select):
+ """
+ Modifies standard django Select widget to add link after to add new instance
+ of related model (doesn't check permissions, that's for the form instance)
+ """
+ def __init__(self, related_model, related_url=None, *args, **kw):
+ super(RelatedFieldWidgetCanAdd, self).__init__(*args, **kw)
+ if not related_url:
+ rel_to = related_model
+ info = (rel_to._meta.app_label, rel_to._meta.object_name.lower())
+ related_url = 'admin:%s_%s_add' % info
+ self.related_url = related_url
+
+ def render(self, name, value, *args, **kwargs):
+ self.related_url = reverse(self.related_url)
+ output = [super(RelatedFieldWidgetCanAdd, self).render(name, value, *args, **kwargs)]
+ output.append('<a class="circle plus small-circle modal-open" href="%s" id="add_id_%s" data-modal-hed-class="%s" data-modal-hed="Add a New %s">New</a>' % (self.related_url, name, name, name.capitalize()))
+ return mark_safe(u''.join(output))