Add first version of a wishlist app - create wishes in the admin for now. Fix a few unrelated things while here: update our date and time formats to be more internationally unambigous, and always show timezone. Also fix an old bug which meant that bornhack.dk/program redirect never worked. (#445)
This commit is contained in:
parent
575324f3cc
commit
95b41bdd44
|
@ -50,6 +50,7 @@ INSTALLED_APPS = [
|
||||||
"tokens",
|
"tokens",
|
||||||
"feedback",
|
"feedback",
|
||||||
"economy",
|
"economy",
|
||||||
|
"wishlist",
|
||||||
"allauth",
|
"allauth",
|
||||||
"allauth.account",
|
"allauth.account",
|
||||||
"allauth_2fa",
|
"allauth_2fa",
|
||||||
|
@ -69,10 +70,10 @@ LANGUAGE_CODE = "en-us"
|
||||||
# USE_I18N = True
|
# USE_I18N = True
|
||||||
# USE_L10N = True
|
# USE_L10N = True
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
SHORT_DATE_FORMAT = "d/m-Y"
|
SHORT_DATE_FORMAT = "Ymd"
|
||||||
DATE_FORMAT = "d/m-Y"
|
DATE_FORMAT = "l, M jS, Y"
|
||||||
DATETIME_FORMAT = "d/m-Y H:i"
|
DATETIME_FORMAT = "l, M jS, Y, H:i (e)"
|
||||||
TIME_FORMAT = "H:i"
|
TIME_FORMAT = "H:i (e)"
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -63,7 +63,7 @@ urlpatterns = [
|
||||||
path(
|
path(
|
||||||
"program/",
|
"program/",
|
||||||
CampRedirectView.as_view(),
|
CampRedirectView.as_view(),
|
||||||
kwargs={"page": "schedule_index"},
|
kwargs={"page": "program:schedule_index"},
|
||||||
name="schedule_index_redirect",
|
name="schedule_index_redirect",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
|
@ -84,6 +84,12 @@ urlpatterns = [
|
||||||
kwargs={"page": "village_list"},
|
kwargs={"page": "village_list"},
|
||||||
name="village_list_redirect",
|
name="village_list_redirect",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"wishlist/",
|
||||||
|
CampRedirectView.as_view(),
|
||||||
|
kwargs={"page": "wishlist:list"},
|
||||||
|
name="wish_list_redirect",
|
||||||
|
),
|
||||||
path("people/", PeopleView.as_view(), name="people"),
|
path("people/", PeopleView.as_view(), name="people"),
|
||||||
# camp specific urls below here
|
# camp specific urls below here
|
||||||
path(
|
path(
|
||||||
|
@ -129,6 +135,7 @@ urlpatterns = [
|
||||||
path("backoffice/", include("backoffice.urls", namespace="backoffice")),
|
path("backoffice/", include("backoffice.urls", namespace="backoffice")),
|
||||||
path("feedback/", FeedbackCreate.as_view(), name="feedback"),
|
path("feedback/", FeedbackCreate.as_view(), name="feedback"),
|
||||||
path("economy/", include("economy.urls", namespace="economy")),
|
path("economy/", include("economy.urls", namespace="economy")),
|
||||||
|
path("wishlist/", include("wishlist.urls", namespace="wishlist")),
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -18,7 +18,7 @@ News | {{ block.super }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% for item in news_items %}
|
{% for item in news_items %}
|
||||||
<div>
|
<div>
|
||||||
<h3><a href="{% url 'news:detail' slug=item.slug %}">{{ item.title }}</a> <small>{{ item.published_at|date:"Y-m-d" }}</small></h3>
|
<h3><a href="{% url 'news:detail' slug=item.slug %}">{{ item.title }}</a> <small>{{ item.published_at }}</small></h3>
|
||||||
</div>
|
</div>
|
||||||
{{ item.content|trustedcommonmark }}
|
{{ item.content|trustedcommonmark }}
|
||||||
{% if not forloop.last %}
|
{% if not forloop.last %}
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
</li>
|
</li>
|
||||||
<li><a href="{% url 'contact' %}">Contact</a></li>
|
<li><a href="{% url 'contact' %}">Contact</a></li>
|
||||||
<li><a href="{% url 'people' %}">People</a></li>
|
<li><a href="{% url 'people' %}">People</a></li>
|
||||||
|
<li><a href="{% url 'wish_list_redirect' %}">Wishlist</a></li>
|
||||||
{% if request.user.is_staff %}
|
{% if request.user.is_staff %}
|
||||||
<li><a href="{% url 'admin:index' %}">Django Admin</a></li>
|
<li><a href="{% url 'admin:index' %}">Django Admin</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
0
src/wishlist/__init__.py
Normal file
0
src/wishlist/__init__.py
Normal file
8
src/wishlist/admin.py
Normal file
8
src/wishlist/admin.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import Wish
|
||||||
|
|
||||||
|
@admin.register(Wish)
|
||||||
|
class WishAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
5
src/wishlist/apps.py
Normal file
5
src/wishlist/apps.py
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class WishlistConfig(AppConfig):
|
||||||
|
name = 'wishlist'
|
33
src/wishlist/migrations/0001_initial.py
Normal file
33
src/wishlist/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# Generated by Django 3.0.3 on 2020-02-09 18:24
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('teams', '0051_auto_20190312_1129'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Wish',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('created', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated', models.DateTimeField(auto_now=True)),
|
||||||
|
('name', models.CharField(help_text='Short description of the wish', max_length=100)),
|
||||||
|
('slug', models.SlugField(help_text='The url slug for this wish')),
|
||||||
|
('description', models.TextField(help_text='Description of the needed item. Markdown is supported!')),
|
||||||
|
('count', models.IntegerField(default=1, help_text='How many do we need?')),
|
||||||
|
('fulfilled', models.BooleanField(default=False, help_text='A Wish is marked as fulfilled when we no longer need the thing.')),
|
||||||
|
('team', models.ForeignKey(help_text='The team that needs this thing. When in doubt pick Orga :)', on_delete=django.db.models.deletion.PROTECT, related_name='wishes', to='teams.Team')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
0
src/wishlist/migrations/__init__.py
Normal file
0
src/wishlist/migrations/__init__.py
Normal file
66
src/wishlist/models.py
Normal file
66
src/wishlist/models.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
from django.db import models
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils.text import slugify
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
|
from utils.models import CampRelatedModel
|
||||||
|
|
||||||
|
|
||||||
|
class Wish(CampRelatedModel):
|
||||||
|
"""
|
||||||
|
This model contains the stuff BornHack needs. This can be anything from kitchen equipment
|
||||||
|
to network cables, or anything really.
|
||||||
|
"""
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=100,
|
||||||
|
help_text="Short description of the wish",
|
||||||
|
)
|
||||||
|
|
||||||
|
slug = models.SlugField(
|
||||||
|
blank=True,
|
||||||
|
help_text="The url slug for this wish. Leave blank to autogenerate one.",
|
||||||
|
)
|
||||||
|
|
||||||
|
description = models.TextField(
|
||||||
|
help_text="Description of the needed item. Markdown is supported!"
|
||||||
|
)
|
||||||
|
|
||||||
|
count = models.IntegerField(
|
||||||
|
default=1,
|
||||||
|
help_text="How many do we need?",
|
||||||
|
)
|
||||||
|
|
||||||
|
fulfilled = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="A Wish is marked as fulfilled when we no longer need the thing.",
|
||||||
|
)
|
||||||
|
|
||||||
|
team = models.ForeignKey(
|
||||||
|
"teams.Team",
|
||||||
|
help_text="The team that needs this thing. When in doubt pick Orga :)",
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
related_name="wishes",
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def camp(self):
|
||||||
|
return self.team.camp
|
||||||
|
|
||||||
|
camp_filter = "team__camp"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def save(self, **kwargs):
|
||||||
|
print("inside save()")
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = slugify(self.name)
|
||||||
|
if not self.slug:
|
||||||
|
raise ValidationError("Unable to slugify")
|
||||||
|
super().save(**kwargs)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse("wishlist:detail", kwargs={
|
||||||
|
"camp_slug": self.camp.slug,
|
||||||
|
"wish_slug": self.slug,
|
||||||
|
})
|
19
src/wishlist/templates/wish_detail.html
Normal file
19
src/wishlist/templates/wish_detail.html
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load commonmark %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
Wish List | {{ block.super }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>BornHack Wish: {{ wish.name }}</h2>
|
||||||
|
|
||||||
|
<p class="lead">{{ wish.description }}</p>
|
||||||
|
|
||||||
|
<p class="lead">Number needed: {{ wish.count }}</p>
|
||||||
|
|
||||||
|
<p class="lead"><i>This wish was created by the <a href="{% url 'teams:general' camp_slug=wish.team.camp.slug team_slug=wish.team.slug %}">{{ wish.team.name }} Team</a> for {{ wish.team.camp.title }} on {{ wish.created }}. If you can help please visit their <a href="{% url 'teams:general' camp_slug=wish.camp.slug team_slug=wish.team.slug %}">team page</a> for contact info!</i></p>
|
||||||
|
|
||||||
|
<a class="btn btn-primary" href="{% url 'wish_list_redirect' %}"><i class="fas fa-undo"></i> Back to Wish List</a>
|
||||||
|
{% endblock %}
|
43
src/wishlist/templates/wish_list.html
Normal file
43
src/wishlist/templates/wish_list.html
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load static %}
|
||||||
|
{% load commonmark %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
Wish List | {{ block.super }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<h2>BornHack Wishlist</h2>
|
||||||
|
|
||||||
|
<p class="lead">
|
||||||
|
This is a list of things the different BornHack teams need. If you own
|
||||||
|
something on the list and you would like to donate it to BornHack then please
|
||||||
|
don't hesitate to contact the team in question. Partial donations are also welcome,
|
||||||
|
so if a team needs 10 of something and you have 5 then we still want to hear from you.
|
||||||
|
When we no longer need something it will be removed from this list.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<table class="table table-hover table-striped">
|
||||||
|
<tr>
|
||||||
|
<th>Wish</th>
|
||||||
|
<th>Team</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Number</th>
|
||||||
|
<th>Created</th>
|
||||||
|
</tr>
|
||||||
|
{% for wish in wish_list %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="{% url 'wishlist:detail' camp_slug=camp.slug wish_slug=wish.slug %}">{{ wish.name }}</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ wish.team.name }} Team</td>
|
||||||
|
<td>{{ wish.description|trustedcommonmark }}</td>
|
||||||
|
<td>{{ wish.count }}</td>
|
||||||
|
<td>{{ wish.created }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% endblock %}
|
3
src/wishlist/tests.py
Normal file
3
src/wishlist/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
9
src/wishlist/urls.py
Normal file
9
src/wishlist/urls.py
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
from django.urls import path
|
||||||
|
from .views import WishListView, WishDetailView
|
||||||
|
|
||||||
|
|
||||||
|
app_name = "wishlist"
|
||||||
|
urlpatterns = [
|
||||||
|
path("", WishListView.as_view(), name="list"),
|
||||||
|
path("<slug:wish_slug>/", WishDetailView.as_view(), name="detail"),
|
||||||
|
]
|
16
src/wishlist/views.py
Normal file
16
src/wishlist/views.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.views.generic import ListView, DetailView
|
||||||
|
|
||||||
|
from .models import Wish
|
||||||
|
|
||||||
|
|
||||||
|
class WishListView(ListView):
|
||||||
|
model = Wish
|
||||||
|
template_name = "wish_list.html"
|
||||||
|
|
||||||
|
|
||||||
|
class WishDetailView(DetailView):
|
||||||
|
model = Wish
|
||||||
|
template_name = "wish_detail.html"
|
||||||
|
slug_url_kwarg = "wish_slug"
|
||||||
|
|
Loading…
Reference in a new issue