diff options
author | luxagraf <sng@luxagraf.net> | 2023-11-15 17:08:29 -0500 |
---|---|---|
committer | luxagraf <sng@luxagraf.net> | 2023-11-15 17:08:29 -0500 |
commit | 5d26ed7dbba5475a93b70c450741b358153b0855 (patch) | |
tree | 8c9dcdb2bb1735e787cb0753215898b17e92f062 /app | |
parent | 9f1c7ef8a47ee1a2df40ce1f57df105af8b99cc1 (diff) |
gtd: rolled in wired stuff plus created my own gtd app
Diffstat (limited to 'app')
25 files changed, 1202 insertions, 0 deletions
diff --git a/app/gtd/__init__.py b/app/gtd/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/gtd/__init__.py diff --git a/app/gtd/forms.py b/app/gtd/forms.py new file mode 100644 index 0000000..a4727cf --- /dev/null +++ b/app/gtd/forms.py @@ -0,0 +1,66 @@ +from django.forms import ModelForm +from django.forms import ChoiceField + +from .models import GTDNote, GTDProject, WiredNote, WiredPost + + +class GTDNoteCreateForm(ModelForm): + class Meta: + model = GTDNote + fields = ['title', 'body_markdown', 'project', 'note_type', 'reminder', 'status'] + + +class GTDNoteEditForm(ModelForm): + class Meta: + model = GTDNote + fields = ['title', 'body_markdown', 'project', 'note_type', 'reminder', 'status'] + + +class GTDProjectCreateForm(ModelForm): + class Meta: + model = GTDProject + fields = ['title', 'body_markdown', 'date_goal', 'project_type', 'outcome'] + + +class GTDProjectUpdateForm(ModelForm): + class Meta: + model = GTDProject + fields = ['title', 'body_markdown', 'date_goal', 'project_type', 'outcome', 'date_ended'] + + +class WiredNoteCreateForm(ModelForm): + + def __init__(self,*args,**kwargs): + user = kwargs.pop('user') + self.user = user + super(WiredNoteCreateForm,self).__init__(*args,**kwargs) + self.fields['post'].required = False + + def save(self, commit=True): + self.instance.user = self.user + return super().save(commit=commit) + + class Meta: + model = WiredNote + fields = ['title', 'url', 'body_markdown', 'post'] + + +class WiredNoteEditForm(ModelForm): + + def __init__(self,*args,**kwargs): + super(WiredNoteEditForm,self).__init__(*args,**kwargs) + self.fields['post'].queryset = WiredPost.objects.all().order_by("title") + + def save(self, commit=True): + return super().save(commit=commit) + + class Meta: + model = WiredNote + fields = ['title', 'url', 'body_markdown', 'post', 'status', 'plan'] + + +class WiredPostUpdateForm(ModelForm): + + class Meta: + model = WiredPost + fields = ['title', 'post_status', 'url', 'template_type', 'update_frequency', 'edit_url', 'date_last_pub'] diff --git a/app/gtd/migrations/0001_initial.py b/app/gtd/migrations/0001_initial.py new file mode 100644 index 0000000..9df3dc9 --- /dev/null +++ b/app/gtd/migrations/0001_initial.py @@ -0,0 +1,71 @@ +# Generated by Django 4.2.7 on 2023-11-14 09:18 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('media', '0008_auto_20201202_1155'), + ] + + operations = [ + migrations.CreateModel( + name='GTDOutcome', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=300)), + ('slug', models.SlugField()), + ('body_markdown', models.TextField()), + ('body_html', models.TextField(blank=True)), + ('date_goal', models.DateField()), + ('date_ended', models.DateField()), + ('featured_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='media.luximage')), + ], + options={ + 'ordering': ('-date_goal',), + }, + ), + migrations.CreateModel( + name='GTDProject', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('short_title', models.CharField(blank=True, max_length=200, null=True)), + ('slug', models.SlugField()), + ('body_markdown', models.TextField(blank=True, null=True)), + ('body_html', models.TextField(blank=True)), + ('date_goal', models.DateField(blank=True, null=True)), + ('date_ended', models.DateField(blank=True, null=True)), + ('project_type', models.IntegerField(choices=[(0, 'wired'), (1, 'lbh'), (2, 'personal')], default=0)), + ('outcome', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='gtd.gtdoutcome')), + ], + options={ + 'ordering': ('-date_goal',), + 'get_latest_by': 'date_goal', + }, + ), + migrations.CreateModel( + name='GTDNote', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('slug', models.SlugField()), + ('body_markdown', models.TextField()), + ('body_html', models.TextField(blank=True)), + ('date_completed', models.DateField()), + ('date_created', models.DateTimeField(auto_now=True)), + ('note_type', models.IntegerField(choices=[(0, 'action'), (1, 'reminder'), (2, 'reference')], default=0)), + ('reminder', models.BigIntegerField(help_text='In days')), + ('status', models.IntegerField(choices=[(0, 'None'), (1, 'Open'), (2, 'Completed')], default=0)), + ('project', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='gtd.gtdproject')), + ], + options={ + 'ordering': ('-date_created',), + 'get_latest_by': 'date_created', + }, + ), + ] diff --git a/app/gtd/migrations/0002_alter_gtdnote_reminder.py b/app/gtd/migrations/0002_alter_gtdnote_reminder.py new file mode 100644 index 0000000..c626625 --- /dev/null +++ b/app/gtd/migrations/0002_alter_gtdnote_reminder.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2023-11-14 09:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('gtd', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='gtdnote', + name='reminder', + field=models.BigIntegerField(blank=True, help_text='In days', null=True), + ), + ] diff --git a/app/gtd/migrations/0003_alter_gtdnote_date_completed.py b/app/gtd/migrations/0003_alter_gtdnote_date_completed.py new file mode 100644 index 0000000..67eeffd --- /dev/null +++ b/app/gtd/migrations/0003_alter_gtdnote_date_completed.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2023-11-14 09:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('gtd', '0002_alter_gtdnote_reminder'), + ] + + operations = [ + migrations.AlterField( + model_name='gtdnote', + name='date_completed', + field=models.DateField(blank=True, null=True), + ), + ] diff --git a/app/gtd/migrations/0004_remove_gtdnote_slug_remove_gtdoutcome_slug_and_more.py b/app/gtd/migrations/0004_remove_gtdnote_slug_remove_gtdoutcome_slug_and_more.py new file mode 100644 index 0000000..e706ab8 --- /dev/null +++ b/app/gtd/migrations/0004_remove_gtdnote_slug_remove_gtdoutcome_slug_and_more.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.7 on 2023-11-14 14:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('gtd', '0003_alter_gtdnote_date_completed'), + ] + + operations = [ + migrations.RemoveField( + model_name='gtdnote', + name='slug', + ), + migrations.RemoveField( + model_name='gtdoutcome', + name='slug', + ), + migrations.RemoveField( + model_name='gtdproject', + name='slug', + ), + migrations.AlterField( + model_name='gtdproject', + name='project_type', + field=models.IntegerField(choices=[(0, 'Wired'), (1, 'LBH'), (2, 'Personal')], default=0), + ), + ] diff --git a/app/gtd/migrations/0005_wiredpost.py b/app/gtd/migrations/0005_wiredpost.py new file mode 100644 index 0000000..ec97830 --- /dev/null +++ b/app/gtd/migrations/0005_wiredpost.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.7 on 2023-11-15 13:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('gtd', '0004_remove_gtdnote_slug_remove_gtdoutcome_slug_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='WiredPost', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(blank=True, max_length=512, null=True)), + ('body', models.TextField(blank=True, null=True)), + ('url', models.CharField(blank=True, max_length=512, null=True)), + ('edit_url', models.CharField(blank=True, max_length=512, null=True)), + ('date_last_pub', models.DateField()), + ('guid', models.CharField(blank=True, db_index=True, max_length=512, null=True)), + ('author', models.CharField(blank=True, max_length=255, null=True)), + ('post_type', models.IntegerField(choices=[(0, 'review'), (1, 'guide'), (2, 'how-to')], default=1)), + ('template_type', models.IntegerField(choices=[(0, 'story'), (1, 'gallery')], default=0)), + ('update_frequency', models.BigIntegerField(help_text='In days')), + ('needs_update', models.BooleanField(default=False)), + ('is_live', models.BooleanField(default=True)), + ('post_status', models.IntegerField(choices=[(0, 'Assigned'), (1, 'turned in'), (2, 'published')], default=2)), + ], + options={ + 'ordering': ('date_last_pub',), + }, + ), + ] diff --git a/app/gtd/migrations/0006_remove_gtdoutcome_featured_image_wirednote.py b/app/gtd/migrations/0006_remove_gtdoutcome_featured_image_wirednote.py new file mode 100644 index 0000000..a301f26 --- /dev/null +++ b/app/gtd/migrations/0006_remove_gtdoutcome_featured_image_wirednote.py @@ -0,0 +1,35 @@ +# Generated by Django 4.2.7 on 2023-11-15 13:54 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('gtd', '0005_wiredpost'), + ] + + operations = [ + migrations.RemoveField( + model_name='gtdoutcome', + name='featured_image', + ), + migrations.CreateModel( + name='WiredNote', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=400)), + ('url', models.CharField(blank=True, max_length=400, null=True)), + ('body_markdown', models.TextField(blank=True, null=True)), + ('date_created', models.DateTimeField(default=django.utils.timezone.now)), + ('status', models.IntegerField(choices=[(0, 'Call In'), (1, 'Asked For'), (2, 'Coming'), (3, 'Testing'), (4, 'Done'), (5, 'Live')], default=0)), + ('plan', models.IntegerField(choices=[(0, 'For Guide'), (1, 'Review'), (2, 'Rave/Rant'), (3, 'No Plan')], default=0)), + ('post', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='gtd.wiredpost')), + ], + options={ + 'ordering': ('date_created', 'status'), + }, + ), + ] diff --git a/app/gtd/migrations/0007_alter_wiredpost_date_last_pub.py b/app/gtd/migrations/0007_alter_wiredpost_date_last_pub.py new file mode 100644 index 0000000..b2f113c --- /dev/null +++ b/app/gtd/migrations/0007_alter_wiredpost_date_last_pub.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.7 on 2023-11-15 15:01 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('gtd', '0006_remove_gtdoutcome_featured_image_wirednote'), + ] + + operations = [ + migrations.AlterField( + model_name='wiredpost', + name='date_last_pub', + field=models.DateField(default=datetime.datetime.now), + ), + ] diff --git a/app/gtd/migrations/0008_alter_wiredpost_date_last_pub.py b/app/gtd/migrations/0008_alter_wiredpost_date_last_pub.py new file mode 100644 index 0000000..a76d230 --- /dev/null +++ b/app/gtd/migrations/0008_alter_wiredpost_date_last_pub.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.7 on 2023-11-15 15:02 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('gtd', '0007_alter_wiredpost_date_last_pub'), + ] + + operations = [ + migrations.AlterField( + model_name='wiredpost', + name='date_last_pub', + field=models.DateField(default=datetime.datetime(2023, 11, 15, 15, 2, 22, 531344)), + ), + ] diff --git a/app/gtd/migrations/__init__.py b/app/gtd/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/gtd/migrations/__init__.py diff --git a/app/gtd/models.py b/app/gtd/models.py new file mode 100644 index 0000000..e021b42 --- /dev/null +++ b/app/gtd/models.py @@ -0,0 +1,232 @@ +import datetime +import os + +from django.utils import timezone +from django.contrib.gis.db import models +from django.contrib.contenttypes.fields import GenericRelation, GenericForeignKey +from django.contrib.contenttypes.models import ContentType +from django.contrib.sites.models import Site +from django.urls import reverse +from django.apps import apps +from django.conf import settings +from django.contrib.sitemaps import Sitemap + +from utils.util import render_images, render_products, parse_video, markdown_to_html, extract_main_image + + +def get_upload_path(self, filename): + return "images/post-images/%s/%s" % (datetime.datetime.today().strftime("%Y"), filename) + + +class GTDOutcome(models.Model): + title = models.CharField(max_length=300) + body_markdown = models.TextField() + body_html = models.TextField(blank=True) + date_goal = models.DateField() + date_ended = models.DateField() + + def __str__(self): + return self.title + + class Meta: + ordering = ('-date_goal',) + + def get_absolute_url(self): + return reverse('gtd:outcome', kwargs={"slug": self.slug}) + + def save(self, *args, **kwargs): + created = self.pk is None + if not created: + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + super(Outcome, self).save(*args, **kwargs) + + +class ProjectType(models.IntegerChoices): + WIRED = 0, ('Wired') + LBH = 1, ('LBH') + PERSONAL = 2, ('Personal') + + +class GTDProject(models.Model): + title = models.CharField(max_length=200) + short_title = models.CharField(max_length=200, blank=True, null=True) + body_markdown = models.TextField(null=True, blank=True) + body_html = models.TextField(blank=True) + date_goal = models.DateField(blank=True, null=True) + date_ended = models.DateField(blank=True, null=True) + project_type = models.IntegerField(choices=ProjectType.choices, default=ProjectType.WIRED) + outcome = models.ForeignKey(GTDOutcome, on_delete=models.SET_NULL, null=True, blank=True) + + class Meta: + ordering = ('-date_goal',) + get_latest_by = 'date_goal' + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse('gtd:project-detail', kwargs={"pk": self.id}) + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_date_goal() + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_date_goal().pk] ) + except model.DoesNotExist: + return '' + + def save(self, *args, **kwargs): + created = self.pk is None + if not created: + md = render_images(self.body_markdown) + self.body_html = markdown_to_html(md) + super(GTDProject, self).save(*args, **kwargs) + + +class NoteType(models.IntegerChoices): + ACTION = 0, ('action') + REMINDER = 1, ('reminder') + REFERENCE = 2, ('reference') + + +class GTDNote(models.Model): + title = models.CharField(max_length=200) + body_markdown = models.TextField() + body_html = models.TextField(blank=True) + date_completed = models.DateField(null=True, blank=True) + date_created = models.DateTimeField(auto_now=True) + note_type = models.IntegerField(choices=NoteType.choices, default=NoteType.ACTION) + reminder = models.BigIntegerField(help_text="In days", null=True, blank=True) + project = models.ForeignKey(GTDProject, on_delete=models.SET_NULL, null=True, blank=True) + STATUS = ( + (0, 'None'), + (1, 'Open'), + (2, 'Completed'), + ) + status = models.IntegerField(choices=STATUS, default=0) + + class Meta: + ordering = ('-date_created',) + get_latest_by = 'date_created' + + def __str__(self): + return self.title + + def get_absolute_url(self, *args, **kwargs): + return reverse('gtd:note-edit', kwargs={"pk": self.pk}) + + def save(self, *args, **kwargs): + self.body_html = markdown_to_html(self.body_markdown) + super(GTDNote, self).save(*args, **kwargs) + + +class PostType(models.IntegerChoices): + REVIEW = 0, ('review') + GUIDE = 1, ('guide') + HOWTO = 2, ('how-to') + + +class TemplateType(models.IntegerChoices): + STORY = 0, ('story') + GALLERY = 1, ('gallery') + + +class PostStatus(models.IntegerChoices): + ASSIGNED = 0, ('Assigned') + TURNEDIN = 1, ('turned in') + PUBLISHED = 2, ('published') + + +class WiredPost(models.Model): + # an entry in a feed + title = models.CharField(max_length=512, blank=True, null=True) + body = models.TextField(blank=True, null=True) + url = models.CharField(max_length=512, blank=True, null=True) + edit_url = models.CharField(max_length=512, blank=True, null=True) + date_last_pub = models.DateField(default=timezone.now()) + guid = models.CharField(max_length=512, blank=True, null=True, db_index=True) + author = models.CharField(max_length=255, blank=True, null=True) + post_type = models.IntegerField(choices=PostType.choices, default=PostType.GUIDE) + template_type = models.IntegerField(choices=TemplateType.choices, default=TemplateType.STORY) + update_frequency = models.BigIntegerField(help_text="In days") + #products = models.ManyToManyField(ProductLink, blank=True, null=True) + needs_update = models.BooleanField(default=False) + is_live = models.BooleanField(default=True) + post_status = models.IntegerField(choices=PostStatus.choices, default=PostStatus.PUBLISHED) + + + class Meta: + ordering = ('date_last_pub',) + + def __str__(self): + return self.title + + def time_since_update(self): + td = timezone.localdate() - self.date_last_pub + return int(td.days) + + #def get_needs_update(self): + # if self.time_since_update() > self.update_frequency: + # return True + # else: + # return False + + def days_overdue(self): + if self.needs_update == True: + return self.time_since_update() - self.update_frequency + else: + return 0 + + def admin_url(self): + return format_html('<a target="_blank" href="%s">%s</a>' % (self.url, self.url)) + admin_link.short_description = 'Link' + + def save(self, *args, **kwargs): + td = timezone.localdate() - self.date_last_pub + if td.days > self.update_frequency and self.post_status != 1: + self.needs_update = True + else: + self.needs_update = False + super(WiredPost, self).save() + + +class WiredNote(models.Model): + title = models.CharField(max_length=400) + url = models.CharField(max_length=400, blank=True, null=True) + body_markdown = models.TextField(blank=True, null=True) + date_created = models.DateTimeField(default=timezone.now) + post = models.ForeignKey(WiredPost, on_delete=models.CASCADE, null=True, blank=True) + STATUS = ( + (0, 'Call In'), + (1, 'Asked For'), + (2, 'Coming'), + (3, 'Testing'), + (4, 'Done'), + (5, 'Live'), + ) + status = models.IntegerField(choices=STATUS, default=0) + PLAN = ( + (0, 'For Guide'), + (1, 'Review'), + (2, 'Rave/Rant'), + (3, 'No Plan'), + ) + plan = models.IntegerField(choices=PLAN, default=0) + + class Meta: + ordering = ('date_created', 'status') + + def __str__(self): + return self.title + + def get_absolute_url(self): + return reverse('gtd:wirednote-edit', kwargs={"pk": self.pk}) + + def save(self, *args, **kwargs): + super(WiredNote, self).save() diff --git a/app/gtd/templates/gtd/note_form.html b/app/gtd/templates/gtd/note_form.html new file mode 100644 index 0000000..13591e8 --- /dev/null +++ b/app/gtd/templates/gtd/note_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" 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"%}<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/gtd/templates/gtd/note_list.html b/app/gtd/templates/gtd/note_list.html new file mode 100644 index 0000000..2e4ea0a --- /dev/null +++ b/app/gtd/templates/gtd/note_list.html @@ -0,0 +1,23 @@ +{% extends 'base_gtd.html' %} +{% block primary %} +<main class="post-detail"> + <div class="post-header"><ul class="flex header-list"> + {% for status in note_statuses %} + <li><a class="btn" href="{% url 'gtd:note-list-status' status.1|lower%}">{{status.1}}</a></li> + {% endfor %} + <li class="right"><a href="{% url 'gtd:note-create' %}" class="btn">New Note</a></li> + </ul> + </div> + <div class="note-list">{% for object in object_list %}<article> + <h2>{{object.title}}{{object.title}}<span class="note-edit"><a href="{%url 'gtd:note-edit' object.pk%}">edit</a></span></h2> + <p>{{object.body_markdown}}</p> + <p class="small">For: <a href="/post/{{object.post.pk}}/notes">{{object.post}}</a></p> + <p class="small">Status: <a href="/post/{{object.post.pk}}/notes">{{object.get_status_display}}</a></p> + </article> +{% endfor%}</div> +</main> + + +{% endblock %} +{% block js %} +{% endblock%} diff --git a/app/gtd/templates/gtd/post_list.html b/app/gtd/templates/gtd/post_list.html new file mode 100644 index 0000000..1b56106 --- /dev/null +++ b/app/gtd/templates/gtd/post_list.html @@ -0,0 +1,25 @@ +{% extends 'base_gtd.html' %} +{%block extrahead%} +<link href="/media/sortable.min.css" rel="stylesheet" /> +{%endblock%} +{% block primary %} +<main class="content"> + <div class="narrow mtop"> + <h2>New Guides</h2> + <ul>{% for object in object_list %} + <li> + <a href="/post/{{object.id}}/notes">{{object.title}}</a> + </li>{% endfor %} + </ul> + <h2>Reviews and Raves</h2> + <ul>{% for object in reviews %} + <li> + <a href="{{object.get_absolute_url}}">{{object.title}}</a> + </li>{% endfor %} + </ul> + </div> +</main> +{% endblock %} +{% block js %} +<script src="/media/sortable.min.js"></script> +{% endblock%} diff --git a/app/gtd/templates/gtd/post_table.html b/app/gtd/templates/gtd/post_table.html new file mode 100644 index 0000000..f6803fe --- /dev/null +++ b/app/gtd/templates/gtd/post_table.html @@ -0,0 +1,93 @@ +{% extends 'base_gtd.html' %} +{%block extrahead%} +<link href="/media/sortable.min.css" rel="stylesheet" /> +{%endblock%} +{% block primary %} +<main class="content"> +<div class="results"> +<table class="sortable" id="result_list"> +<thead> +<tr> +<th scope="col" class="sortable column-title"> + <div class="text"><span>Title</span></div> +</th> +<th scope="col" class="column-admin_url"> + <div class="text"><span>URL</span></div> +</th> +<th scope="col" class="column-admin_url"> + <div class="text"><span>Edit URL</span></div> +</th> +<th scope="col" class="sortable column-date_last_pub sorted ascending"> +Date last pub + </th> +<th scope="col" class="sortable column-post_type"> + <div class="text">Post type</div> +</th> +<th scope="col" class="sortable column-update_frequency"> + <div class="text">Freq</div> +</th> +<th scope="col" class="sortable column-needs_update"> + <div class="text">Update?</div> +</th> +<th scope="col" class="column-days_overdue"> + <div class="text"><span>Overdue</span></div> +</th> +<th scope="col" class="column-days_overdue"> + <div class="text"><span>Edit</span></div> +</th> +</tr> +</thead> +<tbody>{% for object in object_list %} +<tr> + <td class="field-title"> + <a href="{% url 'gtd:post-detail' object.id %}">{{object.title}}</a> + </td> + <td class="field-admin_url"> + <a target="_blank" href="{{object.url}}"> + {{object.url|truncatechars:45}} + </a> + </td> + <td class="field-admin_url">{% if object.edit_url %} + <a target="_blank" href="{{object.edit_url}}"> + edit + </a>{%else%}<a href="{% url 'gtd:post-edit' object.pk %}">add</a>{%endif%} + </td> + <td class="field-date_last_pub nowrap">{{object.date_last_pub}}</td> + <td class="field-post_type"><span class="hide">{{object.post_type}}</span>{{object.get_post_type_display}}</td> + <td class="field-update_frequency">{{object.update_frequency}}</td> + <td class="field-needs_update">{% if object.needs_update %} + <span class="hide">1</span><img src="/static/admin/img/icon-yes.svg" alt="True">{%else%} + <span class="hide">0</span><img src="/static/admin/img/icon-no.svg" alt="False">{%endif%} + </td> + <td class="field-days_overdue">{{object.days_overdue}}</td> + <td class="field-days_overdue"><a href="{% url 'gtd:post-edit' object.pk %}">edit</a></td></tr> +</tr> +{% endfor %} +{% if reviews %}{% for object in reviews %} +<tr> + <td class="field-title"> + <a href="/post/{{object.id}}/notes">{{object.title}}</a> + </td> + <td class="field-admin_url"> + <a target="_blank" href="{{object.url}}"> + {{object.url|truncatechars:55}} + </a> + </td> + <td class="field-date_last_pub nowrap">{{object.date_last_pub}}</td> + <td class="field-post_type"><span class="hide">{{object.post_type}}</span>{{object.get_plan_display}}</td> + <td class="field-update_frequency">{{object.update_frequency}}</td> + <td class="field-needs_update">{% if object.needs_update %} + <span class="hide">1</span><img src="/static/admin/img/icon-yes.svg" alt="True">{%else%} + <span class="hide">0</span><img src="/static/admin/img/icon-no.svg" alt="False">{%endif%} + </td> + <td class="field-days_overdue">{{object.days_overdue}}</td> + <td class="field-days_overdue"><a href="{% url 'notes:edit' object.pk %}">edit</a></td></tr> +</tr>{% endfor %}{% endif %} +</tbody> +</table> +</div> +</main> +{% endblock %} +{% block js %} +<script src="/media/sortable.min.js"></script> +{% endblock%} diff --git a/app/gtd/templates/gtd/project_detail.html b/app/gtd/templates/gtd/project_detail.html new file mode 100644 index 0000000..bab6982 --- /dev/null +++ b/app/gtd/templates/gtd/project_detail.html @@ -0,0 +1,17 @@ +{% extends 'base_gtd.html' %} +{% load typogrify_tags %} +{% block primary %} +<main role="main" class="archive-wrapper"> + <div class="post-body"> + <h2>Project: {{object.title}}</h2> + {{object.body_html|smartypants|safe}} + <h3>Actions</h3> + {% for object in object.gtdnote_set.all %} + <h4>{{object.title}} <span class="note-edit"><a href="{%url 'gtd:note-edit' object.pk%}">edit</a></span></h4> + {{object.body_html|smartypants|safe}} + {% endfor %} + </div> +</main> +{% endblock %} + {% block js %} + {% endblock%} diff --git a/app/gtd/templates/gtd/project_form.html b/app/gtd/templates/gtd/project_form.html new file mode 100644 index 0000000..ac7d13f --- /dev/null +++ b/app/gtd/templates/gtd/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/gtd/templates/gtd/project_list.html b/app/gtd/templates/gtd/project_list.html new file mode 100644 index 0000000..a37f4e8 --- /dev/null +++ b/app/gtd/templates/gtd/project_list.html @@ -0,0 +1,23 @@ +{% extends 'base_gtd.html' %} +{% block primary %} +<main class="post-detail"> + <div class="post-header"><ul class="flex header-list"> + {% for status in note_statuses %} + <li><a class="btn" href="{% url 'gtd:note-list-status' status.1|lower%}">{{status.1}}</a></li> + {% endfor %} + <li class="right"><a href="{% url 'gtd:note-create' %}" class="btn">New Note</a></li> + </ul> + </div> + <div class="note-list">{% for object in object_list %}<article> + <h2>{{object.title}}<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> + </article> +{% endfor%}</div> +</main> + + +{% endblock %} +{% block js %} +{% endblock%} diff --git a/app/gtd/templates/gtd/wirednote_form.html b/app/gtd/templates/gtd/wirednote_form.html new file mode 100644 index 0000000..92b8230 --- /dev/null +++ b/app/gtd/templates/gtd/wirednote_form.html @@ -0,0 +1,26 @@ +{% extends 'base_gtd.html' %} +{% block primary %} +<main role="main" class="archive-wrapper"> + <form action="" method="post" class="comment-form">{% csrf_token %} + {% for field in form %} + <fieldset> + {%if field.name == "post" or field.name == "status" or field.name == 'plan'%}<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> +</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_url').value = params.get("url"); +document.getElementById('id_body_markdown').value = params.get("description"); +</script> +{% endif %} + {% endblock%} diff --git a/app/gtd/templates/gtd/wirednote_list.html b/app/gtd/templates/gtd/wirednote_list.html new file mode 100644 index 0000000..7b17617 --- /dev/null +++ b/app/gtd/templates/gtd/wirednote_list.html @@ -0,0 +1,23 @@ +{% extends 'base_gtd.html' %} +{% block primary %} +<main class="post-detail"> + <div class="post-header"><ul class="flex header-list"> + {% for status in note_statuses %} + <li><a class="btn" href="{% url 'gtd:wirednote-list' status.1|lower%}">{{status.1}}</a></li> + {% endfor %} + <li class="right"><a href="{% url 'gtd:wirednote-create' %}" class="btn">New Note</a></li> + </ul> + </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="{{object.get_absolute_url}}">edit</a></span></h2> + <p>{{object.body_markdown}}</p> + <p class="small">For: <a href="/post/{{object.post.pk}}/notes">{{object.post}}</a></p> + <p class="small">Status: <a href="/post/{{object.post.pk}}/notes">{{object.get_status_display}}</a></p> + </article> +{% endfor%}</div> +</main> + + +{% endblock %} +{% block js %} +{% endblock%} diff --git a/app/gtd/templates/gtd/wiredpost_detail.html b/app/gtd/templates/gtd/wiredpost_detail.html new file mode 100644 index 0000000..92ac269 --- /dev/null +++ b/app/gtd/templates/gtd/wiredpost_detail.html @@ -0,0 +1,21 @@ +{% extends 'base_gtd.html' %} +{% block primary %} +<main class="post-detail"> + <div class="post-header"> +<h1><a href="{{object.url}}">{{object.title}}</a></h1> +<p>Last Updated: {{object.date_last_pub}}</p> +<p>Needs Update: {% if object.needs_update %} Yes {% if object.days_overdue %}{{object.days_overdue}} days overdue{%endif %}{%endif%}</p> + </div>{% regroup notes by get_status_display as newlist %} + <div class="note-list">{% for obj in newlist %} + <h5>{{obj.grouper}}</h5> + {% for object in obj.list %} + <article> + <h2>{%if object.url%}<a href="{{object.url}}">{{object.title}}</a>{%else%}{{object.title}}{%endif%}<span class="note-edit"><a href="{{object.get_absolute_url}}">edit</a></span></h2> + <p>{{object.body_markdown}}</p> + <p>Status: {% if object.status == 0 %}<span class="alert">{{object.get_status_display}}</span>{%else%}{{object.get_status_display}}{%endif%}</p> + </article>{%endfor%} +{% endfor%}</div> +</main> +{% endblock %} + {% block js %} + {% endblock%} diff --git a/app/gtd/templates/gtd/wiredpost_form.html b/app/gtd/templates/gtd/wiredpost_form.html new file mode 100644 index 0000000..a9880cf --- /dev/null +++ b/app/gtd/templates/gtd/wiredpost_form.html @@ -0,0 +1,16 @@ +{% extends 'base_gtd.html' %} +{% block primary %} +<main role="main" class="archive-wrapper"> + <form action="" method="post" class="comment-form">{% csrf_token %} + {% for field in form %} + <fieldset> + {%if field.name == "guide_type" or field.name == "post_status" or field.name == "template_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> +</main> +{% endblock %} diff --git a/app/gtd/urls.py b/app/gtd/urls.py new file mode 100644 index 0000000..db416bb --- /dev/null +++ b/app/gtd/urls.py @@ -0,0 +1,101 @@ +from django.urls import path, re_path + +from . import views + +app_name = "gtd" + +urlpatterns = [ + path( + r'todo', + views.GTDNoteTODOView.as_view(), + name="note-list" + ), + path( + r'notes', + views.GTDNoteListView.as_view(), + {'status':None}, + name="note-list" + ), + path( + r'notes/create', + views.GTDNoteCreateView.as_view(), + name="note-create" + ), + path( + r'notes/<str:status>', + views.GTDNoteListView.as_view(), + name="note-list-status" + ), + path( + r'note/<pk>/edit', + views.GTDNoteUpdateView.as_view(), + name="note-edit" + ), + path( + r'projects', + views.GTDProjectListView.as_view(), + name="project-list" + ), + path( + r'projects/create', + views.GTDProjectCreateView.as_view(), + name="project-create" + ), + path( + r'projects/<pk>', + views.GTDProjectDetailView.as_view(), + name="project-detail" + ), + path( + r'projects/<pk>/edit', + views.GTDProjectUpdateView.as_view(), + name="project-edit" + ), + path( + r'wirednote', + views.WiredNoteListView.as_view(), + {'status':None}, + name="wirednote-list" + ), + path( + r'wirednote/create', + views.WiredNoteCreateView.as_view(), + name="wirednote-create" + ), + path( + r'wirednote/<str:status>', + views.WiredNoteListView.as_view(), + name="wirednote-list" + ), + path( + r'wirednote/<pk>/edit', + views.WiredNoteUpdateView.as_view(), + name="wirednote-edit" + ), + + path( + r'post', + views.WiredPostListView.as_view(), + name="post-list" + ), + path( + r'post/new', + views.WiredPostNewView.as_view(), + name="post-create" + ), + path( + r'post/<pk>/edit', + views.WiredPostUpdateView.as_view(), + name="post-edit" + ), + path( + r'posts/todo', + views.WiredPostTodoView.as_view(), + name="post-todo" + ), + path( + r'<pk>/notes', + views.WiredPostNotesView.as_view(), + name="post-detail" + ), +] diff --git a/app/gtd/views.py b/app/gtd/views.py new file mode 100644 index 0000000..8082db3 --- /dev/null +++ b/app/gtd/views.py @@ -0,0 +1,221 @@ +from django.views.generic import UpdateView, DetailView, ListView +from django.views.generic.edit import CreateView, DeleteView, UpdateView +from django.contrib.auth.mixins import LoginRequiredMixin +from django.urls import reverse + +from .models import GTDNote, GTDProject, WiredNote, WiredPost +from .forms import GTDNoteCreateForm, GTDNoteEditForm, GTDProjectCreateForm, GTDProjectUpdateForm, WiredNoteCreateForm, WiredNoteEditForm, WiredPostUpdateForm + +class GTDNoteCreateView(CreateView): + model = GTDNote + form_class = GTDNoteCreateForm + template_name = "gtd/note_form.html" + + def get_form_kwargs(self): + kwargs = super(GTDNoteCreateView, self).get_form_kwargs() + return kwargs + + def get_success_url(self): + if 'add_new' in self.request.POST: + return reverse('gtd:note-create') + else: + if self.object.project: + return reverse('gtd:project-detail', kwargs={"pk": self.object.project.pk}) + else: + return reverse('gtd:note-create') + + +class GTDNoteUpdateView(UpdateView): + model = GTDNote + form_class = GTDNoteEditForm + template_name = "gtd/note_form.html" + + def get_context_data(self, **kwargs): + context = super(GTDNoteUpdateView, self).get_context_data(**kwargs) + context['is_update'] = True + return context + + def get_success_url(self): + return reverse('gtd:project-detail', kwargs={"pk": self.object.project.pk}) + + +class GTDNoteListView(ListView): + model = GTDNote + template_name = "gtd/note_list.html" + + def get_queryset(self): + if self.kwargs['status']: + status_reverse = dict((v, k) for k, v in GTDNote.STATUS) + status = status_reverse[self.kwargs['status'].title()] + return GTDNote.objects.filter(status=status) + return GTDNote.objects.filter(status=1) + + def get_context_data(self, **kwargs): + context = super(GTDNoteListView, self).get_context_data(**kwargs) + context['note_statuses'] = GTDNote.STATUS + return context + + +class GTDProjectCreateView(CreateView): + model = GTDProject + form_class = GTDProjectCreateForm + template_name = "gtd/project_form.html" + + def get_form_kwargs(self): + kwargs = super(GTDProjectCreateView, self).get_form_kwargs() + return kwargs + + def get_success_url(self): + if 'add_new' in self.request.POST: + return reverse('gtd:project-create') + else: + return reverse('gtd:project-detail', kwargs={"pk": self.object.pk}) + + +class GTDProjectUpdateView(UpdateView): + model = GTDProject + form_class = GTDProjectUpdateForm + template_name = "gtd/project_form.html" + + def get_context_data(self, **kwargs): + context = super(GTDProjectUpdateView, self).get_context_data(**kwargs) + context['is_update'] = True + return context + + +class GTDProjectDetailView(DetailView): + model = GTDProject + template_name = "gtd/project_detail.html" + + def get_context_data(self, **kwargs): + context = super(GTDProjectDetailView, self).get_context_data(**kwargs) + return context + + +class GTDProjectListView(ListView): + model = GTDProject + template_name = "gtd/project_list.html" + + def get_context_data(self, **kwargs): + context = super(GTDProjectListView, self).get_context_data(**kwargs) + return context + + +class GTDNoteTODOView(ListView): + model = GTDNote + template_name = "gtd/note_list.html" + + def get_queryset(self): + return GTDNote.objects.filter(status=1) + + +class WiredNoteCreateView(CreateView): + model = WiredNote + form_class = WiredNoteCreateForm + + def get_form_kwargs(self): + kwargs = super(WiredNoteCreateView, self).get_form_kwargs() + kwargs.update({'user': self.request.user}) + return kwargs + + def get_success_url(self): + if 'add_new' in self.request.POST: + return reverse('gtd:wirednotes-create') + else: + if self.object.post: + return reverse('gtd:wiredposts-detail', kwargs={"pk": self.object.post.pk}) + else: + return reverse('gtd:wirednotes-create') + + +class WiredNoteUpdateView(UpdateView): + model = WiredNote + form_class = WiredNoteEditForm + + def get_form_kwargs(self): + kwargs = super(WiredNoteUpdateView, self).get_form_kwargs() + return kwargs + + def get_context_data(self, **kwargs): + context = super(WiredNoteUpdateView, self).get_context_data(**kwargs) + context['is_update'] = True + return context + + def get_success_url(self): + return reverse('gtd:post-detail', kwargs={"pk": self.object.post.pk}) + + +class WiredNoteListView(ListView): + model = WiredNote + + def get_queryset(self): + if self.kwargs['status']: + status_reverse = dict((v, k) for k, v in WiredNote.STATUS) + status = status_reverse[self.kwargs['status'].title()] + return WiredNote.objects.filter(status=status) + return WiredNote.objects.all() + + def get_context_data(self, **kwargs): + context = super(WiredNoteListView, self).get_context_data(**kwargs) + context['note_statuses'] = WiredNote.STATUS + return context + + +class WiredPostListView(ListView): + model = WiredPost + template_name = 'gtd/post_table.html' + + def get_queryset(self): + return WiredPost.objects.all().order_by("-needs_update") + + +class WiredPostNewView(ListView): + template_name = 'gtd/post_list.html' + + def get_queryset(self): + return WiredPost.objects.filter(is_live=0) + + def get_context_data(self, **kwargs): + context = super(WiredPostNewView, self).get_context_data(**kwargs) + context['reviews'] = WiredNote.objects.filter(plan__in=[1,2], status__in=[0,1,2,3]) + return context + + +class WiredPostTodoView(LoginRequiredMixin, ListView): + template_name = 'posts/post_table.html' + + def get_queryset(self): + qs = WiredPost.objects.filter(user=self.request.user) + qs = qs.filter(Q(needs_update=True) | Q(is_live=0)).exclude(post_status=1) + unsorted_results = qs.all() + return sorted(unsorted_results, key=lambda a: a.days_overdue(), reverse=True) + + def get_context_data(self, **kwargs): + context = super(WiredPostTodoView, self).get_context_data(**kwargs) + context['reviews'] = WiredNote.objects.filter(plan__in=[1,2,3]).exclude(status=4) + return context + + +class WiredPostNotesView(DetailView): + model = WiredPost + + def get_context_data(self, **kwargs): + context = super(WiredPostNotesView, self).get_context_data(**kwargs) + context['notes'] = self.get_object().wirednote_set.all().order_by("status") + return context + +class WiredPostUpdateView(UpdateView): + model = WiredPost + form_class = WiredPostUpdateForm + + def get_form_kwargs(self): + kwargs = super(WiredPostUpdateView, self).get_form_kwargs() + return kwargs + + def get_success_url(self): + if 'add_new' in self.request.POST: + return reverse('gtd:posts-create') + else: + return reverse('gtd:posts-detail', kwargs={"pk": self.object.pk}) + + |