[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
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
"""Main module for the Calendar.social app
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
import os
|
||||
@ -40,6 +43,9 @@ def get_locale():
|
||||
|
||||
|
||||
def template_vars():
|
||||
"""Function to inject global template variables
|
||||
"""
|
||||
|
||||
now = datetime.utcnow()
|
||||
|
||||
return {
|
||||
@ -50,6 +56,12 @@ def template_vars():
|
||||
|
||||
|
||||
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):
|
||||
setattr(func, 'routing', (args, kwargs))
|
||||
|
||||
@ -59,6 +71,9 @@ def route(*args, **kwargs):
|
||||
|
||||
|
||||
class CalendarSocialApp(Flask):
|
||||
"""The Calendar.social app
|
||||
"""
|
||||
|
||||
def __init__(self, name, config=None):
|
||||
from .models import db, User, Role
|
||||
from .security import security
|
||||
@ -93,6 +108,12 @@ class CalendarSocialApp(Flask):
|
||||
|
||||
@route('/')
|
||||
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
|
||||
|
||||
if not current_user.is_authenticated:
|
||||
@ -109,6 +130,12 @@ class CalendarSocialApp(Flask):
|
||||
|
||||
@route('/register', methods=['POST', 'GET'])
|
||||
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']:
|
||||
return render_template('registration-disabled.html')
|
||||
|
||||
@ -132,6 +159,11 @@ class CalendarSocialApp(Flask):
|
||||
@route('/new-event', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
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 .models import db, Event
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
"""Main module for Calendar.social so the app can be run directly.
|
||||
"""
|
||||
|
||||
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:
|
||||
"""Base class for calendar systems
|
||||
"""
|
||||
|
||||
__has_months__ = False
|
||||
__has_weeks__ = False
|
||||
|
||||
@property
|
||||
def day_names(self):
|
||||
"""An iterable of day names
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def month(self):
|
||||
"""The name of the current month, if applicable
|
||||
|
||||
It may include the year.
|
||||
"""
|
||||
|
||||
raise NotImplementedError()
|
||||
|
||||
@property
|
||||
def days(self):
|
||||
"""An iterable of days to be displayed on the month view, if applicable
|
||||
"""
|
||||
|
||||
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 functools import wraps
|
||||
|
||||
@ -7,6 +26,9 @@ 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()
|
||||
@ -15,6 +37,9 @@ def to_timestamp(func):
|
||||
|
||||
|
||||
class GregorianCalendar(CalendarSystem):
|
||||
"""Gregorian calendar system for Calendar.social
|
||||
"""
|
||||
|
||||
__name__ = _('Gregorian')
|
||||
|
||||
__has_months__ = True
|
||||
@ -75,15 +100,24 @@ class GregorianCalendar(CalendarSystem):
|
||||
@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)
|
||||
|
||||
@ -91,6 +125,9 @@ class GregorianCalendar(CalendarSystem):
|
||||
|
||||
@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:
|
||||
@ -101,6 +138,9 @@ class GregorianCalendar(CalendarSystem):
|
||||
@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)
|
||||
|
||||
@ -108,6 +148,9 @@ class GregorianCalendar(CalendarSystem):
|
||||
|
||||
@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:
|
||||
@ -118,14 +161,23 @@ class GregorianCalendar(CalendarSystem):
|
||||
@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 today’s date
|
||||
"""
|
||||
|
||||
now = datetime.utcnow()
|
||||
month_start_timestamp = self.timestamp.replace(day=1,
|
||||
hour=0,
|
||||
@ -144,6 +196,9 @@ class GregorianCalendar(CalendarSystem):
|
||||
|
||||
@staticmethod
|
||||
def day_events(date, user=None):
|
||||
"""Returns all events for a given day
|
||||
"""
|
||||
|
||||
from ..models import Event
|
||||
|
||||
events = Event.query
|
||||
|
@ -1,5 +1,9 @@
|
||||
"""Configuration file for the development environment
|
||||
"""
|
||||
|
||||
DEBUG = True
|
||||
ENV = 'dev'
|
||||
#: If ``True``, registration on the site is enabled.
|
||||
REGISTRATION_ENABLED = True
|
||||
|
||||
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_wtf import FlaskForm
|
||||
import pytz
|
||||
@ -8,17 +27,32 @@ from wtforms.widgets import TextArea
|
||||
|
||||
|
||||
class RegistrationForm(FlaskForm):
|
||||
"""Registration form
|
||||
"""
|
||||
|
||||
username = StringField(_('Username'), validators=[DataRequired()])
|
||||
email = StringField(_('Email address'), validators=[Email()])
|
||||
password = PasswordField(_('Password'), validators=[DataRequired()])
|
||||
password_retype = PasswordField(_('Password, once more'), validators=[DataRequired()])
|
||||
|
||||
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:
|
||||
raise ValidationError(_('The two passwords must match!'))
|
||||
|
||||
|
||||
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):
|
||||
kwargs.update({
|
||||
'choices': [
|
||||
@ -44,6 +78,9 @@ class TimezoneField(SelectField):
|
||||
|
||||
@staticmethod
|
||||
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)
|
||||
|
||||
def process_data(self, value):
|
||||
@ -68,6 +105,9 @@ class TimezoneField(SelectField):
|
||||
|
||||
|
||||
class EventForm(FlaskForm):
|
||||
"""Form for event creation/editing
|
||||
"""
|
||||
|
||||
title = StringField(_('Title'), validators=[DataRequired()])
|
||||
time_zone = TimezoneField(_('Time zone'), validators=[DataRequired()])
|
||||
start_time = DateTimeField(_('Start time'), validators=[DataRequired()])
|
||||
@ -76,6 +116,9 @@ class EventForm(FlaskForm):
|
||||
description = StringField(_('Description'), widget=TextArea())
|
||||
|
||||
def populate_obj(self, obj):
|
||||
"""Populate ``obj`` with event data
|
||||
"""
|
||||
|
||||
FlaskForm.populate_obj(self, obj)
|
||||
|
||||
timezone = self.time_zone.data
|
||||
@ -85,5 +128,10 @@ class EventForm(FlaskForm):
|
||||
obj.end_time = timezone.localize(self.end_time.data).astimezone(pytz.utc)
|
||||
|
||||
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:
|
||||
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 flask_security import UserMixin, RoleMixin
|
||||
@ -11,6 +30,9 @@ users_roles = db.Table(
|
||||
|
||||
|
||||
class User(db.Model, UserMixin):
|
||||
"""Database model for users
|
||||
"""
|
||||
|
||||
__tablename__ = 'users'
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
|
||||
@ -42,6 +64,9 @@ class User(db.Model, UserMixin):
|
||||
|
||||
|
||||
class Role(db.Model, RoleMixin):
|
||||
"""Database model for roles
|
||||
"""
|
||||
|
||||
__tablename__ = 'roles'
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
|
||||
@ -56,6 +81,9 @@ class Role(db.Model, RoleMixin):
|
||||
|
||||
|
||||
class Event(db.Model):
|
||||
"""Database model for events
|
||||
"""
|
||||
|
||||
__tablename__ = 'events'
|
||||
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`
|
||||
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)
|
||||
|
||||
#: 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)
|
||||
|
||||
#: If `True`, the event is a whole-day event
|
||||
@ -89,10 +117,16 @@ class Event(db.Model):
|
||||
|
||||
@property
|
||||
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)
|
||||
|
||||
@property
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
security = Security()
|
||||
|
Loading…
Reference in New Issue
Block a user