summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/notes/admin.py34
-rw-r--r--app/notes/forms.py15
-rw-r--r--app/notes/migrations/0001_initial.py30
-rw-r--r--app/notes/migrations/0002_luxnote_tags.py20
-rw-r--r--app/notes/migrations/__init__.py0
-rw-r--r--app/notes/models.py41
-rw-r--r--app/notes/templates/confirm_delete.html12
-rw-r--r--app/notes/templates/note_form.html39
-rw-r--r--app/notes/templates/note_list.html31
-rw-r--r--app/notes/templates/project_detail.html40
-rw-r--r--app/notes/templates/project_form.html35
-rw-r--r--app/notes/templates/project_list.html25
-rw-r--r--app/notes/urls.py34
-rw-r--r--app/notes/views.py72
-rw-r--r--config/base_urls.py1
-rw-r--r--templates/base_notes.html43
16 files changed, 472 insertions, 0 deletions
diff --git a/app/notes/admin.py b/app/notes/admin.py
new file mode 100644
index 0000000..5409200
--- /dev/null
+++ b/app/notes/admin.py
@@ -0,0 +1,34 @@
+from django.contrib import admin
+
+from utils.widgets import AdminImageWidget, LGEntryForm, TagListFilter
+
+from .models import (
+ LuxNote,
+)
+
+
+@admin.register(LuxNote)
+class LuxNoteAdmin(admin.ModelAdmin):
+ list_display = ('title', 'admin_link', 'date_created')
+ search_fields = ['title', 'description', 'url']
+ list_filter = [TagListFilter]
+ fieldsets = (
+ (None, {
+ 'fields': (
+ 'title',
+ 'url',
+ 'description',
+ 'body_markdown'
+ 'tags',
+ )
+ }),
+ ('Details', {
+ 'fields': (
+ 'date_created ',
+ ),
+ 'classes': 'collapse'
+ }),
+ )
+
+ class Media:
+ js = ('next-prev-links.js',)
diff --git a/app/notes/forms.py b/app/notes/forms.py
new file mode 100644
index 0000000..72395c6
--- /dev/null
+++ b/app/notes/forms.py
@@ -0,0 +1,15 @@
+from django.forms import ModelForm
+
+from .models import LuxNote
+
+
+class LuxNoteCreateForm(ModelForm):
+ class Meta:
+ model = LuxNote
+ fields = ['title', 'url', 'description', 'body_markdown', 'tags' ]
+
+
+class LuxNoteEditForm(ModelForm):
+ class Meta:
+ model = LuxNote
+ fields = ['title', 'url', 'description', 'body_markdown', 'tags' ]
diff --git a/app/notes/migrations/0001_initial.py b/app/notes/migrations/0001_initial.py
new file mode 100644
index 0000000..deb651d
--- /dev/null
+++ b/app/notes/migrations/0001_initial.py
@@ -0,0 +1,30 @@
+# Generated by Django 5.1.7 on 2025-04-07 16:21
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='LuxNote',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('title', models.CharField(max_length=200)),
+ ('url', models.CharField(blank=True, max_length=400, null=True)),
+ ('description', models.TextField(blank=True, null=True)),
+ ('body_markdown', models.TextField(blank=True, null=True)),
+ ('body_html', models.TextField(blank=True)),
+ ('date_created', models.DateTimeField(auto_now=True)),
+ ],
+ options={
+ 'ordering': ('-date_created',),
+ 'get_latest_by': 'date_created',
+ },
+ ),
+ ]
diff --git a/app/notes/migrations/0002_luxnote_tags.py b/app/notes/migrations/0002_luxnote_tags.py
new file mode 100644
index 0000000..6cb50be
--- /dev/null
+++ b/app/notes/migrations/0002_luxnote_tags.py
@@ -0,0 +1,20 @@
+# Generated by Django 5.1.7 on 2025-04-07 16:22
+
+import taggit.managers
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('notes', '0001_initial'),
+ ('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='luxnote',
+ name='tags',
+ field=taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'),
+ ),
+ ]
diff --git a/app/notes/migrations/__init__.py b/app/notes/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/app/notes/migrations/__init__.py
diff --git a/app/notes/models.py b/app/notes/models.py
new file mode 100644
index 0000000..c0c88e8
--- /dev/null
+++ b/app/notes/models.py
@@ -0,0 +1,41 @@
+import datetime
+
+from django.db import models
+from django.urls import reverse
+from django.utils import timezone
+from django.utils.html import format_html
+
+from taggit.managers import TaggableManager
+from utils.util import markdown_to_html
+
+
+#class LuxProject(models.Model):
+
+class LuxNote(models.Model):
+ title = models.CharField(max_length=200)
+ url = models.CharField(max_length=400, blank=True, null=True)
+ description = models.TextField(blank=True, null=True)
+ body_markdown = models.TextField(null=True, blank=True)
+ body_html = models.TextField(blank=True)
+ date_created = models.DateTimeField(auto_now=True)
+ #project = models.ForeignKey(LuxProject, on_delete=models.SET_NULL, null=True, blank=True)
+ tags = TaggableManager(blank=True)
+
+ class Meta:
+ ordering = ('-date_created',)
+ get_latest_by = 'date_created'
+
+ def __str__(self):
+ return self.title
+
+ def admin_link(self):
+ return format_html('<a href="%s">Visit Site</a>' % (self.url))
+ admin_link.short_description = 'Link'
+
+ def save(self, *args, **kwargs):
+ if self.body_markdown:
+ self.body_html = markdown_to_html(self.body_markdown)
+ super(LuxNote, self).save(*args, **kwargs)
+
+ def get_absolute_url(self, *args, **kwargs):
+ return reverse('notes:note-edit', kwargs={"pk": self.pk})
diff --git a/app/notes/templates/confirm_delete.html b/app/notes/templates/confirm_delete.html
new file mode 100644
index 0000000..8e657b1
--- /dev/null
+++ b/app/notes/templates/confirm_delete.html
@@ -0,0 +1,12 @@
+<form method="post">{% csrf_token %}
+
+
+
+
+<p>Are you sure you want to delete "{{ object }}"?</p>
+
+
+
+
+ <input type="submit" value="Confirm">
+</form>
diff --git a/app/notes/templates/note_form.html b/app/notes/templates/note_form.html
new file mode 100644
index 0000000..9c8ac37
--- /dev/null
+++ b/app/notes/templates/note_form.html
@@ -0,0 +1,39 @@
+{% extends 'base_notes.html' %}
+{% block extrahead %}
+<style>
+form .selector label {
+ position: inherit;
+}
+</style>
+<script src="/media/js/nice-select2.js"></script>
+<link rel="stylesheet" href="/media/nice-select2.css">
+{% endblock %}
+{% block primary %}
+<main role="main" class="archive-wrapper">
+ <div class="post-body">
+ <form action="" method="post" class="comment-form">{% csrf_token %}
+ {% for field in form %}
+ <fieldset>
+ {%if field.name == "project" or field.name == "status" or field.name == 'note_type'%}<span class="selector">{{field.label_tag}}</span>{%else%}{{field.label_tag}}{%endif%}
+ {%if field.name == "body_markdown" or field.name == "description" %}<div class="textarea-rounded">{{ field }}</div>{%else%}{{field}}{%endif%}
+ </fieldset>
+ <small class="alert">{% if field.errors %}{{field.errors}}{% endif %}</small>
+ {%endfor%}
+ <input class="btn" type="submit" name="add_new" value="Save and add another" />
+ <input type="submit" name="save" class="btn" value="Save" />
+ </form>
+ </div>
+</main>
+{% endblock %}
+ {% block js %}
+<script type="text/javascript">
+var options = {searchable: true};
+NiceSelect.bind(document.getElementById("id_project"), options);
+ {% if is_update %}{%else%}
+let params = new URL(document.location).searchParams;
+document.getElementById('id_title').value = params.get("title");
+document.getElementById('id_url').value = params.get("url");
+document.getElementById('id_body_markdown').value = params.get("description");
+{% endif %}
+</script>
+ {% endblock%}
diff --git a/app/notes/templates/note_list.html b/app/notes/templates/note_list.html
new file mode 100644
index 0000000..bd16cc0
--- /dev/null
+++ b/app/notes/templates/note_list.html
@@ -0,0 +1,31 @@
+{% extends 'base_notes.html' %}
+{% block primary %}
+<main class="post-detail">
+ <div class="post-header"><ul class="flex header-list">
+ <li><a class="btn" href="{% url 'notes:note-list' %}">All</a></li>
+ {% for object in note_types %}
+ <li><a class="btn" href="{% url 'gtd:note-list-status' object.label|lower%}">{{object.label}}</a></li>
+ {% endfor %}
+ <li class="right"><a href="{% url 'notes:note-create' %}" class="btn">New Note</a></li>
+ </ul>
+
+<select class="form-control" style="margin-top: 2%;" onchange="go_from_select(this.options[this.selectedIndex].value)">
+ <option value="">All Projects</option>{% for object in tags %}
+ <option {% if object.Tag == project %}selected="selected" {%endif%}value="?tag={{object}}">{{object}}</option>{%endfor%}
+</select>
+ </div>
+ <div class="note-list">{% for object in object_list %}<article>
+ <h2>{% if object.url %}<a href="{{object.url}}">{{object.title}}</a>{%else%}{{object.title}}{%endif%} <span class="note-edit"><a href="{%url 'gtd:note-edit' object.pk%}">edit</a></span></h2>
+ <p>{{object.body_markdown}}</p>
+ {% if object.project %}<p class="small">For: <a href="{% url 'gtd:project-detail' object.project.id %}">{{object.project}}</a></p>{%endif%}
+ <p class="small">Status: {{object.get_status_display}}</p>
+ <p class="small"><a href="{% url 'gtd:note-delete' object.pk %}">delete</a></p>
+ </article>
+{% endfor%}</div>
+</main>
+
+
+{% endblock %}
+{% block js %}
+<script type="text/javascript">var go_from_select = function(opt) { window.location = window.location.pathname + opt };</script>
+{% endblock%}
diff --git a/app/notes/templates/project_detail.html b/app/notes/templates/project_detail.html
new file mode 100644
index 0000000..543b9f6
--- /dev/null
+++ b/app/notes/templates/project_detail.html
@@ -0,0 +1,40 @@
+{% extends 'base_gtd.html' %}
+{% load typogrify_tags %}
+{% load get_note_type %}
+{% block extrahead %}
+<style>
+.detail-header {
+ margin-top: 3rem;
+ margin-bottom: 1rem;
+ padding-bottom: 1rem;
+}
+</style>
+{% endblock %}
+{% block primary %}
+<main role="main" class="archive-wrapper">
+ <div class="post-header detail-header">
+ <h1>Project: {{object.title}}</h1>
+
+<select class="form-control" style="margin-top: 2%;" onchange="go_from_select(this.options[this.selectedIndex].value)">
+ <option value="">All Projects</option>{% for object in projects %}
+ <option {% if object.title == project %}selected="selected" {%endif%}value="{% url 'gtd:project-detail' object.id%}">{{object}}</option>{%endfor%}
+</select>
+ </div>
+ <div class="post-body">
+ {{object.body_html|smartypants|safe}}
+ {% regroup note_set by note_type as type_list %}
+ {% for type in type_list %}
+ <h4>{% get_note_type type.grouper %}</h4>
+ <div class="note-list">{% for object in type.list %}<article>
+ <h2>{% if object.get_status_display == 'Completed' %}<strike style="color: #918d8d">{%endif%}{% if object.url %}<a href="{{object.url}}">{{object.title}}</a>{%else%}{{object.title}}{%endif%} <span class="note-edit"><a href="{%url 'gtd:note-edit' object.pk%}">edit</a></span>{% if object.get_status_display == 'Completed' %}</strike>{%endif%}</h2>
+ {{object.body_html|smartypants|safe}}
+ {% if object.get_status_display != 'None' %}<p class="small">{{object.get_status_display}}</p>{% endif %}
+ <p class="small"><a href="{% url 'gtd:note-delete' object.pk %}">delete</a></p></article>
+ {% endfor %}</div>
+{% endfor %}
+ </div>
+</main>
+{% endblock %}
+{% block js %}
+<script type="text/javascript">var go_from_select = function(opt) { console.log(opt); window.location = opt };</script>
+{% endblock%}
diff --git a/app/notes/templates/project_form.html b/app/notes/templates/project_form.html
new file mode 100644
index 0000000..ac7d13f
--- /dev/null
+++ b/app/notes/templates/project_form.html
@@ -0,0 +1,35 @@
+{% extends 'base_gtd.html' %}
+
+{% block extrahead %}
+<style>
+form .selector label {
+ position: inherit;
+}
+</style>
+{% endblock %}
+{% block primary %}
+<main role="main" class="archive-wrapper">
+ <div class="post-body">
+ <form action="" method="post" class="comment-form">{% csrf_token %}
+ {% for field in form %}
+ <fieldset>
+ {%if field.name == "project_type" or field.name == "outcome" or field.name == 'note_type'%}<span class="selector">{{field.label_tag}}</span>{%else%}{{field.label_tag}}{%endif%}
+ {%if field.name == "body_markdown"%}<div class="textarea-rounded">{{ field }}</div>{%else%}{{field}}{%endif%}
+ </fieldset>
+ <small class="alert">{% if field.errors %}{{field.errors}}{% endif %}</small>
+ {%endfor%}
+ <input class="btn" type="submit" name="add_new" value="Save and add another" />
+ <input type="submit" name="save" class="btn" value="Save" />
+ </form>
+ </div>
+</main>
+{% endblock %}
+ {% block js %}
+ {% if is_update %}{%else%}
+<script type="text/javascript">
+let params = new URL(document.location).searchParams;
+document.getElementById('id_title').value = params.get("title");
+document.getElementById('id_body_markdown').value = params.get("description");
+</script>
+{% endif %}
+ {% endblock%}
diff --git a/app/notes/templates/project_list.html b/app/notes/templates/project_list.html
new file mode 100644
index 0000000..ba46b59
--- /dev/null
+++ b/app/notes/templates/project_list.html
@@ -0,0 +1,25 @@
+{% extends 'base_gtd.html' %}
+{% block primary %}
+<main class="post-detail">
+ <div class="post-header"><ul class="flex header-list">
+ <li><a class="btn" href="{% url 'gtd:project-list' %}">All</a></li>
+ {% for object in project_types %}
+ <li><a class="btn" href="{% url 'gtd:project-list-type' object.1|lower%}">{%if object.1 == 'Lbh'%}{{object.1|upper}}{%else%}{{object.1}}{%endif%}</a></li>
+ {% endfor %}
+ <li class="right"><a href="{% url 'gtd:project-create' %}" class="btn">New</a></li>
+ </ul>
+ </div>
+ <div class="note-list">{% for object in object_list %}<article>
+ <h2><a href="{{object.get_absolute_url}}">{{object.title}}</a><span class="note-edit"><a href="{% url 'gtd:project-edit' object.pk %}">edit</a></span></h2>
+ <p>{{object.body_markdown}}</p>
+ <p class="small">Date Goal: {{object.date_goal}}</p>
+ <p class="small">Type: {{object.get_project_type_display}}</p>
+ <p class="small"><a href="{% url 'gtd:project-delete' object.pk %}">delete</a></p>
+ </article>
+{% endfor%}</div>
+</main>
+
+
+{% endblock %}
+{% block js %}
+{% endblock%}
diff --git a/app/notes/urls.py b/app/notes/urls.py
new file mode 100644
index 0000000..eb83804
--- /dev/null
+++ b/app/notes/urls.py
@@ -0,0 +1,34 @@
+from django.urls import path
+
+from . import views
+
+app_name = "notes"
+
+urlpatterns = [
+ path(
+ r'',
+ views.LuxNoteListView.as_view(),
+ {'note_type': None},
+ name="note-list"
+ ),
+ path(
+ r'create',
+ views.LuxNoteCreateView.as_view(),
+ name="note-create"
+ ),
+ path(
+ r'<str:note_type>',
+ views.LuxNoteListView.as_view(),
+ name="note-list-status"
+ ),
+ path(
+ r'<pk>/edit',
+ views.LuxNoteUpdateView.as_view(),
+ name="note-edit"
+ ),
+ path(
+ r'<pk>/delete',
+ views.LuxNoteDeleteView.as_view(),
+ name="note-delete"
+ ),
+]
diff --git a/app/notes/views.py b/app/notes/views.py
new file mode 100644
index 0000000..b160bed
--- /dev/null
+++ b/app/notes/views.py
@@ -0,0 +1,72 @@
+from django.views.generic import UpdateView, DetailView, ListView, CreateView, DeleteView, RedirectView
+from django.views.generic.base import TemplateView
+from django.urls import reverse, reverse_lazy
+from django.db.models import Q
+from django.db.models import Count
+
+#from taxonomy.models import Category
+
+from .models import (
+ LuxNote,
+)
+
+from .forms import (
+ LuxNoteCreateForm,
+ LuxNoteEditForm,
+)
+
+
+class LuxNoteCreateView(CreateView):
+ model = LuxNote
+ form_class = LuxNoteCreateForm
+ template_name = "note_form.html"
+
+ def get_success_url(self):
+ if 'add_new' in self.request.POST:
+ return reverse('notes:note-create')
+ else:
+ if self.object.project:
+ return reverse('notes:project-detail', kwargs={"pk": self.object.project.pk})
+ else:
+ return reverse('notes:note-create')
+
+
+class LuxNoteUpdateView(UpdateView):
+ model = LuxNote
+ form_class = LuxNoteEditForm
+ template_name = "note_form.html"
+
+ def get_context_data(self, **kwargs):
+ context = super(LuxNoteUpdateView, self).get_context_data(**kwargs)
+ context['is_update'] = True
+ return context
+
+ def get_success_url(self):
+ if self.object.project:
+ return reverse('notes:project-detail', kwargs={"pk": self.object.project.pk})
+ else:
+ return reverse('notes:note-list', kwargs={"pk": self.object.project.pk})
+
+
+class LuxNoteListView(ListView):
+ model = LuxNote
+ template_name = "note_list.html"
+
+ def get_queryset(self):
+ tag = self.request.GET.get("tag", False)
+ if tag:
+ return LuxNote.objects.filter(tags__name__in=[tag])
+ return LuxNote.objects.all()
+
+ def get_context_data(self, **kwargs):
+ context = super(LuxNoteListView, self).get_context_data(**kwargs)
+ context['tags'] = LuxNote.tags.all().order_by('name')
+ #context['project'] = self.request.GET.get("project", False)
+ return context
+
+
+class LuxNoteDeleteView(DeleteView):
+ # specify the model you want to use
+ model = LuxNote
+ success_url = "/notes/notes"
+ template_name = "notes/confirm_delete.html"
diff --git a/config/base_urls.py b/config/base_urls.py
index 93b90b7..ad79987 100644
--- a/config/base_urls.py
+++ b/config/base_urls.py
@@ -56,6 +56,7 @@ urlpatterns = [
path(r'admin/', admin.site.urls),
path(r'trading/', include('trading.urls')),
path(r'gtd/', include('gtd.urls')),
+ path(r'notes/', include('notes.urls')),
path(r'planner/', include('planner.urls')),
path(r'luximages/insert/', utils.views.insert_image),
path(r'feed.xml', JrnlRSSFeedView(),name="feed"),
diff --git a/templates/base_notes.html b/templates/base_notes.html
new file mode 100644
index 0000000..f406446
--- /dev/null
+++ b/templates/base_notes.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html {%block htmlclass%}{%endblock%} dir="ltr" lang="en-US">
+ {% block sitename %}
+<head>
+ <title>{% block pagetitle %}{% endblock %}</title>{%endblock%}
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="description"
+ content="{% block metadescription %}{% endblock %}">
+ <meta name="author" content="luxagraf">
+ {%block stylesheet%}<link rel="stylesheet"
+ href="/media/gtdscreenv1.css{%comment%}?{% now "u" %}{%endcomment%}"
+ media="screen">{%endblock%}
+ <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon">
+ {%block extrahead%}{%endblock%}
+</head>
+<body {%block bodyid%}{%endblock%}{%block bodyevents%}{%endblock%}>
+ <header class="header-wrapper">
+ <div id="logo">
+ <a class="logo-link" href="/" title="Home">L</span></a>
+ </div>
+ <nav>
+ <a class="nav-item" href="{% url 'notes:note-list' %}" title="View Notes">Notes</a>
+ <a class="nav-item" href="{% url 'notes:note-list' %}" title="View things that need to be done">todo</a>
+ </nav>
+ </header>
+ {% block breadcrumbs %}{% endblock %}
+ {% block primary %}{% endblock %}
+ {% block extrabody %}{% endblock %}
+ <footer class="page-footer">
+ <nav>
+ <a class="nav-item" href="/notes/todo" title="See what needs to be called in">Todo</a>
+ <a class="nav-item" href="/notes" title="View Guides">Notes</a>
+ <a class="nav-item" href="/posts" title="View Guides">Guides</a>
+ </nav>
+ <p id="license">
+ &copy; 2020-{% now "Y" %}
+ <span class="h-card"><a class="p-name u-url" href="https://luxagraf.net/">luxagraf</a><data class="p-locality" value="Everywhere"></data><data class="p-country-name" value="United States"></data></span>.
+ </p>
+ </footer>
+ {% block js %}{% endblock%}
+</body>
+</html>