Use django-recurrence for recurrence fields
This commit is contained in:
parent
6918a3d497
commit
66c1ef629b
2
Pipfile
2
Pipfile
@ -4,7 +4,6 @@ verify_ssl = true
|
|||||||
name = "pypi"
|
name = "pypi"
|
||||||
|
|
||||||
[packages]
|
[packages]
|
||||||
python-dateutil = "*"
|
|
||||||
django = "*"
|
django = "*"
|
||||||
django-widget-tweaks = "*"
|
django-widget-tweaks = "*"
|
||||||
django-auth-ldap = "*"
|
django-auth-ldap = "*"
|
||||||
@ -12,6 +11,7 @@ django-markdownx = "*"
|
|||||||
django-markdownify = "*"
|
django-markdownify = "*"
|
||||||
uvicorn = "*"
|
uvicorn = "*"
|
||||||
mysqlclient = "*"
|
mysqlclient = "*"
|
||||||
|
django-recurrence = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
|
12
Pipfile.lock
generated
12
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "cf9669556a14beab13e75133255258d1c446c321e486d27def845667bfaec370"
|
"sha256": "909b6de0a11b2a6648f920e4bf5a8c191e482bc956a0c22965a0c65cb06a642b"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -72,6 +72,14 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==3.0.1"
|
"version": "==3.0.1"
|
||||||
},
|
},
|
||||||
|
"django-recurrence": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:715f681f6af029ff3a8d73c7b1460abd8cbc5d5a5001efcb127032e84d9cb963",
|
||||||
|
"sha256:9053b44b78b7fbfe3530673edfdd6d2f562105f8a192bc6a4b906a3df4f95f59"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==1.10.3"
|
||||||
|
},
|
||||||
"django-widget-tweaks": {
|
"django-widget-tweaks": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:9f91ca4217199b7671971d3c1f323a2bec71a0c27dec6260b3c006fa541bc489",
|
"sha256:9f91ca4217199b7671971d3c1f323a2bec71a0c27dec6260b3c006fa541bc489",
|
||||||
@ -203,7 +211,7 @@
|
|||||||
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
|
||||||
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
|
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
|
||||||
],
|
],
|
||||||
"index": "pypi",
|
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||||
"version": "==2.8.1"
|
"version": "==2.8.1"
|
||||||
},
|
},
|
||||||
"python-ldap": {
|
"python-ldap": {
|
||||||
|
@ -26,6 +26,7 @@ INSTALLED_APPS = [
|
|||||||
'widget_tweaks',
|
'widget_tweaks',
|
||||||
'markdownx',
|
'markdownx',
|
||||||
'markdownify',
|
'markdownify',
|
||||||
|
'recurrence',
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
|
31
tasks/migrations/0003_task_recurrence.py
Normal file
31
tasks/migrations/0003_task_recurrence.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 3.2.3 on 2021-05-19 21:46
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import recurrence
|
||||||
|
import recurrence.fields
|
||||||
|
|
||||||
|
def transfer_recurrence(apps, schema_editor):
|
||||||
|
Task = apps.get_model('tasks', 'task')
|
||||||
|
for task in Task.objects.all():
|
||||||
|
task.recurrence = recurrence.deserialize('RRULE:' + task.recurrence_interval)
|
||||||
|
task.save(update_fields=['recurrence'])
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('tasks', '0002_tool_slug'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='task',
|
||||||
|
name='recurrence',
|
||||||
|
field=recurrence.fields.RecurrenceField(default=''),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.RunPython(transfer_recurrence),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='task',
|
||||||
|
name='recurrence_interval',
|
||||||
|
),
|
||||||
|
]
|
@ -1,11 +1,11 @@
|
|||||||
from datetime import datetime
|
import datetime
|
||||||
|
|
||||||
from dateutil.rrule import rrulestr
|
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from markdownx.models import MarkdownxField
|
from markdownx.models import MarkdownxField
|
||||||
|
from recurrence.fields import RecurrenceField
|
||||||
|
|
||||||
class Tool(models.Model):
|
class Tool(models.Model):
|
||||||
name = models.CharField(max_length=200)
|
name = models.CharField(max_length=200)
|
||||||
@ -24,7 +24,7 @@ class Task(models.Model):
|
|||||||
slug = models.SlugField()
|
slug = models.SlugField()
|
||||||
tool = models.ForeignKey(Tool, on_delete=models.CASCADE)
|
tool = models.ForeignKey(Tool, on_delete=models.CASCADE)
|
||||||
description = MarkdownxField(blank=True)
|
description = MarkdownxField(blank=True)
|
||||||
recurrence_interval = models.CharField(max_length=200)
|
recurrence = RecurrenceField(include_dtstart=False)
|
||||||
recurrence_base = models.DateField(null=True, blank=True)
|
recurrence_base = models.DateField(null=True, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -42,18 +42,20 @@ class Task(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def next_recurrence(self):
|
def next_recurrence(self):
|
||||||
|
def _date_to_datetime(date):
|
||||||
|
return datetime.datetime.combine(date, datetime.time.min)
|
||||||
if self.recurrence_base is None: # relative date
|
if self.recurrence_base is None: # relative date
|
||||||
try:
|
try:
|
||||||
rrule = rrulestr(self.recurrence_interval, dtstart=self.last_event.date)
|
return self.recurrence.after(
|
||||||
return rrule[1]
|
_date_to_datetime(self.last_event.date),
|
||||||
|
dtstart=_date_to_datetime(self.last_event.date))
|
||||||
except Event.DoesNotExist:
|
except Event.DoesNotExist:
|
||||||
return None
|
return None
|
||||||
else: # absolute date
|
else: # absolute date
|
||||||
rrule = rrulestr(self.recurrence_interval, dtstart=self.recurrence_base)
|
|
||||||
try:
|
try:
|
||||||
return rrule.after(self.last_event.date)
|
return self.recurrence.after(_date_to_datetime(self.last_event.date))
|
||||||
except Event.DoesNotExist:
|
except Event.DoesNotExist:
|
||||||
return rrule[1]
|
return self.recurrence.occurrences()[0]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_overdue(self):
|
def is_overdue(self):
|
||||||
@ -61,7 +63,7 @@ class Task(models.Model):
|
|||||||
if next_rec is None:
|
if next_rec is None:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return next_rec < datetime.now()
|
return next_rec < datetime.datetime.now()
|
||||||
|
|
||||||
|
|
||||||
class SubscriptionSettings(models.Model):
|
class SubscriptionSettings(models.Model):
|
||||||
@ -104,7 +106,7 @@ class GroupTaskSubscription(SubscriptionSettings):
|
|||||||
next_recurrence = self.task.next_recurrence
|
next_recurrence = self.task.next_recurrence
|
||||||
if next_recurrence is None:
|
if next_recurrence is None:
|
||||||
return False
|
return False
|
||||||
time_until_overdue = next_recurrence - datetime.now()
|
time_until_overdue = next_recurrence - datetime.datetime.now()
|
||||||
return self.task.is_overdue or (time_until_overdue.days <= self.days_before)
|
return self.task.is_overdue or (time_until_overdue.days <= self.days_before)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -14,14 +14,26 @@
|
|||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<p> Next scheduled time: {{ task.next_recurrence|date }} </p>
|
<section>
|
||||||
|
<h2> Recurrence </h2>
|
||||||
|
<ul>
|
||||||
|
{% for rule in task.recurrence.rrules %}
|
||||||
|
<li> {{ rule.to_text }} </li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p> Next scheduled time: {{ task.next_recurrence|date|default:"never" }} </p>
|
||||||
{% if task.is_overdue %}
|
{% if task.is_overdue %}
|
||||||
<div class="alert alert-danger">
|
<div class="alert alert-danger">
|
||||||
Task is overdue!
|
Task is overdue!
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2> Description </h2>
|
||||||
{{ task.description|markdownify }}
|
{{ task.description|markdownify }}
|
||||||
|
</section>
|
||||||
|
|
||||||
{% if form.errors %}
|
{% if form.errors %}
|
||||||
<div class="alert alert-warning">
|
<div class="alert alert-warning">
|
||||||
|
Loading…
Reference in New Issue
Block a user