forked from gergely/calendar-social
[Docs] Add/update docstrings and license text in every file
This commit is contained in:
parent
a5bf841898
commit
2f66fdb83e
@ -14,6 +14,9 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""Main module for the Calendar.social app
|
||||||
|
"""
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import os
|
import os
|
||||||
@ -40,6 +43,9 @@ def get_locale():
|
|||||||
|
|
||||||
|
|
||||||
def template_vars():
|
def template_vars():
|
||||||
|
"""Function to inject global template variables
|
||||||
|
"""
|
||||||
|
|
||||||
now = datetime.utcnow()
|
now = datetime.utcnow()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -50,6 +56,12 @@ def template_vars():
|
|||||||
|
|
||||||
|
|
||||||
def route(*args, **kwargs):
|
def route(*args, **kwargs):
|
||||||
|
"""Mark a function as a future route
|
||||||
|
|
||||||
|
Such functions will be iterated over when the application is initialised. ``*args`` and
|
||||||
|
``**kwargs`` will be passed verbatim to `Flask.route()`.
|
||||||
|
"""
|
||||||
|
|
||||||
def decorator(func):
|
def decorator(func):
|
||||||
setattr(func, 'routing', (args, kwargs))
|
setattr(func, 'routing', (args, kwargs))
|
||||||
|
|
||||||
@ -59,6 +71,9 @@ def route(*args, **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
class CalendarSocialApp(Flask):
|
class CalendarSocialApp(Flask):
|
||||||
|
"""The Calendar.social app
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, name, config=None):
|
def __init__(self, name, config=None):
|
||||||
from .models import db, User, Role
|
from .models import db, User, Role
|
||||||
from .security import security
|
from .security import security
|
||||||
@ -93,6 +108,12 @@ class CalendarSocialApp(Flask):
|
|||||||
|
|
||||||
@route('/')
|
@route('/')
|
||||||
def hello(self):
|
def hello(self):
|
||||||
|
"""View for the main page
|
||||||
|
|
||||||
|
This will display a welcome message for users not logged in; for others, their main
|
||||||
|
calendar view is displayed.
|
||||||
|
"""
|
||||||
|
|
||||||
from .calendar_system.gregorian import GregorianCalendar
|
from .calendar_system.gregorian import GregorianCalendar
|
||||||
|
|
||||||
if not current_user.is_authenticated:
|
if not current_user.is_authenticated:
|
||||||
@ -109,6 +130,12 @@ class CalendarSocialApp(Flask):
|
|||||||
|
|
||||||
@route('/register', methods=['POST', 'GET'])
|
@route('/register', methods=['POST', 'GET'])
|
||||||
def register(self):
|
def register(self):
|
||||||
|
"""View for user registration
|
||||||
|
|
||||||
|
If the ``REGISTRATION_FAILED`` configuration value is set to ``True`` it displays the
|
||||||
|
registration disabled template. Otherwise, it performs user registration.
|
||||||
|
"""
|
||||||
|
|
||||||
if not current_app.config['REGISTRATION_ENABLED']:
|
if not current_app.config['REGISTRATION_ENABLED']:
|
||||||
return render_template('registration-disabled.html')
|
return render_template('registration-disabled.html')
|
||||||
|
|
||||||
@ -132,6 +159,11 @@ class CalendarSocialApp(Flask):
|
|||||||
@route('/new-event', methods=['GET', 'POST'])
|
@route('/new-event', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def new_event(self):
|
def new_event(self):
|
||||||
|
"""View for creating a new event
|
||||||
|
|
||||||
|
This presents a form to the user that allows entering event details.
|
||||||
|
"""
|
||||||
|
|
||||||
from .forms import EventForm
|
from .forms import EventForm
|
||||||
from .models import db, Event
|
from .models import db, Event
|
||||||
|
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
"""Main module for Calendar.social so the app can be run directly.
|
||||||
|
"""
|
||||||
|
|
||||||
from calsocial import CalendarSocialApp
|
from calsocial import CalendarSocialApp
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,15 +1,48 @@
|
|||||||
|
# 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/>.
|
||||||
|
|
||||||
|
"""Calendar system base definition for Calendar.social
|
||||||
|
"""
|
||||||
|
|
||||||
class CalendarSystem:
|
class CalendarSystem:
|
||||||
|
"""Base class for calendar systems
|
||||||
|
"""
|
||||||
|
|
||||||
__has_months__ = False
|
__has_months__ = False
|
||||||
__has_weeks__ = False
|
__has_weeks__ = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def day_names(self):
|
def day_names(self):
|
||||||
|
"""An iterable of day names
|
||||||
|
"""
|
||||||
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def month(self):
|
def month(self):
|
||||||
|
"""The name of the current month, if applicable
|
||||||
|
|
||||||
|
It may include the year.
|
||||||
|
"""
|
||||||
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def days(self):
|
def days(self):
|
||||||
|
"""An iterable of days to be displayed on the month view, if applicable
|
||||||
|
"""
|
||||||
|
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
@ -1,3 +1,22 @@
|
|||||||
|
# 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 datetime import datetime, timedelta
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
@ -7,6 +26,9 @@ from . import CalendarSystem
|
|||||||
|
|
||||||
|
|
||||||
def to_timestamp(func):
|
def to_timestamp(func):
|
||||||
|
"""Decorator that converts the return value of a function from `datetime` to a UNIX timestamp
|
||||||
|
"""
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
def decorator(*args, **kwargs):
|
def decorator(*args, **kwargs):
|
||||||
return func(*args, **kwargs).timestamp()
|
return func(*args, **kwargs).timestamp()
|
||||||
@ -15,6 +37,9 @@ def to_timestamp(func):
|
|||||||
|
|
||||||
|
|
||||||
class GregorianCalendar(CalendarSystem):
|
class GregorianCalendar(CalendarSystem):
|
||||||
|
"""Gregorian calendar system for Calendar.social
|
||||||
|
"""
|
||||||
|
|
||||||
__name__ = _('Gregorian')
|
__name__ = _('Gregorian')
|
||||||
|
|
||||||
__has_months__ = True
|
__has_months__ = True
|
||||||
@ -75,15 +100,24 @@ class GregorianCalendar(CalendarSystem):
|
|||||||
@property
|
@property
|
||||||
@to_timestamp
|
@to_timestamp
|
||||||
def prev_year(self):
|
def prev_year(self):
|
||||||
|
"""Returns the timestamp of the same date in the previous year
|
||||||
|
"""
|
||||||
|
|
||||||
return self.timestamp.replace(year=self.timestamp.year - 1)
|
return self.timestamp.replace(year=self.timestamp.year - 1)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def prev_year_year(self):
|
def prev_year_year(self):
|
||||||
|
"""The number of the previous year
|
||||||
|
"""
|
||||||
|
|
||||||
return self.timestamp.replace(year=self.timestamp.year - 1).year
|
return self.timestamp.replace(year=self.timestamp.year - 1).year
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@to_timestamp
|
@to_timestamp
|
||||||
def prev_month(self):
|
def prev_month(self):
|
||||||
|
"""Returns the timestamp of the same day in the previous month
|
||||||
|
"""
|
||||||
|
|
||||||
if self.timestamp.month == 1:
|
if self.timestamp.month == 1:
|
||||||
return self.prev_year.replace(month=12)
|
return self.prev_year.replace(month=12)
|
||||||
|
|
||||||
@ -91,6 +125,9 @@ class GregorianCalendar(CalendarSystem):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def prev_month_name(self):
|
def prev_month_name(self):
|
||||||
|
"""The name of the previous month
|
||||||
|
"""
|
||||||
|
|
||||||
if self.timestamp.month == 1:
|
if self.timestamp.month == 1:
|
||||||
timestamp = self.prev_year.replace(month=12)
|
timestamp = self.prev_year.replace(month=12)
|
||||||
else:
|
else:
|
||||||
@ -101,6 +138,9 @@ class GregorianCalendar(CalendarSystem):
|
|||||||
@property
|
@property
|
||||||
@to_timestamp
|
@to_timestamp
|
||||||
def next_month(self):
|
def next_month(self):
|
||||||
|
"""Returns the timestamp of the same day in the next month
|
||||||
|
"""
|
||||||
|
|
||||||
if self.timestamp.month == 12:
|
if self.timestamp.month == 12:
|
||||||
return self.next_year.replace(month=1)
|
return self.next_year.replace(month=1)
|
||||||
|
|
||||||
@ -108,6 +148,9 @@ class GregorianCalendar(CalendarSystem):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def next_month_name(self):
|
def next_month_name(self):
|
||||||
|
"""The name of the next month
|
||||||
|
"""
|
||||||
|
|
||||||
if self.timestamp.month == 12:
|
if self.timestamp.month == 12:
|
||||||
timestamp = self.prev_year.replace(month=1)
|
timestamp = self.prev_year.replace(month=1)
|
||||||
else:
|
else:
|
||||||
@ -118,14 +161,23 @@ class GregorianCalendar(CalendarSystem):
|
|||||||
@property
|
@property
|
||||||
@to_timestamp
|
@to_timestamp
|
||||||
def next_year(self):
|
def next_year(self):
|
||||||
|
"""Returns the timestamp of the same date in the next year
|
||||||
|
"""
|
||||||
|
|
||||||
return self.timestamp.replace(year=self.timestamp.year + 1)
|
return self.timestamp.replace(year=self.timestamp.year + 1)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def next_year_year(self):
|
def next_year_year(self):
|
||||||
|
"""The number of the next year
|
||||||
|
"""
|
||||||
|
|
||||||
return self.timestamp.replace(year=self.timestamp.year + 1).year
|
return self.timestamp.replace(year=self.timestamp.year + 1).year
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_today(self):
|
def has_today(self):
|
||||||
|
"""``True`` if the set month holds today’s date
|
||||||
|
"""
|
||||||
|
|
||||||
now = datetime.utcnow()
|
now = datetime.utcnow()
|
||||||
month_start_timestamp = self.timestamp.replace(day=1,
|
month_start_timestamp = self.timestamp.replace(day=1,
|
||||||
hour=0,
|
hour=0,
|
||||||
@ -144,6 +196,9 @@ class GregorianCalendar(CalendarSystem):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def day_events(date, user=None):
|
def day_events(date, user=None):
|
||||||
|
"""Returns all events for a given day
|
||||||
|
"""
|
||||||
|
|
||||||
from ..models import Event
|
from ..models import Event
|
||||||
|
|
||||||
events = Event.query
|
events = Event.query
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
"""Configuration file for the development environment
|
||||||
|
"""
|
||||||
|
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
ENV = 'dev'
|
ENV = 'dev'
|
||||||
|
#: If ``True``, registration on the site is enabled.
|
||||||
REGISTRATION_ENABLED = True
|
REGISTRATION_ENABLED = True
|
||||||
|
|
||||||
SQLALCHEMY_DATABASE_URI = 'sqlite:///local.db'
|
SQLALCHEMY_DATABASE_URI = 'sqlite:///local.db'
|
||||||
|
@ -1,3 +1,22 @@
|
|||||||
|
# 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/>.
|
||||||
|
|
||||||
|
"""Forms for Calendar.social
|
||||||
|
"""
|
||||||
|
|
||||||
from flask_babelex import lazy_gettext as _
|
from flask_babelex import lazy_gettext as _
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
import pytz
|
import pytz
|
||||||
@ -8,17 +27,32 @@ from wtforms.widgets import TextArea
|
|||||||
|
|
||||||
|
|
||||||
class RegistrationForm(FlaskForm):
|
class RegistrationForm(FlaskForm):
|
||||||
|
"""Registration form
|
||||||
|
"""
|
||||||
|
|
||||||
username = StringField(_('Username'), validators=[DataRequired()])
|
username = StringField(_('Username'), validators=[DataRequired()])
|
||||||
email = StringField(_('Email address'), validators=[Email()])
|
email = StringField(_('Email address'), validators=[Email()])
|
||||||
password = PasswordField(_('Password'), validators=[DataRequired()])
|
password = PasswordField(_('Password'), validators=[DataRequired()])
|
||||||
password_retype = PasswordField(_('Password, once more'), validators=[DataRequired()])
|
password_retype = PasswordField(_('Password, once more'), validators=[DataRequired()])
|
||||||
|
|
||||||
def validate_password_retype(self, field):
|
def validate_password_retype(self, field):
|
||||||
|
"""Validate the ``password_retype`` field
|
||||||
|
|
||||||
|
Its value must match the ``password`` field’s.
|
||||||
|
"""
|
||||||
|
|
||||||
if field.data != self.password.data:
|
if field.data != self.password.data:
|
||||||
raise ValidationError(_('The two passwords must match!'))
|
raise ValidationError(_('The two passwords must match!'))
|
||||||
|
|
||||||
|
|
||||||
class TimezoneField(SelectField):
|
class TimezoneField(SelectField):
|
||||||
|
"""Field for selecting a time zone
|
||||||
|
|
||||||
|
Note: this field overrides whatever is passed to the ``choices`` parameter, and fills
|
||||||
|
``choices`` with the common timezones of pytz. In every other aspects, it behaves exactly
|
||||||
|
like `SelectField`.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
kwargs.update({
|
kwargs.update({
|
||||||
'choices': [
|
'choices': [
|
||||||
@ -44,6 +78,9 @@ class TimezoneField(SelectField):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_pytz_instance(value):
|
def is_pytz_instance(value):
|
||||||
|
"""Check if ``value`` is a valid pytz time zone instance
|
||||||
|
"""
|
||||||
|
|
||||||
return value is pytz.UTC or isinstance(value, pytz.tzinfo.BaseTzInfo)
|
return value is pytz.UTC or isinstance(value, pytz.tzinfo.BaseTzInfo)
|
||||||
|
|
||||||
def process_data(self, value):
|
def process_data(self, value):
|
||||||
@ -68,6 +105,9 @@ class TimezoneField(SelectField):
|
|||||||
|
|
||||||
|
|
||||||
class EventForm(FlaskForm):
|
class EventForm(FlaskForm):
|
||||||
|
"""Form for event creation/editing
|
||||||
|
"""
|
||||||
|
|
||||||
title = StringField(_('Title'), validators=[DataRequired()])
|
title = StringField(_('Title'), validators=[DataRequired()])
|
||||||
time_zone = TimezoneField(_('Time zone'), validators=[DataRequired()])
|
time_zone = TimezoneField(_('Time zone'), validators=[DataRequired()])
|
||||||
start_time = DateTimeField(_('Start time'), validators=[DataRequired()])
|
start_time = DateTimeField(_('Start time'), validators=[DataRequired()])
|
||||||
@ -76,6 +116,9 @@ class EventForm(FlaskForm):
|
|||||||
description = StringField(_('Description'), widget=TextArea())
|
description = StringField(_('Description'), widget=TextArea())
|
||||||
|
|
||||||
def populate_obj(self, obj):
|
def populate_obj(self, obj):
|
||||||
|
"""Populate ``obj`` with event data
|
||||||
|
"""
|
||||||
|
|
||||||
FlaskForm.populate_obj(self, obj)
|
FlaskForm.populate_obj(self, obj)
|
||||||
|
|
||||||
timezone = self.time_zone.data
|
timezone = self.time_zone.data
|
||||||
@ -85,5 +128,10 @@ class EventForm(FlaskForm):
|
|||||||
obj.end_time = timezone.localize(self.end_time.data).astimezone(pytz.utc)
|
obj.end_time = timezone.localize(self.end_time.data).astimezone(pytz.utc)
|
||||||
|
|
||||||
def validate_end_time(self, field):
|
def validate_end_time(self, field):
|
||||||
|
"""Validate the ``end_time`` field
|
||||||
|
|
||||||
|
Its value must be later than the value of the ``start_time`` field.
|
||||||
|
"""
|
||||||
|
|
||||||
if field.data < self.start_time.data:
|
if field.data < self.start_time.data:
|
||||||
raise ValidationError(_('End time must be later than start time!'))
|
raise ValidationError(_('End time must be later than start time!'))
|
||||||
|
@ -1,3 +1,22 @@
|
|||||||
|
# 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/>.
|
||||||
|
|
||||||
|
"""Database models for Calendar.social
|
||||||
|
"""
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from flask_security import UserMixin, RoleMixin
|
from flask_security import UserMixin, RoleMixin
|
||||||
@ -11,6 +30,9 @@ users_roles = db.Table(
|
|||||||
|
|
||||||
|
|
||||||
class User(db.Model, UserMixin):
|
class User(db.Model, UserMixin):
|
||||||
|
"""Database model for users
|
||||||
|
"""
|
||||||
|
|
||||||
__tablename__ = 'users'
|
__tablename__ = 'users'
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
|
|
||||||
@ -42,6 +64,9 @@ class User(db.Model, UserMixin):
|
|||||||
|
|
||||||
|
|
||||||
class Role(db.Model, RoleMixin):
|
class Role(db.Model, RoleMixin):
|
||||||
|
"""Database model for roles
|
||||||
|
"""
|
||||||
|
|
||||||
__tablename__ = 'roles'
|
__tablename__ = 'roles'
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
|
|
||||||
@ -56,6 +81,9 @@ class Role(db.Model, RoleMixin):
|
|||||||
|
|
||||||
|
|
||||||
class Event(db.Model):
|
class Event(db.Model):
|
||||||
|
"""Database model for events
|
||||||
|
"""
|
||||||
|
|
||||||
__tablename__ = 'events'
|
__tablename__ = 'events'
|
||||||
id = db.Column(db.Integer(), primary_key=True)
|
id = db.Column(db.Integer(), primary_key=True)
|
||||||
|
|
||||||
@ -70,10 +98,10 @@ class Event(db.Model):
|
|||||||
#: The time zone to be used for `start_time` and `end_time`
|
#: The time zone to be used for `start_time` and `end_time`
|
||||||
time_zone = db.Column(db.String(length=80), nullable=False)
|
time_zone = db.Column(db.String(length=80), nullable=False)
|
||||||
|
|
||||||
#: The starting timestamp of the event
|
#: The starting timestamp of the event. It is in the UTC time zone
|
||||||
start_time = db.Column(db.DateTime(), nullable=False)
|
start_time = db.Column(db.DateTime(), nullable=False)
|
||||||
|
|
||||||
#: The ending timestamp of the event
|
#: The ending timestamp of the event. It is in the UTC time zone
|
||||||
end_time = db.Column(db.DateTime(), nullable=False)
|
end_time = db.Column(db.DateTime(), nullable=False)
|
||||||
|
|
||||||
#: If `True`, the event is a whole-day event
|
#: If `True`, the event is a whole-day event
|
||||||
@ -89,10 +117,16 @@ class Event(db.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def start_time_tz(self):
|
def start_time_tz(self):
|
||||||
|
"""The same timestamp as `start_time`, but in the time zone specified by `time_zone`.
|
||||||
|
"""
|
||||||
|
|
||||||
return self.__as_tz(self.start_time)
|
return self.__as_tz(self.start_time)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def end_time_tz(self):
|
def end_time_tz(self):
|
||||||
|
"""The same timestamp as `end_time`, but in the time zone specified by `time_zone`.
|
||||||
|
"""
|
||||||
|
|
||||||
return self.__as_tz(self.end_time)
|
return self.__as_tz(self.end_time)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
@ -1,3 +1,22 @@
|
|||||||
|
# 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/>.
|
||||||
|
|
||||||
|
"""Security related things for Calendar.social
|
||||||
|
"""
|
||||||
|
|
||||||
from flask_security import Security
|
from flask_security import Security
|
||||||
|
|
||||||
security = Security()
|
security = Security()
|
||||||
|
Loading…
Reference in New Issue
Block a user