diff options
author | luxagraf <sng@luxagraf.net> | 2021-07-25 16:38:47 -0400 |
---|---|---|
committer | luxagraf <sng@luxagraf.net> | 2021-07-25 16:38:47 -0400 |
commit | ae46526a82d604174a33dd3dd9456aa8483cee80 (patch) | |
tree | d368f83d15ce6528e7e56746772a7380616bce2b /app/planner | |
parent | c3672978183dbc0cb0bf728b3dfadf12dd831a60 (diff) |
added basic planner app
Diffstat (limited to 'app/planner')
-rw-r--r-- | app/planner/__init__.py | 0 | ||||
-rw-r--r-- | app/planner/admin.py | 37 | ||||
-rw-r--r-- | app/planner/migrations/0001_initial.py | 42 | ||||
-rw-r--r-- | app/planner/migrations/0002_auto_20210725_1548.py | 22 | ||||
-rw-r--r-- | app/planner/migrations/__init__.py | 0 | ||||
-rw-r--r-- | app/planner/models.py | 108 | ||||
-rw-r--r-- | app/planner/templates/planner/base.html | 25 | ||||
-rw-r--r-- | app/planner/templates/planner/create_form.html | 42 | ||||
-rw-r--r-- | app/planner/templates/planner/list.html | 15 | ||||
-rw-r--r-- | app/planner/urls.py | 19 | ||||
-rw-r--r-- | app/planner/views.py | 44 |
11 files changed, 354 insertions, 0 deletions
diff --git a/app/planner/__init__.py b/app/planner/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/planner/__init__.py diff --git a/app/planner/admin.py b/app/planner/admin.py new file mode 100644 index 0000000..3b24ec2 --- /dev/null +++ b/app/planner/admin.py @@ -0,0 +1,37 @@ +from django.contrib import admin +from django import forms +from django.contrib.gis.admin import OSMGeoAdmin +from django.contrib.contenttypes.admin import GenericStackedInline + +from utils.widgets import AdminImageWidget, LGEntryForm +from .models import Camp + +from media.models import LuxImage +from utils.util import get_latlon + + +@admin.register(Camp) +class PlannerAdmin(OSMGeoAdmin): + list_display = ('name', 'date_arrive', 'date_depart', 'distance_from_previous', 'price', 'site_number',) + list_filter = ('date_arrive', 'has_water' ) + # options for OSM map Using custom ESRI topo map + lat, lon = get_latlon() + default_lon = lon + default_lat = lat + default_zoom = 10 + units = True + scrollable = False + map_width = 700 + map_height = 425 + map_template = 'gis/admin/osm.html' + openlayers_url = '/static/admin/js/OpenLayers.js' + + class Media: + js = ('image-loader.js', 'next-prev-links.js') + #'product-loader.js', + css = { + "all": ("my_styles.css",) + } + + + diff --git a/app/planner/migrations/0001_initial.py b/app/planner/migrations/0001_initial.py new file mode 100644 index 0000000..8ae9744 --- /dev/null +++ b/app/planner/migrations/0001_initial.py @@ -0,0 +1,42 @@ +# Generated by Django 3.2.5 on 2021-07-25 11:56 + +import django.contrib.gis.db.models.fields +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('locations', '0028_auto_20200308_1152'), + ] + + operations = [ + migrations.CreateModel( + name='Camp', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('date_arrive', models.DateField(verbose_name='Arrival Date')), + ('date_depart', models.DateField(blank=True, null=True, verbose_name='Departure Date')), + ('distance_from_previous', models.FloatField(blank=True, null=True)), + ('link', models.CharField(blank=True, max_length=200, null=True)), + ('point', django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326)), + ('notes', models.TextField(blank=True, null=True)), + ('notes_html', models.TextField(blank=True, null=True)), + ('price', models.FloatField(blank=True, null=True)), + ('site_number', models.FloatField(blank=True, null=True)), + ('has_water', models.BooleanField(default=True)), + ('has_electric', models.BooleanField(default=True)), + ('has_dump', models.BooleanField(default=True)), + ('rating', models.CharField(blank=True, choices=[('1', '1 Star'), ('2', '2 Stars'), ('3', '3 Stars'), ('4', '4 Stars'), ('5', '5 Stars')], max_length=1, null=True)), + ('location', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='locations.location')), + ], + options={ + 'ordering': ('-date_arrive',), + 'get_latest_by': 'date_arrive', + }, + ), + ] diff --git a/app/planner/migrations/0002_auto_20210725_1548.py b/app/planner/migrations/0002_auto_20210725_1548.py new file mode 100644 index 0000000..1edafe5 --- /dev/null +++ b/app/planner/migrations/0002_auto_20210725_1548.py @@ -0,0 +1,22 @@ +# Generated by Django 3.2.5 on 2021-07-25 15:48 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('planner', '0001_initial'), + ] + + operations = [ + migrations.AlterModelOptions( + name='camp', + options={'get_latest_by': 'date_arrive', 'ordering': ('date_arrive',)}, + ), + migrations.AddField( + model_name='camp', + name='map_link', + field=models.CharField(blank=True, max_length=400, null=True), + ), + ] diff --git a/app/planner/migrations/__init__.py b/app/planner/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/planner/migrations/__init__.py diff --git a/app/planner/models.py b/app/planner/models.py new file mode 100644 index 0000000..f5bb042 --- /dev/null +++ b/app/planner/models.py @@ -0,0 +1,108 @@ +import datetime +import os + +from django.dispatch import receiver +from django.contrib.gis.db import models +from django.db.models.signals import post_save +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.utils.functional import cached_property +from django.apps import apps +from django.conf import settings +from django.contrib.sitemaps import Sitemap +from django import forms + +from locations.models import Location +from utils.util import render_images, render_products, parse_video, markdown_to_html, extract_main_image + + +class Camp(models.Model): + name = models.CharField(max_length=200) + date_arrive = models.DateField('Arrival Date') + date_depart = models.DateField('Departure Date', blank=True, null=True) + distance_from_previous = models.FloatField(null=True, blank=True) + link = models.CharField(max_length=200, null=True, blank=True) + map_link = models.CharField(max_length=400, null=True, blank=True) + point = models.PointField(null=True, blank=True) + location = models.ForeignKey(Location, on_delete=models.CASCADE, null=True, blank=True) + notes = models.TextField(null=True, blank=True) + notes_html = models.TextField(null=True, blank=True) + price = models.FloatField(null=True, blank=True) + site_number = models.FloatField(null=True, blank=True) + has_water = models.BooleanField(default=True) + has_electric = models.BooleanField(default=True) + has_dump = models.BooleanField(default=True) + RATINGS = ( + ('1', "1 Star"), + ('2', "2 Stars"), + ('3', "3 Stars"), + ('4', "4 Stars"), + ('5', "5 Stars"), + ) + rating = models.CharField(max_length=1, choices=RATINGS, null=True, blank=True) + + class Meta: + ordering = ('date_arrive',) + get_latest_by = 'date_arrive' + + def __str__(self): + return self.name + + def get_absolute_url(self): + if self.post_type == PostType.ESSAY: + return reverse('essays:detail', kwargs={"slug": self.slug}) + if self.post_type == PostType.SRC: + return reverse('src:detail', kwargs={"slug": self.slug}) + if self.post_type == PostType.FIELD_NOTE: + return reverse('fieldnote:detail', kwargs={"year": self.pub_date.year, "month": self.pub_date.strftime("%m"), "slug": self.slug}) + if self.post_type == PostType.JRNL: + return reverse('jrnl:detail', kwargs={"year": self.pub_date.year, "month": self.pub_date.strftime("%m"), "slug": self.slug}) + if self.post_type == PostType.RANGE: + return reverse('range:range-detail', kwargs={"issue": self.get_issue_str(), "slug": self.slug}) + if self.post_type == PostType.FRIENDS: + return reverse('friends:friends-detail', kwargs={"issue": self.get_issue_str(), "slug": self.slug}) + + def comment_period_open(self): + return self.enable_comments and datetime.datetime.today() - datetime.timedelta(30) <= self.pub_date + + def get_featured_image_thumb(self): + return self.featured_image.get_image_url_by_size("thumbnail") + + def get_content_type(self): + return ContentType.objects.get(app_label="posts", model="post") + + def get_issue_str(self): + issue = self.issue + if self.issue < 100: + issue = "0%s" % self.issue + if self.issue < 10: + issue = "00%s" % self.issue + return issue + + @property + def get_previous_published(self): + return self.get_previous_by_date_arrive() + + @property + def get_previous_admin_url(self): + n = self.get_previous_by_pub_date() + return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + + @property + def get_next_published(self): + return self.get_next_by_date_arrive() + + @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_pub_date().pk] ) + except model.DoesNotExist: + return '' + + def save(self, *args, **kwargs): + created = self.pk is None + self.notes_html = markdown_to_html(self.notes) + super(Camp, self).save(*args, **kwargs) diff --git a/app/planner/templates/planner/base.html b/app/planner/templates/planner/base.html new file mode 100644 index 0000000..4de5ce7 --- /dev/null +++ b/app/planner/templates/planner/base.html @@ -0,0 +1,25 @@ +<html> +<head> + <title>{% block pagetitle %}Luxagraf - Planner{% endblock %}</title> + <meta charset="utf-8"> + <meta http-equiv="x-ua-compatible" content="ie=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + {%block stylesheet%}<link rel="stylesheet" + href="/media/planner.css{%comment%}?{% now "u" %}{%endcomment%}" + media="screen">{%endblock%} + <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon"> + {%block extrahead%}{%endblock%} +</head> + </head> + <body> + <nav> + <span class="nav-item"><a href="{% url 'luxplanner:list' %}">Home</a></span> + <span class="nav-item"><a href="{% url 'luxplanner:create' %}">Add Camp</a></span> + </nav> + {% block content %} + {% endblock %} + </body> + {% block js %} + {% endblock %} +</html> + diff --git a/app/planner/templates/planner/create_form.html b/app/planner/templates/planner/create_form.html new file mode 100644 index 0000000..3db0282 --- /dev/null +++ b/app/planner/templates/planner/create_form.html @@ -0,0 +1,42 @@ +{% extends 'planner/base.html' %} +{%block extrahead%}<link href="/media/mc-calendar.min.css" rel="stylesheet" />{%endblock%} +{% load typogrify_tags %} + {% block pagetitle %}Luxagraf - Add Camp{% endblock %} + {% block content %} + <form id="id_form" action="{% url 'luxplanner:create' %}" method="post" class="big">{% csrf_token %} + {% for field in form %} + <fieldset> + {{ field.errors }} + {% if field.name == 'status'%} + <label class="hide" for="id_status">Status:</label>{{ field }} + {% else %} + {{ field.label_tag }} {{ field }} + {% endif %} + {% if field.help_text %} + <p class="help">{{ field.help_text|safe }}</p> + {% endif %} + </fieldset> +{% endfor %} + <div class="flex"> + <input type="submit" name="post" class="btn" value="add camp"/> + </div> + </form> + {% endblock %} + + {% block js %} + +<script src="/media/js/mc-calendar.min.js"></script> + <script> +arrive = MCDatepicker.create({ + el: '#id_date_arrive', + dateFormat: 'YYYY-MM-DD' +}); +depart = MCDatepicker.create({ + el: '#id_date_depart', + dateFormat: 'YYYY-MM-DD', +}); +arrive.onClose(() => depart.setYear(arrive.getYear())); +arrive.onClose(() => depart.setMonth(arrive.getMonth())); +arrive.onClose(() => depart.setDate(arrive.getDate())); +</script> +{% endblock %} diff --git a/app/planner/templates/planner/list.html b/app/planner/templates/planner/list.html new file mode 100644 index 0000000..e212867 --- /dev/null +++ b/app/planner/templates/planner/list.html @@ -0,0 +1,15 @@ +{% extends 'planner/base.html' %} +{% load typogrify_tags %} +{% block pagetitle %}Luxagraf - Camping{% endblock %} + {% block content %} + <div class="camp-list"> + {% for object in object_list %} + <div class="distance"><a href="{{object.map_link}}" target="_blank">{{object.distance_from_previous|floatformat:"0"}} Miles</a></div> + <article> + <h3 class="campname">{{object.name}}</h3> + <h6 class="date">Arrive: {{object.date_arrive|date:"D"}} Afternoon, {{object.date_arrive|date:"M j, Y"}}</h6> + <h6 class="date">Depart: {{object.date_depart|date:"D"}} Morning, {{object.date_depart|date:"M j, Y"}}</h6> + <div class="notes">{{object.notes_html|safe|urlize}}</div> + </article>{% endfor %} + </div> + {% endblock %} diff --git a/app/planner/urls.py b/app/planner/urls.py new file mode 100644 index 0000000..b9bd2b1 --- /dev/null +++ b/app/planner/urls.py @@ -0,0 +1,19 @@ +from django.urls import path, re_path + +from . import views + +app_name = "luxplanner" + +urlpatterns = [ + re_path( + r'camp/add', + views.PlannerModelFormView.as_view(), + name="create" + ), + re_path( + r'', + views.PlannerListView.as_view(), + {'page':1}, + name="list" + ), +] diff --git a/app/planner/views.py b/app/planner/views.py new file mode 100644 index 0000000..b89506a --- /dev/null +++ b/app/planner/views.py @@ -0,0 +1,44 @@ +from django.views.generic import ListView +from django.views.generic.detail import DetailView +from django.views.generic.edit import CreateView, UpdateView +from django.urls import reverse +from django.utils import timezone +from django.conf import settings + +from utils.views import PaginatedListView +from .models import Camp + + +class PlannerListView(PaginatedListView): + model = Camp + template_name = 'planner/list.html' + + def get_context_data(self, **kwargs): + # Call the base implementation first to get a context + context = super(PlannerListView, self).get_context_data(**kwargs) + return context + + def get_queryset(self): + queryset = super(PlannerListView, self).get_queryset() + return queryset.filter(date_arrive__gte=timezone.now()) + + +class PlannerModelFormView(CreateView): + model = Camp + fields = [ + 'name', + 'date_arrive', + 'date_depart', + 'distance_from_previous', + 'notes', + 'link', + 'price', + 'site_number', + 'has_water', + 'has_electric', + 'has_dump', + 'point', + ] + success_url = '/planner/' + template_name = 'planner/create_form.html' + |