summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/planner/__init__.py0
-rw-r--r--app/planner/admin.py37
-rw-r--r--app/planner/migrations/0001_initial.py42
-rw-r--r--app/planner/migrations/0002_auto_20210725_1548.py22
-rw-r--r--app/planner/migrations/__init__.py0
-rw-r--r--app/planner/models.py108
-rw-r--r--app/planner/templates/planner/base.html25
-rw-r--r--app/planner/templates/planner/create_form.html42
-rw-r--r--app/planner/templates/planner/list.html15
-rw-r--r--app/planner/urls.py19
-rw-r--r--app/planner/views.py44
-rw-r--r--config/base_urls.py1
12 files changed, 355 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'
+
diff --git a/config/base_urls.py b/config/base_urls.py
index c749cfe..e77f4f5 100644
--- a/config/base_urls.py
+++ b/config/base_urls.py
@@ -34,6 +34,7 @@ urlpatterns = [
#path(r'admin/income/invoice/monthlyview/<str:slug>/', MonthlyInvoiceView.as_view(), name="monthly-invoice"),
path(r'admin/', admin.site.urls),
path(r'trading/', include('trading.urls')),
+ path(r'planner/', include('planner.urls')),
path(r'luximages/insert/', utils.views.insert_image),
path(r'luxproduct/insert/', products.views.insert_products),
path(r'feed.xml', PostRSSFeedView(),name="feed"),