Dreams module completed (?)
This commit is contained in:
parent
4784185e99
commit
fc7d56680a
18 changed files with 705 additions and 5 deletions
16
TODO.md
16
TODO.md
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
## mood module
|
## mood module
|
||||||
|
|
||||||
|
[_] Missing mood views
|
||||||
[ ] Statistics / graphs
|
[ ] Statistics / graphs
|
||||||
|
|
||||||
## cbt module
|
## cbt module
|
||||||
|
@ -16,7 +17,14 @@
|
||||||
|
|
||||||
## dreams module
|
## dreams module
|
||||||
|
|
||||||
[ ] New dream page
|
[x] New dream page
|
||||||
[ ] Edit dream page
|
[x] Edit dream page
|
||||||
[ ] Delete dream page
|
[x] Delete dream page
|
||||||
[ ] Statistics / template tags
|
[ ] Statistics / template tags
|
||||||
|
[ ] Check for copy-paste errors in pages
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
|
||||||
|
[ ] Better icons for sidebar items
|
||||||
|
[ ] Sidebar auto-generation (?)
|
||||||
|
[ ] Fix sidebar highlighting for pages with same name
|
17
dreams/forms.py
Normal file
17
dreams/forms.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
from django.forms import ModelForm, ModelMultipleChoiceField
|
||||||
|
|
||||||
|
from multiupload.fields import MultiFileField
|
||||||
|
|
||||||
|
from .models import Dream, Theme
|
||||||
|
|
||||||
|
class DreamForm(ModelForm):
|
||||||
|
uploads = MultiFileField(required=False)
|
||||||
|
themes = ModelMultipleChoiceField(queryset=Theme.objects.all())
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Dream
|
||||||
|
fields = ["timestamp", "mood", "title", "content", 'type', 'wet', 'lucid']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields["mood"].required = False
|
|
@ -14,6 +14,9 @@ class Theme(models.Model):
|
||||||
icon = models.CharField(default="fas fa-bed", max_length=64)
|
icon = models.CharField(default="fas fa-bed", max_length=64)
|
||||||
color = ColorField(default="#000000")
|
color = ColorField(default="#000000")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
class Dream(models.Model):
|
class Dream(models.Model):
|
||||||
class DreamTypes(models.IntegerChoices):
|
class DreamTypes(models.IntegerChoices):
|
||||||
NIGHT = 0, 'Night (main) sleep'
|
NIGHT = 0, 'Night (main) sleep'
|
||||||
|
@ -21,6 +24,7 @@ class Dream(models.Model):
|
||||||
NAP = 2, 'Napping'
|
NAP = 2, 'Napping'
|
||||||
|
|
||||||
user = models.ForeignKey(get_user_model(), models.CASCADE)
|
user = models.ForeignKey(get_user_model(), models.CASCADE)
|
||||||
|
timestamp = models.DateTimeField(default=timezone.now)
|
||||||
title = models.CharField(max_length=64)
|
title = models.CharField(max_length=64)
|
||||||
content = models.TextField()
|
content = models.TextField()
|
||||||
type = models.IntegerField(choices=DreamTypes.choices)
|
type = models.IntegerField(choices=DreamTypes.choices)
|
||||||
|
@ -28,6 +32,14 @@ class Dream(models.Model):
|
||||||
lucid = models.BooleanField(default=False)
|
lucid = models.BooleanField(default=False)
|
||||||
wet = models.BooleanField(default=False)
|
wet = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def short_text(self):
|
||||||
|
return self.title or self.content[:64]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def theme_set(self):
|
||||||
|
return [theme.theme for theme in self.dreamtheme_set.all()]
|
||||||
|
|
||||||
class DreamTheme(models.Model):
|
class DreamTheme(models.Model):
|
||||||
dream = models.ForeignKey(Dream, models.CASCADE)
|
dream = models.ForeignKey(Dream, models.CASCADE)
|
||||||
theme = models.ForeignKey(Theme, models.CASCADE)
|
theme = models.ForeignKey(Theme, models.CASCADE)
|
||||||
|
|
16
dreams/templates/dreams/dream_delete.html
Normal file
16
dreams/templates/dreams/dream_delete.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% block "content" %}
|
||||||
|
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Delete {{ object }}</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<p>Are you sure you wish to delete this dream? This cannot be undone.</p>
|
||||||
|
<form action="" method="POST">{% csrf_token %}<button class="form-control btn-danger" type="submit">Delete Dream</button></form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
60
dreams/templates/dreams/dream_edit.html
Normal file
60
dreams/templates/dreams/dream_edit.html
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% block "content" %}
|
||||||
|
|
||||||
|
{% if form.errors %}
|
||||||
|
{% for field in form %}
|
||||||
|
{% for error in field.errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{{ field.name }}: {{ error }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Edit {{ object.title }}</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="" method="POST" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table>
|
||||||
|
<tr><th><label for="id_timestamp">Timestamp:</label></th><td><input type="text" name="timestamp" value="{% if object.timestamp %}{{ object.timestamp | date:"Y-m-d H:i" }}{% else %}{% now "Y-m-d H:i" %}{% endif %}" required id="id_timestamp"><input type="hidden" name="initial-timestamp" value="2020-12-26 19:24:14" id="initial-id_timestamp"></td></tr>
|
||||||
|
<tr><th><label for="id_type">Type:</label></th><td><select name="type" required="" id="id_type">
|
||||||
|
{% for value, text in form.type.field.choices %}
|
||||||
|
<option value="{{ value }}" {% if object.type == value %}selected{% endif %}>{{ text }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</select></td></tr>
|
||||||
|
<tr><th><label for="id_title">Title:</label></th><td><input value="{{ object.title }}" type="text" name="title" maxlength="64" id="id_title"></td></tr>
|
||||||
|
|
||||||
|
<tr><th><label for="id_content">Text:</label></th><td><textarea name="content" cols="40" rows="10" id="id_content">{{ object.content }}</textarea></td></tr>
|
||||||
|
|
||||||
|
<tr><th><label for="id_mood">Mood:</label></th><td><select name="mood" class="dtb" required id="id_mood">
|
||||||
|
<option value="" {% if not object.mood %}selected{% endif %}>---------</option>
|
||||||
|
|
||||||
|
{% for mood in request.user.mood_set.all|dictsort:"value" %}
|
||||||
|
<option data-icon="{{ mood.icon }}" value="{{ mood.id }}" {% if object.mood == mood %}selected{% endif %}>{{ mood }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</select></td></tr>
|
||||||
|
|
||||||
|
|
||||||
|
<tr><th><label for="id_lucid">Lucid:</label></th><td><input type="checkbox" {% if object.lucid %}checked{% endif %} name="lucid"> <i class="fas fa-exclamation"></i> Lucid Dream?<br></td></tr>
|
||||||
|
<tr><th><label for="id_lucid">Wet:</label></th><td><input type="checkbox" {% if object.wet %}checked{% endif %} name="wet"> <i class="fas fa-tint"></i> Wet Dream?<br></td></tr>
|
||||||
|
|
||||||
|
<tr><th><label for="activities">Themes:</label></th><td>
|
||||||
|
{% for theme in request.user.theme_set.all %}
|
||||||
|
<input type="checkbox" {% if theme in object.theme_set %}checked{% endif %} value="{{ theme.id }}" name="themes"> <i class="{{ theme.icon }}" style="color:{{ theme.color }};"></i> {{ theme }}<br>
|
||||||
|
{% endfor %}
|
||||||
|
</td></tr>
|
||||||
|
|
||||||
|
<tr><th><label for="id_uploads">Uploads:</label></th><td><input type="file" name="uploads" id="id_uploads" multiple="multiple"></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<button style="margin-top: 20px;" class="form-control btn-primary" type="submit">Save</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
34
dreams/templates/dreams/dream_list.html
Normal file
34
dreams/templates/dreams/dream_list.html
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% block "content" %}
|
||||||
|
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Your Dreams</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table data-order="[[ 0, "desc" ]]" class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Timestamp</th>
|
||||||
|
<th>Mood</th>
|
||||||
|
<th>Themes</th>
|
||||||
|
<th>Title</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for dream in object_list %}
|
||||||
|
<tr onclick="window.location.href='{% url "dreams:dream_view" dream.id %}'">
|
||||||
|
<td data-order="{{ dream.timestamp | date:"U" }}">{{ dream.timestamp }}</td>
|
||||||
|
<td><i data-order="{{ dream.mood.value }}" style="color:{{ dream.mood.color }};" class="{{ dream.mood.icon }}"></i> {{ dream.mood }}</td>
|
||||||
|
<td>{% for theme in dream.dreamtheme_set.all %}<i title="{{ theme.theme }}{% if theme.comment %} - {{ theme.comment }}{% endif %}" style="color:{{ theme.theme.color }};" class="{{ theme.theme.icon }}"></i> {% endfor %}</td>
|
||||||
|
<td>{{ dream.short_text }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
70
dreams/templates/dreams/dream_view.html
Normal file
70
dreams/templates/dreams/dream_view.html
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% block "content" %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<!-- Entry details -->
|
||||||
|
<div class="col-xl-8 col-lg-7">
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<!-- Card Header - Dropdown -->
|
||||||
|
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Dream Entry</h6>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- Card Body -->
|
||||||
|
<div class="card-body">
|
||||||
|
<p><i>{{ object.timestamp }}</i></p>
|
||||||
|
<h3>{{ object.title }}</h3>
|
||||||
|
<blockquote>{{ object.content|linebreaks }}</blockquote>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mood -->
|
||||||
|
<div class="col-xl-4 col-lg-5">
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<!-- Card Header - Dropdown -->
|
||||||
|
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Mood</h6>
|
||||||
|
</div>
|
||||||
|
<!-- Card Body -->
|
||||||
|
<div class="card-body">
|
||||||
|
<i title="{{ object.mood }}" style="color: {{ object.mood.color }}; font-size: 200px;" title="" class="{{ object.mood.icon }}"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Themes</h6>
|
||||||
|
</div>
|
||||||
|
<div class="py-3">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row" id="cards">
|
||||||
|
{% for theme in object.dreamtheme_set.all %}
|
||||||
|
<div class="card">
|
||||||
|
<i title="{{ theme.theme }}" style="display: inline; color: {{ theme.theme.color }}; font-size: 50px;" title="" class="{{ theme.theme.icon }}"></i>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Attachments</h6>
|
||||||
|
</div>
|
||||||
|
<div class="py-3">
|
||||||
|
<div class="container">
|
||||||
|
<ul>
|
||||||
|
{% for attachment in object.dreammedia_set.all %}
|
||||||
|
<li><a href="{{ attachment.media.url }}">{{ attachment.media.name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
28
dreams/templates/dreams/mood_list.html
Normal file
28
dreams/templates/dreams/mood_list.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% block "content" %}
|
||||||
|
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Your Moods</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name <i>(Value)</i></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for mood in object_list %}
|
||||||
|
<tr onclick="window.location.href='{% url "mood:mood_edit" mood.id %}'">
|
||||||
|
<td><i data-order="{{ mood.value }}" style="color:{{ mood.color }};" class="{{ mood.icon }}"></i> {{ mood }} <i>({{ mood.value }})</i></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
16
dreams/templates/dreams/notification_delete.html
Normal file
16
dreams/templates/dreams/notification_delete.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% block "content" %}
|
||||||
|
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Delete {{ object.time }} Notification?</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<p>Are you sure you wish to delete this notification? This cannot be undone.</p>
|
||||||
|
<form action="" method="POST">{% csrf_token %}<button class="form-control btn-danger" type="submit">Delete Notification</button></form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
30
dreams/templates/dreams/notification_edit.html
Normal file
30
dreams/templates/dreams/notification_edit.html
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% block "content" %}
|
||||||
|
|
||||||
|
{% if form.errors %}
|
||||||
|
{% for field in form %}
|
||||||
|
{% for error in field.errors %}
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{{ field.name }}: {{ error }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Edit {{ object.time }}</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table>
|
||||||
|
<tr><th><label for="id_time">Time:</label></th><td><input type="time" name="time" value="{% if object.time %}{{ object.time|date:"H:i" }}{% else %}12:00{% endif %}" maxlength="64" required id="id_time"></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<button style="margin-top: 20px;" class="form-control btn-primary" type="submit">Save</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
28
dreams/templates/dreams/notification_list.html
Normal file
28
dreams/templates/dreams/notification_list.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% block "content" %}
|
||||||
|
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Your Daily Notifications</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Time</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for notification in object_list %}
|
||||||
|
<tr onclick="window.location.href='{% url "mood:notification_edit" notification.id %}'">
|
||||||
|
<td>{{ notification.time }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
16
dreams/templates/dreams/theme_delete.html
Normal file
16
dreams/templates/dreams/theme_delete.html
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% block "content" %}
|
||||||
|
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Delete {{ object }}</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<p>Are you sure you wish to delete {{ object }}? This cannot be undone. Mood entries that are associated with this activity will not be deleted.</p>
|
||||||
|
<form action="" method="POST">{% csrf_token %}<button class="form-control btn-danger" type="submit">Delete Activity</button></form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
30
dreams/templates/dreams/theme_edit.html
Normal file
30
dreams/templates/dreams/theme_edit.html
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% block "content" %}
|
||||||
|
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Edit {{ object.name }}</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form action="" method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table>
|
||||||
|
<tr><th><label for="id_name">Name:</label></th><td><input type="text" name="name" value="{{ object.name }}" maxlength="64" required id="id_name"></td></tr>
|
||||||
|
<tr><th><label for="id_icon">Icon:</label></th><td><input style="visibility: hidden;" class="icp icp-auto" type="text" name="icon" value="{{ object.icon }}" maxlength="64" required id="id_icon"></td></tr>
|
||||||
|
<tr><th><label for="id_color">Color:</label></th><td><input type="text"
|
||||||
|
id="id_color"
|
||||||
|
class="form-control colorfield_field jscolor"
|
||||||
|
name="color"
|
||||||
|
value="{{ object.color }}"
|
||||||
|
placeholder="{{ object.color }}"
|
||||||
|
data-jscolor="{hash:true,width:225,height:150,required:true}"
|
||||||
|
required /></td></tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<button style="margin-top: 20px;" class="form-control btn-primary" type="submit">Save</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
28
dreams/templates/dreams/theme_list.html
Normal file
28
dreams/templates/dreams/theme_list.html
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% block "content" %}
|
||||||
|
|
||||||
|
<div class="card shadow mb-4">
|
||||||
|
<div class="card-header py-3">
|
||||||
|
<h6 class="m-0 font-weight-bold text-primary">Your Themes</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for theme in object_list %}
|
||||||
|
<tr onclick="window.location.href='{% url "dreams:theme_edit" theme.id %}'">
|
||||||
|
<td><i data-order="{{ theme }}" style="color:{{ theme.color }};" class="{{ theme.icon }}"></i> {{ theme }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
21
dreams/urls.py
Normal file
21
dreams/urls.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from .views import DreamListView, DreamViewView, DreamDeleteView, DreamEditView, DreamCreateView, ThemeListView, ThemeEditView, ThemeCreateView, ThemeDeleteView, NotificationCreateView, NotificationDeleteView, NotificationEditView, NotificationListView
|
||||||
|
|
||||||
|
from django.urls import path, include
|
||||||
|
|
||||||
|
app_name = "dreams"
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', DreamListView.as_view(), name="dream_list"),
|
||||||
|
path('dream/<int:id>/view/', DreamViewView.as_view(), name="dream_view"),
|
||||||
|
path('dream/<int:id>/edit/', DreamEditView.as_view(), name="dream_edit"),
|
||||||
|
path('dream/<int:id>/delete/', DreamDeleteView.as_view(), name="dream_delete"),
|
||||||
|
path('dream/new/', DreamCreateView.as_view(), name="dream_create"),
|
||||||
|
path('theme/', ThemeListView.as_view(), name="theme_list"),
|
||||||
|
path('theme/<int:id>/edit/', ThemeEditView.as_view(), name="theme_edit"),
|
||||||
|
path('theme/new/', ThemeCreateView.as_view(), name="theme_create"),
|
||||||
|
path('theme/<int:id>/delete/', ThemeDeleteView.as_view(), name="theme_delete"),
|
||||||
|
path('notification/', NotificationListView.as_view(), name="notification_list"),
|
||||||
|
path('notification/<int:id>/edit/', NotificationEditView.as_view(), name="notification_edit"),
|
||||||
|
path('notification/<int:id>/delete/', NotificationDeleteView.as_view(), name="notification_delete"),
|
||||||
|
path('notification/new/', NotificationCreateView.as_view(), name="notification_create"),
|
||||||
|
]
|
258
dreams/views.py
258
dreams/views.py
|
@ -1,3 +1,259 @@
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
|
||||||
# Create your views here.
|
from django.views.generic import TemplateView, ListView, UpdateView, DetailView, CreateView, DeleteView
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
|
||||||
|
from .models import Dream, DreamTheme, DreamMedia, Theme
|
||||||
|
from .forms import DreamForm
|
||||||
|
|
||||||
|
from msgio.models import NotificationDailySchedule, Notification
|
||||||
|
|
||||||
|
class DreamListView(LoginRequiredMixin, ListView):
|
||||||
|
template_name = "dreams/dream_list.html"
|
||||||
|
model = Dream
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["title"] = "Dream List"
|
||||||
|
context["subtitle"] = "A list of the dreams you have entered so far."
|
||||||
|
context["buttons"] = [(reverse_lazy("dreams:dream_create"), "New Dream", "plus")]
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Dream.objects.filter(user=self.request.user).order_by('timestamp')
|
||||||
|
|
||||||
|
|
||||||
|
class DreamViewView(LoginRequiredMixin, DetailView):
|
||||||
|
template_name = "dreams/dream_view.html"
|
||||||
|
model = Dream
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["title"] = "View Dream"
|
||||||
|
context["subtitle"] = "View the details of your dream."
|
||||||
|
context["buttons"] = [(reverse_lazy("dreams:dream_edit", kwargs={"id": self.kwargs["id"]}), "Edit Dream", "pen")]
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return get_object_or_404(Dream, user=self.request.user, id=self.kwargs["id"])
|
||||||
|
|
||||||
|
|
||||||
|
class DreamCreateView(LoginRequiredMixin, CreateView):
|
||||||
|
template_name = "dreams/dream_edit.html"
|
||||||
|
form_class = DreamForm
|
||||||
|
model = Dream
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["title"] = "Create Dream"
|
||||||
|
context["subtitle"] = "What did you dream?"
|
||||||
|
context["scripts"] = ["frontend/js/dropdown-to-buttons.js"]
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.instance.user = self.request.user
|
||||||
|
|
||||||
|
ret = super().form_valid(form)
|
||||||
|
|
||||||
|
for theme in form.cleaned_data["themes"]:
|
||||||
|
if theme.user == self.request.user:
|
||||||
|
DreamTheme.objects.create(theme=theme, dream=form.instance)
|
||||||
|
|
||||||
|
for attachment in form.cleaned_data["uploads"]:
|
||||||
|
DreamMedia.objects.create(dream=form.instance, media=attachment)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy("dreams:dream_view", kwargs={"id": self.object.id})
|
||||||
|
|
||||||
|
|
||||||
|
class DreamEditView(LoginRequiredMixin, UpdateView):
|
||||||
|
template_name = "dreams/dream_edit.html"
|
||||||
|
form_class = DreamForm
|
||||||
|
model = Dream
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["title"] = "Update Dream"
|
||||||
|
context["subtitle"] = "Change details of a dream you entered before."
|
||||||
|
context["scripts"] = ["frontend/js/dropdown-to-buttons.js"]
|
||||||
|
context["buttons"] = [(reverse_lazy("dreams:dream_delete", kwargs={"id": self.kwargs["id"]}), "Delete Dream", "trash-alt")]
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return get_object_or_404(Dream, user=self.request.user, id=self.kwargs["id"])
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
for theme in form.cleaned_data["themes"]:
|
||||||
|
if theme.user == self.request.user:
|
||||||
|
if not theme in form.instance.theme_set:
|
||||||
|
DreamTheme.objects.create(theme=theme, dream=form.instance)
|
||||||
|
|
||||||
|
for dreamtheme in form.instance.dreamtheme_set.all():
|
||||||
|
if not dreamtheme.theme in form.cleaned_data["themes"]:
|
||||||
|
dreamtheme.delete()
|
||||||
|
|
||||||
|
for upload in form.cleaned_data["uploads"]:
|
||||||
|
DreamMedia.objects.create(dream=form.instance, media=upload)
|
||||||
|
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy("dreams:dream_view", kwargs={"id": self.object.id})
|
||||||
|
|
||||||
|
|
||||||
|
class DreamDeleteView(LoginRequiredMixin, DeleteView):
|
||||||
|
template_name = "dreams/dream_delete.html"
|
||||||
|
model = Dream
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return get_object_or_404(Dream, user=self.request.user, id=self.kwargs["id"])
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy("dreams:dream_list")
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeListView(LoginRequiredMixin, ListView):
|
||||||
|
template_name = "dreams/theme_list.html"
|
||||||
|
model = Theme
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["title"] = "Themes"
|
||||||
|
context["subtitle"] = "The themes you have defined for your dreams."
|
||||||
|
context["buttons"] = [(reverse_lazy("dreams:theme_create"), "Create Theme", "pen")]
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Theme.objects.filter(user=self.request.user)
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeEditView(LoginRequiredMixin, UpdateView):
|
||||||
|
template_name = "dreams/theme_edit.html"
|
||||||
|
model = Theme
|
||||||
|
fields = ["name", "icon", "color"]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["title"] = "Edit Theme"
|
||||||
|
context["subtitle"] = "Make changes to the theme."
|
||||||
|
context["scripts"] = ["colorfield/jscolor/jscolor.js", "colorfield/colorfield.js", "frontend/js/fontawesome-iconpicker.min.js", "frontend/js/iconpicker-loader.js"]
|
||||||
|
context["styles"] = ["frontend/css/fontawesome-iconpicker.min.css"]
|
||||||
|
context["buttons"] = [(reverse_lazy("dreams:theme_delete", kwargs={"id": self.kwargs["id"]}), "Delete Theme", "trash-alt")]
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return get_object_or_404(Theme, user=self.request.user, id=self.kwargs["id"])
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy("dreams:theme_list")
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeCreateView(LoginRequiredMixin, CreateView):
|
||||||
|
template_name = "dreams/theme_edit.html"
|
||||||
|
model = Theme
|
||||||
|
fields = ["name", "icon", "color"]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["title"] = "Create Theme"
|
||||||
|
context["subtitle"] = "Add a new theme for your dreams."
|
||||||
|
context["scripts"] = ["colorfield/jscolor/jscolor.js", "colorfield/colorfield.js", "frontend/js/fontawesome-iconpicker.min.js", "frontend/js/iconpicker-loader.js"]
|
||||||
|
context["styles"] = ["frontend/css/fontawesome-iconpicker.min.css"]
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
obj = form.save(commit=False)
|
||||||
|
obj.user = self.request.user
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy("dreams:theme_list")
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeDeleteView(LoginRequiredMixin, DeleteView):
|
||||||
|
template_name = "dreams/theme_delete.html"
|
||||||
|
model = Theme
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return get_object_or_404(Theme, user=self.request.user, id=self.kwargs["id"])
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy("dreams:theme_list")
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationListView(LoginRequiredMixin, ListView):
|
||||||
|
template_name = "dreams/notification_list.html"
|
||||||
|
model = NotificationDailySchedule
|
||||||
|
fields = ["time"]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["title"] = "Notifications"
|
||||||
|
context["subtitle"] = "The daily reminders you have set up."
|
||||||
|
context["buttons"] = [(reverse_lazy("dreams:notification_create"), "New Notification", "plus")]
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return NotificationDailySchedule.objects.filter(notification__recipient=self.request.user, notification__app="dreams")
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationCreateView(LoginRequiredMixin, CreateView):
|
||||||
|
template_name = "dreams/notification_edit.html"
|
||||||
|
model = NotificationDailySchedule
|
||||||
|
fields = ["time"]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["title"] = "Create Notification"
|
||||||
|
context["subtitle"] = "Add a new daily notification."
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
notification = Notification.objects.create(content="What did you dream tonight? Go to %KUMIFYURL% to document your dreams!", recipient=self.request.user, app="dreams")
|
||||||
|
obj = form.save(commit=False)
|
||||||
|
obj.notification = notification
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy("dreams:notification_list")
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationEditView(LoginRequiredMixin, UpdateView):
|
||||||
|
template_name = "dreams/notification_edit.html"
|
||||||
|
model = NotificationDailySchedule
|
||||||
|
fields = ["time"]
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["title"] = "Edit Notification"
|
||||||
|
context["subtitle"] = "Change the time of a daily notification."
|
||||||
|
context["buttons"] = [(reverse_lazy("dreams:notification_delete", args=[self.kwargs["id"]]), "Delete Notification")]
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy("dreams:notification_list")
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return get_object_or_404(NotificationDailySchedule, notification__recipient=self.request.user, id=self.kwargs["id"])
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationDeleteView(LoginRequiredMixin, DeleteView):
|
||||||
|
template_name = "dreams/notification_delete.html"
|
||||||
|
model = NotificationDailySchedule
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
return get_object_or_404(NotificationDailySchedule, notification__recipient=self.request.user, id=self.kwargs["id"])
|
||||||
|
|
||||||
|
def delete(self, request, *args, **kwargs):
|
||||||
|
self.object = self.get_object()
|
||||||
|
success_url = self.get_success_url()
|
||||||
|
self.object.notification.delete()
|
||||||
|
return HttpResponseRedirect(success_url)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy("dreams:notification_list")
|
|
@ -55,6 +55,35 @@
|
||||||
<span>Notifications</span></a>
|
<span>Notifications</span></a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<!-- Divider -->
|
||||||
|
<hr class="sidebar-divider">
|
||||||
|
|
||||||
|
<!-- Heading -->
|
||||||
|
<div class="sidebar-heading">
|
||||||
|
Dreams
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Nav Item - Dream List -->
|
||||||
|
<li class="nav-item {% if title == "Dream List" %}active{% endif %}">
|
||||||
|
<a class="nav-link" href="{% url "dreams:dream_list" %}">
|
||||||
|
<i class="fas fa-fw fa-smile"></i>
|
||||||
|
<span>Dream List</span></a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- Nav Item - Theme List -->
|
||||||
|
<li class="nav-item {% if title == "Themes" %}active{% endif %}">
|
||||||
|
<a class="nav-link" href="{% url "dreams:theme_list" %}">
|
||||||
|
<i class="fas fa-fw fa-smile"></i>
|
||||||
|
<span>Themes</span></a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- Nav Item - Notification List -->
|
||||||
|
<li class="nav-item {% if title == "Notifications" %}active{% endif %}">
|
||||||
|
<a class="nav-link" href="{% url "dreams:notification_list" %}">
|
||||||
|
<i class="fas fa-fw fa-smile"></i>
|
||||||
|
<span>Notifications</span></a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<!-- Divider -->
|
<!-- Divider -->
|
||||||
<hr class="sidebar-divider d-none d-md-block">
|
<hr class="sidebar-divider d-none d-md-block">
|
||||||
|
|
||||||
|
|
|
@ -25,4 +25,5 @@ urlpatterns = [
|
||||||
path('mood/', include("mood.urls", "mood")),
|
path('mood/', include("mood.urls", "mood")),
|
||||||
path('cron/', include("cronhandler.urls", "cron")),
|
path('cron/', include("cronhandler.urls", "cron")),
|
||||||
path('webhooks/telegram/', TelegramWebhookView.as_view()),
|
path('webhooks/telegram/', TelegramWebhookView.as_view()),
|
||||||
|
path('dreams/', include("dreams.urls", "dreams")),
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue