calendar-social/calsocial/calendar_system/gregorian.py
Gergely Polonkai 6078e6171f [Bugfix] Rework month padding and event fetching routines
Month padding (ie. adding the days of previous/next months) is now working as expected.  Fetching
multi-day events now also displays events correctly.
2018-07-23 08:00:06 +02:00

234 lines
6.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Calendar.social
# Copyright (C) 2018 Gergely Polonkai
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Gregorian calendar system for Calendar.social
"""
from datetime import datetime, timedelta
from functools import wraps
from flask_babelex import lazy_gettext as _
from . import CalendarSystem
def to_timestamp(func):
"""Decorator that converts the return value of a function from `datetime` to a UNIX timestamp
"""
@wraps(func)
def _decorator(*args, **kwargs):
return func(*args, **kwargs).timestamp()
return _decorator
class GregorianCalendar(CalendarSystem):
"""Gregorian calendar system for Calendar.social
"""
__name__ = _('Gregorian')
__has_months__ = True
START_DAY = 0
END_DAY = 7
month_names = (
_('January'),
_('February'),
_('March'),
_('April'),
_('May'),
_('June'),
_('July'),
_('August'),
_('September'),
_('October'),
_('November'),
_('December'),
)
day_names = (
_('Monday'),
_('Tuesday'),
_('Wednesday'),
_('Thursday'),
_('Friday'),
_('Saturday'),
_('Sunday'),
)
def __init__(self, timestamp):
self.timestamp = datetime.utcfromtimestamp(timestamp)
@property
def month(self):
return self.timestamp.strftime("%B, %Y")
@property
def days(self):
day_list = []
month_first = self.timestamp.replace(day=1)
if self.timestamp.month == 12:
month_last = month_first.replace(day=31)
else:
month_last = month_first.replace(month=month_first.month + 1) - timedelta(days=1)
pad_before = (7 - self.START_DAY + month_first.weekday()) % 7
pad_after = (6 - month_last.weekday() + self.START_DAY) % 7
first_display = month_first - timedelta(days=pad_before)
last_display = month_last + timedelta(days=pad_after)
current = first_display
while current <= last_display:
day_list.append(current)
current += timedelta(days=1)
return day_list
@property
@to_timestamp
def prev_year(self):
"""Returns the timestamp of the same date in the previous year
"""
return self.timestamp.replace(year=self.timestamp.year - 1)
@property
def prev_year_year(self):
"""The number of the previous year
"""
return self.timestamp.replace(year=self.timestamp.year - 1).year
@property
@to_timestamp
def prev_month(self):
"""Returns the timestamp of the same day in the previous month
"""
if self.timestamp.month == 1:
return self.prev_year.replace(month=12)
return self.timestamp.replace(month=self.timestamp.month - 1)
@property
def prev_month_name(self):
"""The name of the previous month
"""
if self.timestamp.month == 1:
timestamp = self.prev_year.replace(month=12)
else:
timestamp = self.timestamp.replace(month=self.timestamp.month - 1)
return self.month_names[timestamp.month - 1]
@property
@to_timestamp
def next_month(self):
"""Returns the timestamp of the same day in the next month
"""
if self.timestamp.month == 12:
return self.next_year.replace(month=1)
return self.timestamp.replace(month=self.timestamp.month + 1)
@property
def next_month_name(self):
"""The name of the next month
"""
if self.timestamp.month == 12:
timestamp = self.prev_year.replace(month=1)
else:
timestamp = self.timestamp.replace(month=self.timestamp.month + 1)
return self.month_names[timestamp.month - 1]
@property
@to_timestamp
def next_year(self):
"""Returns the timestamp of the same date in the next year
"""
return self.timestamp.replace(year=self.timestamp.year + 1)
@property
def next_year_year(self):
"""The number of the next year
"""
return self.timestamp.replace(year=self.timestamp.year + 1).year
@property
def has_today(self):
"""``True`` if the set month holds todays date
"""
now = datetime.utcnow()
month_start_timestamp = self.timestamp.replace(day=1,
hour=0,
minute=0,
second=0,
microsecond=0)
if self.timestamp.month == 12:
next_month = 1
else:
next_month = self.timestamp.month + 1
month_end_timestamp = month_start_timestamp.replace(month=next_month)
return now >= month_start_timestamp and now < month_end_timestamp
@staticmethod
def day_events(date, user=None):
"""Returns all events for a given day
"""
from ..models import Event, EventVisibility, Invitation, Profile, Response
events = Event.query
if user:
events = events.outerjoin(Invitation) \
.outerjoin(Response) \
.join(Profile, Event.profile) \
.filter(Profile.user == user)
start_timestamp = date.replace(hour=0, minute=0, second=0, microsecond=0)
end_timestamp = start_timestamp + timedelta(days=1)
events = events.filter((Event.start_time <= end_timestamp) &
(Event.end_time >= start_timestamp)) \
.order_by('start_time', 'end_time')
if user is None:
events = events.filter(Event.visibility == EventVisibility.public)
else:
events = events.filter((Event.visibility == EventVisibility.public) |
(Event.profile == user.profile) |
(Invitation.invitee == user.profile) |
(Response.profile == user.profile))
return events