import from kontor-flask
This commit is contained in:
@@ -0,0 +1,47 @@
|
|||||||
|
# config.py
|
||||||
|
|
||||||
|
|
||||||
|
class Config(object):
|
||||||
|
"""
|
||||||
|
Common configurations
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Put any configurations here that are common across all environments
|
||||||
|
host = '127.0.0.1'
|
||||||
|
port = 8500
|
||||||
|
database = 'kontor'
|
||||||
|
|
||||||
|
|
||||||
|
class DevelopmentConfig(Config):
|
||||||
|
"""
|
||||||
|
Development configurations
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEBUG = True
|
||||||
|
host = '0.0.0.0'
|
||||||
|
database = 'kontor_dev'
|
||||||
|
|
||||||
|
|
||||||
|
class ProductionConfig(Config):
|
||||||
|
"""
|
||||||
|
Production configurations
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
class TestingConfig(Config):
|
||||||
|
"""
|
||||||
|
Testing configurations
|
||||||
|
"""
|
||||||
|
|
||||||
|
TESTING = True
|
||||||
|
DEBUG = True
|
||||||
|
port = 8600
|
||||||
|
database = 'kontor_test'
|
||||||
|
|
||||||
|
app_config = {
|
||||||
|
'development': DevelopmentConfig,
|
||||||
|
'production': ProductionConfig,
|
||||||
|
'testing': TestingConfig
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Minimal makefile for Sphinx documentation
|
||||||
|
#
|
||||||
|
|
||||||
|
# You can set these variables from the command line.
|
||||||
|
SPHINXOPTS =
|
||||||
|
SPHINXBUILD = sphinx-build
|
||||||
|
SPHINXPROJ = KontorFlask
|
||||||
|
SOURCEDIR = .
|
||||||
|
BUILDDIR = _build
|
||||||
|
|
||||||
|
# Put it first so that "make" without argument is like "make help".
|
||||||
|
help:
|
||||||
|
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
|
|
||||||
|
.PHONY: help Makefile
|
||||||
|
|
||||||
|
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||||
|
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||||
|
%: Makefile
|
||||||
|
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# Kontor Flask documentation build configuration file, created by
|
||||||
|
# sphinx-quickstart on Mon Nov 6 08:53:04 2017.
|
||||||
|
#
|
||||||
|
# This file is execfile()d with the current directory set to its
|
||||||
|
# containing dir.
|
||||||
|
#
|
||||||
|
# Note that not all possible configuration values are present in this
|
||||||
|
# autogenerated file.
|
||||||
|
#
|
||||||
|
# All configuration values have a default; values that are commented out
|
||||||
|
# serve to show the default.
|
||||||
|
|
||||||
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
|
#
|
||||||
|
# import os
|
||||||
|
# import sys
|
||||||
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
|
|
||||||
|
|
||||||
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
|
# If your documentation needs a minimal Sphinx version, state it here.
|
||||||
|
#
|
||||||
|
# needs_sphinx = '1.0'
|
||||||
|
|
||||||
|
# Add any Sphinx extension module names here, as strings. They can be
|
||||||
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
|
# ones.
|
||||||
|
extensions = ['sphinx.ext.autodoc',
|
||||||
|
'sphinx.ext.doctest',
|
||||||
|
'sphinx.ext.intersphinx',
|
||||||
|
'sphinx.ext.todo',
|
||||||
|
'sphinx.ext.coverage',
|
||||||
|
'sphinx.ext.mathjax',
|
||||||
|
'sphinx.ext.ifconfig',
|
||||||
|
'sphinx.ext.viewcode']
|
||||||
|
|
||||||
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
templates_path = ['_templates']
|
||||||
|
|
||||||
|
# The suffix(es) of source filenames.
|
||||||
|
# You can specify multiple suffix as a list of string:
|
||||||
|
#
|
||||||
|
# source_suffix = ['.rst', '.md']
|
||||||
|
source_suffix = '.rst'
|
||||||
|
|
||||||
|
# The master toctree document.
|
||||||
|
master_doc = 'index'
|
||||||
|
|
||||||
|
# General information about the project.
|
||||||
|
project = 'Kontor Flask'
|
||||||
|
copyright = '2017, Thomas Peetz'
|
||||||
|
author = 'Thomas Peetz'
|
||||||
|
|
||||||
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
|
# |version| and |release|, also used in various other places throughout the
|
||||||
|
# built documents.
|
||||||
|
#
|
||||||
|
# The short X.Y version.
|
||||||
|
version = '0.1'
|
||||||
|
# The full version, including alpha/beta/rc tags.
|
||||||
|
release = '0.0.7'
|
||||||
|
|
||||||
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
|
# for a list of supported languages.
|
||||||
|
#
|
||||||
|
# This is also used if you do content translation via gettext catalogs.
|
||||||
|
# Usually you set "language" from the command line for these cases.
|
||||||
|
language = 'de'
|
||||||
|
|
||||||
|
# List of patterns, relative to source directory, that match files and
|
||||||
|
# directories to ignore when looking for source files.
|
||||||
|
# This patterns also effect to html_static_path and html_extra_path
|
||||||
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||||
|
|
||||||
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
|
pygments_style = 'sphinx'
|
||||||
|
|
||||||
|
# If true, `todo` and `todoList` produce output, else they produce nothing.
|
||||||
|
todo_include_todos = True
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output ----------------------------------------------
|
||||||
|
|
||||||
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
|
# a list of builtin themes.
|
||||||
|
#
|
||||||
|
html_theme = 'alabaster'
|
||||||
|
|
||||||
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
|
# further. For a list of options available for each theme, see the
|
||||||
|
# documentation.
|
||||||
|
#
|
||||||
|
# html_theme_options = {}
|
||||||
|
|
||||||
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
# Custom sidebar templates, must be a dictionary that maps document names
|
||||||
|
# to template names.
|
||||||
|
#
|
||||||
|
# This is required for the alabaster theme
|
||||||
|
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
||||||
|
html_sidebars = {
|
||||||
|
'**': [
|
||||||
|
'relations.html', # needs 'show_related': True theme option to display
|
||||||
|
'searchbox.html',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTMLHelp output ------------------------------------------
|
||||||
|
|
||||||
|
# Output file base name for HTML help builder.
|
||||||
|
htmlhelp_basename = 'KontorFlaskdoc'
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for LaTeX output ---------------------------------------------
|
||||||
|
|
||||||
|
latex_elements = {
|
||||||
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
|
#
|
||||||
|
# 'papersize': 'letterpaper',
|
||||||
|
|
||||||
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
|
#
|
||||||
|
# 'pointsize': '10pt',
|
||||||
|
|
||||||
|
# Additional stuff for the LaTeX preamble.
|
||||||
|
#
|
||||||
|
# 'preamble': '',
|
||||||
|
|
||||||
|
# Latex figure (float) alignment
|
||||||
|
#
|
||||||
|
# 'figure_align': 'htbp',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Grouping the document tree into LaTeX files. List of tuples
|
||||||
|
# (source start file, target name, title,
|
||||||
|
# author, documentclass [howto, manual, or own class]).
|
||||||
|
latex_documents = [
|
||||||
|
(master_doc, 'KontorFlask.tex', 'Kontor Flask Documentation',
|
||||||
|
'Thomas Peetz', 'manual'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for manual page output ---------------------------------------
|
||||||
|
|
||||||
|
# One entry per manual page. List of tuples
|
||||||
|
# (source start file, name, description, authors, manual section).
|
||||||
|
man_pages = [
|
||||||
|
(master_doc, 'kontorflask', 'Kontor Flask Documentation',
|
||||||
|
[author], 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for Texinfo output -------------------------------------------
|
||||||
|
|
||||||
|
# Grouping the document tree into Texinfo files. List of tuples
|
||||||
|
# (source start file, target name, title, author,
|
||||||
|
# dir menu entry, description, category)
|
||||||
|
texinfo_documents = [
|
||||||
|
(master_doc, 'KontorFlask', 'Kontor Flask Documentation',
|
||||||
|
author, 'KontorFlask', 'One line description of project.',
|
||||||
|
'Miscellaneous'),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for Epub output ----------------------------------------------
|
||||||
|
|
||||||
|
# Bibliographic Dublin Core info.
|
||||||
|
epub_title = project
|
||||||
|
epub_author = author
|
||||||
|
epub_publisher = author
|
||||||
|
epub_copyright = copyright
|
||||||
|
|
||||||
|
# The unique identifier of the text. This can be a ISBN number
|
||||||
|
# or the project homepage.
|
||||||
|
#
|
||||||
|
# epub_identifier = ''
|
||||||
|
|
||||||
|
# A unique identification for the text.
|
||||||
|
#
|
||||||
|
# epub_uid = ''
|
||||||
|
|
||||||
|
# A list of files that should not be packed into the epub file.
|
||||||
|
epub_exclude_files = ['search.html']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Example configuration for intersphinx: refer to the Python standard library.
|
||||||
|
intersphinx_mapping = {'https://docs.python.org/': None}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
.. Kontor Flask documentation master file, created by
|
||||||
|
sphinx-quickstart on Mon Nov 6 08:53:04 2017.
|
||||||
|
You can adapt this file completely to your liking, but it should at least
|
||||||
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
Welcome to Kontor Flask's documentation!
|
||||||
|
========================================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Contents:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
"""
|
||||||
|
Module kontor implements Kontor application.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import Flask
|
||||||
|
from flask_bootstrap import Bootstrap
|
||||||
|
from flask_login import LoginManager
|
||||||
|
from pymodm import connect
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from config import app_config
|
||||||
|
from .version import __version__
|
||||||
|
|
||||||
|
|
||||||
|
_LOGIN_MANAGER_ = LoginManager()
|
||||||
|
|
||||||
|
|
||||||
|
def get_host(config_name):
|
||||||
|
"""
|
||||||
|
Returns host address from configuration.
|
||||||
|
:param config_name:
|
||||||
|
:return: host address
|
||||||
|
"""
|
||||||
|
host = app_config[config_name].host
|
||||||
|
return host
|
||||||
|
|
||||||
|
|
||||||
|
def get_port(config_name):
|
||||||
|
"""
|
||||||
|
Returns port number from configuration.
|
||||||
|
:param config_name:
|
||||||
|
:return: port number
|
||||||
|
"""
|
||||||
|
port = app_config[config_name].port
|
||||||
|
return port
|
||||||
|
|
||||||
|
|
||||||
|
def create_app(config_name):
|
||||||
|
"""
|
||||||
|
Returns Flask application.
|
||||||
|
:param config_name:
|
||||||
|
:return: Flask application
|
||||||
|
"""
|
||||||
|
app = Flask(__name__, instance_relative_config=True)
|
||||||
|
app.config.from_object(app_config[config_name])
|
||||||
|
app.config.from_pyfile('config.py')
|
||||||
|
|
||||||
|
database = "mongodb://localhost:27017/{}".format(app_config[config_name].database)
|
||||||
|
connect(database, alias="kontor")
|
||||||
|
|
||||||
|
Bootstrap(app)
|
||||||
|
_LOGIN_MANAGER_.init_app(app)
|
||||||
|
_LOGIN_MANAGER_.login_message = "You must be logged in to access this page."
|
||||||
|
_LOGIN_MANAGER_.login_view = "auth.login"
|
||||||
|
|
||||||
|
from .admin.views import ADMIN as ADMIN_BLUEPRINT
|
||||||
|
app.register_blueprint(ADMIN_BLUEPRINT, url_prefix='/admin')
|
||||||
|
|
||||||
|
from .auth.views import AUTH as AUTH_BLUEPRINT
|
||||||
|
app.register_blueprint(AUTH_BLUEPRINT)
|
||||||
|
|
||||||
|
from .home.views import HOME as HOME_BLUEPRINT
|
||||||
|
app.register_blueprint(HOME_BLUEPRINT)
|
||||||
|
|
||||||
|
from .comics.views import COMIC as COMIC_BLUEPRINT
|
||||||
|
app.register_blueprint(COMIC_BLUEPRINT, url_prefix='/comics')
|
||||||
|
|
||||||
|
from .library.views import LIBRARY as LIBRARY_BLUEPRINT
|
||||||
|
app.register_blueprint(LIBRARY_BLUEPRINT, url_prefix='/library')
|
||||||
|
|
||||||
|
from .tysc.views import TYSC as TYSC_BLUEPRINT
|
||||||
|
app.register_blueprint(TYSC_BLUEPRINT, url_prefix='/tysc')
|
||||||
|
|
||||||
|
return app
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
"""
|
||||||
|
Module admin implements administration functions.
|
||||||
|
"""
|
||||||
|
from . import views
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
"""
|
||||||
|
Define form to edit users
|
||||||
|
"""
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, SubmitField
|
||||||
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
|
|
||||||
|
class UserEditForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form for admin to edit users
|
||||||
|
"""
|
||||||
|
email = StringField('Email', validators=[DataRequired()])
|
||||||
|
username = StringField('Username', validators=[DataRequired()])
|
||||||
|
first_name = StringField('First Name', validators=[DataRequired()])
|
||||||
|
last_name = StringField('Last Name', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
"""
|
||||||
|
Define routing rules for managing users
|
||||||
|
"""
|
||||||
|
from flask import abort, Blueprint, flash, redirect, render_template, url_for
|
||||||
|
from flask_login import current_user, login_required
|
||||||
|
|
||||||
|
from .forms import UserEditForm
|
||||||
|
from ..models import User
|
||||||
|
|
||||||
|
ADMIN = Blueprint('admin', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
def check_admin():
|
||||||
|
"""
|
||||||
|
Prevent non-admins from accessing the page
|
||||||
|
"""
|
||||||
|
if not current_user.is_admin:
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
|
||||||
|
# User Views
|
||||||
|
@ADMIN.route('/users')
|
||||||
|
@login_required
|
||||||
|
def list_users():
|
||||||
|
"""
|
||||||
|
List all users
|
||||||
|
"""
|
||||||
|
check_admin()
|
||||||
|
|
||||||
|
users = User.objects.all()
|
||||||
|
return render_template('admin/users/users.html',
|
||||||
|
users=users, title='Users')
|
||||||
|
|
||||||
|
|
||||||
|
@ADMIN.route('/users/edit/<user_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_user(user_id):
|
||||||
|
"""
|
||||||
|
Edit an user.
|
||||||
|
"""
|
||||||
|
check_admin()
|
||||||
|
|
||||||
|
user = User.objects.get({'username': user_id})
|
||||||
|
|
||||||
|
# prevent admin from being assigned a department or role
|
||||||
|
if user.is_admin:
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
form = UserEditForm(obj=user)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
user.email = form.email.data
|
||||||
|
user.username = form.username.data
|
||||||
|
user.first_name = form.first_name.data
|
||||||
|
user.last_name = form.last_name.data
|
||||||
|
user.save()
|
||||||
|
flash('You have successfully edited an user.')
|
||||||
|
|
||||||
|
# redirect to the roles page
|
||||||
|
return redirect(url_for('admin.list_users'))
|
||||||
|
|
||||||
|
return render_template('admin/users/user.html',
|
||||||
|
user=user, form=form,
|
||||||
|
title='Edit User')
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
"""
|
||||||
|
Module auth implements authentication functions.
|
||||||
|
"""
|
||||||
|
from . import views
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
"""
|
||||||
|
Contains forms for authentication.
|
||||||
|
"""
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import PasswordField, StringField, SubmitField, ValidationError
|
||||||
|
from wtforms.validators import DataRequired, Email, EqualTo
|
||||||
|
|
||||||
|
from ..models import User
|
||||||
|
|
||||||
|
|
||||||
|
class RegistrationForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form for users to create new account
|
||||||
|
"""
|
||||||
|
email = StringField('Email', validators=[DataRequired(), Email()])
|
||||||
|
username = StringField('Username', validators=[DataRequired()])
|
||||||
|
first_name = StringField('First Name', validators=[DataRequired()])
|
||||||
|
last_name = StringField('Last Name', validators=[DataRequired()])
|
||||||
|
password = PasswordField('Password', validators=[DataRequired(),
|
||||||
|
EqualTo('confirm_password')
|
||||||
|
])
|
||||||
|
confirm_password = PasswordField('Confirm Password')
|
||||||
|
submit = SubmitField('Register')
|
||||||
|
|
||||||
|
def validate_email(self, field):
|
||||||
|
"""
|
||||||
|
Check if email is already in use.
|
||||||
|
:param field:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if User.objects.raw({'email': field.data}).count() > 0:
|
||||||
|
raise ValidationError('Email is already in use.')
|
||||||
|
|
||||||
|
def validate_username(self, field):
|
||||||
|
"""
|
||||||
|
check if username is already in use.
|
||||||
|
:param field:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
if User.objects.raw({'username': field.data}).count() > 0:
|
||||||
|
raise ValidationError('Username is already in use.')
|
||||||
|
|
||||||
|
|
||||||
|
class LoginForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form for users to login
|
||||||
|
"""
|
||||||
|
email = StringField('Email', validators=[DataRequired(), Email()])
|
||||||
|
password = PasswordField('Password', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Login')
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
"""
|
||||||
|
Define routing rules for registering users and login and logout.
|
||||||
|
"""
|
||||||
|
from flask import Blueprint, flash, redirect, render_template, url_for
|
||||||
|
from flask_login import login_required, login_user, logout_user
|
||||||
|
from .forms import LoginForm, RegistrationForm
|
||||||
|
from ..models import User
|
||||||
|
|
||||||
|
AUTH = Blueprint('auth', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@AUTH.route('/register', methods=['GET', 'POST'])
|
||||||
|
def register():
|
||||||
|
"""
|
||||||
|
Handle requests to the /register route
|
||||||
|
Add an employee to the database through the registration form
|
||||||
|
"""
|
||||||
|
form = RegistrationForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
user = User()
|
||||||
|
user.email = form.email.data
|
||||||
|
user.username = form.username.data
|
||||||
|
user.first_name = form.first_name.data
|
||||||
|
user.last_name = form.last_name.data
|
||||||
|
user.password = form.password.data
|
||||||
|
# add employee to the database
|
||||||
|
user.save()
|
||||||
|
flash('You have successfully registered! You may now login.')
|
||||||
|
|
||||||
|
# redirect to the login page
|
||||||
|
return redirect(url_for('auth.login'))
|
||||||
|
|
||||||
|
# load registration template
|
||||||
|
return render_template('auth/register.html', form=form, title='Register')
|
||||||
|
|
||||||
|
|
||||||
|
@AUTH.route('/login', methods=['GET', 'POST'])
|
||||||
|
def login():
|
||||||
|
"""
|
||||||
|
Handle requests to the /login route
|
||||||
|
Validate given email with matching password
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = LoginForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
|
||||||
|
# check whether employee exists in the database and whether
|
||||||
|
# the password entered matches the password in the database
|
||||||
|
user = User.objects.get({'email': form.email.data})
|
||||||
|
if user is not None and user.verify_password(
|
||||||
|
form.password.data):
|
||||||
|
# log employee in
|
||||||
|
login_user(user)
|
||||||
|
|
||||||
|
# redirect to the appropriate dashboard page
|
||||||
|
if user.is_admin:
|
||||||
|
return redirect(url_for('home.admin_dashboard'))
|
||||||
|
else:
|
||||||
|
return redirect(url_for('home.dashboard'))
|
||||||
|
|
||||||
|
# when login details are incorrect
|
||||||
|
else:
|
||||||
|
flash('Invalid email or password.')
|
||||||
|
|
||||||
|
# load login template
|
||||||
|
return render_template('auth/login.html', form=form, title='Login')
|
||||||
|
|
||||||
|
@AUTH.route('/logout')
|
||||||
|
@login_required
|
||||||
|
def logout():
|
||||||
|
"""
|
||||||
|
Handle requests to the /logout route
|
||||||
|
Log an employee out through the logout link
|
||||||
|
"""
|
||||||
|
logout_user()
|
||||||
|
flash('You have successfully been logged out.')
|
||||||
|
|
||||||
|
# redirect to the login page
|
||||||
|
return redirect(url_for('auth.login'))
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Define routing rules for comic related information
|
||||||
|
"""
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
"""Define form to edit publisher, artists and comics"""
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, SubmitField, BooleanField, SelectField
|
||||||
|
from wtforms.validators import DataRequired
|
||||||
|
from bson import ObjectId
|
||||||
|
|
||||||
|
|
||||||
|
class PublisherForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit a Comic publisher
|
||||||
|
"""
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class ArtistForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit a Comic publisher
|
||||||
|
"""
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class ComicForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit Comics
|
||||||
|
"""
|
||||||
|
title = StringField('Title', validators=[DataRequired()])
|
||||||
|
publisher = SelectField('Publisher', coerce=ObjectId)
|
||||||
|
current_order = BooleanField('Current Order')
|
||||||
|
submit = SubmitField('Submit')
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
"""This modules declares the model for Comic related information."""
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
|
from pymongo.write_concern import WriteConcern
|
||||||
|
from pymodm import MongoModel, fields
|
||||||
|
|
||||||
|
|
||||||
|
class Publisher(MongoModel):
|
||||||
|
"""Class Publisher represents a publisher of a comic."""
|
||||||
|
name = fields.CharField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Publisher({})".format(self.name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def comics(self):
|
||||||
|
"""
|
||||||
|
Return list of comics which has reference to this publisher
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
comics = Comic.objects.raw({'publisher': self.pk})
|
||||||
|
current_app.logger.debug(comics)
|
||||||
|
return comics
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Artist(MongoModel):
|
||||||
|
"""Class Artist represents a comic artist."""
|
||||||
|
name = fields.CharField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Comic(MongoModel):
|
||||||
|
"""Class Comic represents a comic."""
|
||||||
|
title = fields.CharField()
|
||||||
|
publisher = fields.ReferenceField(Publisher)
|
||||||
|
current_order = fields.BooleanField(default=False)
|
||||||
|
completed = fields.BooleanField(default=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
"""
|
||||||
|
Define routing rules for comics, publisher and artists
|
||||||
|
"""
|
||||||
|
from flask import Blueprint, flash, redirect, render_template, url_for
|
||||||
|
from flask_login import login_required
|
||||||
|
from bson import ObjectId
|
||||||
|
from pymongo.errors import PyMongoError
|
||||||
|
from .forms import ComicForm, PublisherForm, ArtistForm
|
||||||
|
from .models import Comic, Publisher, Artist
|
||||||
|
|
||||||
|
|
||||||
|
COMIC = Blueprint('comic', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/artists')
|
||||||
|
@login_required
|
||||||
|
def list_artists():
|
||||||
|
"""
|
||||||
|
List all artists
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
artists = Artist.objects.all()
|
||||||
|
return render_template('comics/artists.html',
|
||||||
|
artists=artists, title="Artists")
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/artists/edit/<artist_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_artist(artist_id):
|
||||||
|
"""
|
||||||
|
Edit a comic artist
|
||||||
|
"""
|
||||||
|
artist = Artist.objects.get({'_id': ObjectId(artist_id)})
|
||||||
|
form = ArtistForm(obj=artist)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
artist.name = form.name.data
|
||||||
|
artist.save()
|
||||||
|
flash('You have successfully edited the artist.')
|
||||||
|
return redirect(url_for('comic.list_artists'))
|
||||||
|
form.name.data = artist.name
|
||||||
|
return render_template('simpleform.html', action="Edit",
|
||||||
|
form=form, title="Edit Artist")
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/artists/add', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def add_artist():
|
||||||
|
"""
|
||||||
|
Add a artist
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = ArtistForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
artist = Artist()
|
||||||
|
artist.name = form.name.data
|
||||||
|
try:
|
||||||
|
# add publisher to the database
|
||||||
|
artist.save()
|
||||||
|
flash('You have successfully added a new artist.')
|
||||||
|
except PyMongoError:
|
||||||
|
# in case publisher name already exists
|
||||||
|
flash('Error: artist name already exists.')
|
||||||
|
return redirect(url_for('comic.list_artists'))
|
||||||
|
return render_template('simpleform.html', action="Add",
|
||||||
|
form=form, title="Add Artist")
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/artists/delete/<artist_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_artist(artist_id):
|
||||||
|
"""
|
||||||
|
Delete a comic artist
|
||||||
|
:param artist_id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
artist = Artist.objects.raw({'_id': ObjectId(artist_id)})
|
||||||
|
if artist:
|
||||||
|
artist.delete()
|
||||||
|
flash('You have successfully deleted the comic artist.')
|
||||||
|
return redirect(url_for('comic.list_artists'))
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/publishers')
|
||||||
|
@login_required
|
||||||
|
def list_publishers():
|
||||||
|
"""
|
||||||
|
List all publishers
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
publishers = Publisher.objects.all()
|
||||||
|
return render_template('comics/publishers.html',
|
||||||
|
publishers=publishers, title="Publishers")
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/publishers/add', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def add_publisher():
|
||||||
|
"""
|
||||||
|
Add a publisher to the database
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = PublisherForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
publisher = Publisher()
|
||||||
|
publisher.name = form.name.data
|
||||||
|
try:
|
||||||
|
# add publisher to the database
|
||||||
|
publisher.save()
|
||||||
|
flash('You have successfully added a new publisher.')
|
||||||
|
except PyMongoError:
|
||||||
|
# in case publisher name already exists
|
||||||
|
flash('Error: publisher name already exists.')
|
||||||
|
return redirect(url_for('comic.list_publishers'))
|
||||||
|
return render_template('simpleform.html', action="Add",
|
||||||
|
form=form, title="Add Publisher")
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/publishers/edit/<publisher_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_publisher(publisher_id):
|
||||||
|
"""
|
||||||
|
Edit a publisher
|
||||||
|
"""
|
||||||
|
publisher = Publisher.objects.get({'_id': ObjectId(publisher_id)})
|
||||||
|
form = PublisherForm(obj=publisher)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
publisher.name = form.name.data
|
||||||
|
publisher.save()
|
||||||
|
flash('You have successfully edited the publisher.')
|
||||||
|
return redirect(url_for('comic.list_publishers'))
|
||||||
|
form.name.data = publisher.name
|
||||||
|
return render_template('simpleform.html', action="Edit",
|
||||||
|
form=form, title="Edit Publisher")
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/publishers/delete/<publisher_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_publisher(publisher_id):
|
||||||
|
"""
|
||||||
|
Delete a publisher
|
||||||
|
:param publisher_id: ObjectId of publisher
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
publisher = Publisher.objects.raw({'_id': ObjectId(publisher_id)})
|
||||||
|
if publisher:
|
||||||
|
publisher.delete()
|
||||||
|
flash('You have successfully deleted the publisher.')
|
||||||
|
return redirect(url_for('comic.list_publishers'))
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/comics')
|
||||||
|
@login_required
|
||||||
|
def list_comics():
|
||||||
|
"""
|
||||||
|
List all comics
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
comics = Comic.objects.all()
|
||||||
|
return render_template('comics/comics.html',
|
||||||
|
comics=comics, title="Comics")
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/comics/edit/<comic_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_comic(comic_id):
|
||||||
|
"""
|
||||||
|
Edit a comic
|
||||||
|
"""
|
||||||
|
comic = Comic.objects.get({'_id': ObjectId(comic_id)})
|
||||||
|
form = ComicForm(obj=comic)
|
||||||
|
form.publisher.choices = [(p.pk, p.name) for p in Publisher.objects.all()]
|
||||||
|
form.publisher.default = comic.publisher.pk
|
||||||
|
form.publisher.process_data(comic.publisher.pk)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
comic.title = form.title.data
|
||||||
|
comic.current_order = form.current_order.data
|
||||||
|
comic.publisher = form.publisher.data
|
||||||
|
comic.save()
|
||||||
|
flash('You have successfully edited the comic.')
|
||||||
|
return redirect(url_for('comic.list_comics'))
|
||||||
|
form.title.data = comic.title
|
||||||
|
form.current_order.data = comic.current_order
|
||||||
|
return render_template('simpleform.html', action="Edit",
|
||||||
|
form=form, title="Edit Comic")
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/comics/add', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def add_comic():
|
||||||
|
"""
|
||||||
|
Add a comic
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = ComicForm()
|
||||||
|
form.publisher.choices = [(p.pk, p.name) for p in Publisher.objects.all()]
|
||||||
|
if form.validate_on_submit():
|
||||||
|
comic = Comic()
|
||||||
|
comic.title = form.title.data
|
||||||
|
comic.publisher = form.publisher.data
|
||||||
|
try:
|
||||||
|
comic.save()
|
||||||
|
flash('You have successfully added a new comic.')
|
||||||
|
except PyMongoError:
|
||||||
|
flash('Error: comic title already exists.')
|
||||||
|
return redirect(url_for('comic.list_comics'))
|
||||||
|
return render_template('simpleform.html', action="Add",
|
||||||
|
form=form, title="Add Comic")
|
||||||
|
|
||||||
|
|
||||||
|
@COMIC.route('/comics/delete/<comic_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_comic(comic_id):
|
||||||
|
"""
|
||||||
|
Delete a comic
|
||||||
|
:param comic_id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
comic = Comic.objects.raw({'_id': ObjectId(comic_id)})
|
||||||
|
if comic:
|
||||||
|
comic.delete()
|
||||||
|
flash('You have successfully deleted the comic.')
|
||||||
|
return redirect(url_for('comic.list_comics'))
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Module for the Kontor homepage.
|
||||||
|
"""
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
"""
|
||||||
|
Define routing rules for homepage and dashboards
|
||||||
|
"""
|
||||||
|
|
||||||
|
from flask import Blueprint, render_template, abort
|
||||||
|
from flask_login import login_required, current_user
|
||||||
|
|
||||||
|
HOME = Blueprint('home', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@HOME.route('/')
|
||||||
|
def homepage():
|
||||||
|
"""
|
||||||
|
Render the homepage template on the / route
|
||||||
|
"""
|
||||||
|
return render_template('home/index.html', title="Welcome")
|
||||||
|
|
||||||
|
|
||||||
|
@HOME.route('/dashboard')
|
||||||
|
@login_required
|
||||||
|
def dashboard():
|
||||||
|
"""
|
||||||
|
Render the dashboard template on the /dashboard route
|
||||||
|
"""
|
||||||
|
return render_template('home/dashboard.html', title="Dashboard")
|
||||||
|
|
||||||
|
|
||||||
|
@HOME.route('/admin/dashboard')
|
||||||
|
@login_required
|
||||||
|
def admin_dashboard():
|
||||||
|
"""
|
||||||
|
Render the admin_dashboard template on the /admin/dashboard route
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# prevent non-admins from accessing the page
|
||||||
|
if not current_user.is_admin:
|
||||||
|
abort(403)
|
||||||
|
|
||||||
|
return render_template('home/admin_dashboard.html', title="Dashboard")
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Define routing rules for library related information
|
||||||
|
"""
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
"""
|
||||||
|
Define form to edit publisher, artists and books
|
||||||
|
"""
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, SubmitField, SelectField, IntegerField
|
||||||
|
from wtforms.validators import DataRequired, Optional, NumberRange
|
||||||
|
from bson import ObjectId
|
||||||
|
|
||||||
|
|
||||||
|
class PublisherForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit a Comic publisher
|
||||||
|
"""
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class AuthorForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit a Comic publisher
|
||||||
|
"""
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class BookForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit Comics
|
||||||
|
"""
|
||||||
|
title = StringField('Title', validators=[DataRequired()])
|
||||||
|
author = SelectField('Author', coerce=ObjectId)
|
||||||
|
publisher = SelectField('Publisher', coerce=ObjectId)
|
||||||
|
isbn = StringField('ISBN', validators=[Optional()])
|
||||||
|
year = IntegerField('Year', validators=[NumberRange(min=1970, max=2050)])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
"""
|
||||||
|
This module declares the model for the library related information.
|
||||||
|
"""
|
||||||
|
from pymongo.write_concern import WriteConcern
|
||||||
|
from pymodm import MongoModel, fields
|
||||||
|
|
||||||
|
|
||||||
|
class Publisher(MongoModel):
|
||||||
|
"""
|
||||||
|
Class Publisher represents a publisher of a book.
|
||||||
|
"""
|
||||||
|
name = fields.CharField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Publisher({})".format(self.name)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Author(MongoModel):
|
||||||
|
"""
|
||||||
|
Class Author represents an author of a book.
|
||||||
|
"""
|
||||||
|
name = fields.CharField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Author({})".format(self.name)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Book(MongoModel):
|
||||||
|
"""
|
||||||
|
Class book represents a book.
|
||||||
|
"""
|
||||||
|
title = fields.CharField()
|
||||||
|
author = fields.ReferenceField(Author)
|
||||||
|
publisher = fields.ReferenceField(Publisher)
|
||||||
|
isbn = fields.CharField(blank=True)
|
||||||
|
year = fields.IntegerField(blank=True)
|
||||||
|
edition = fields.CharField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Book({})".format(self.title)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
"""Define routing rules for book publishers"""
|
||||||
|
from flask import Blueprint, flash, redirect, render_template, url_for
|
||||||
|
from flask_login import login_required
|
||||||
|
from bson import ObjectId
|
||||||
|
from pymongo.errors import PyMongoError
|
||||||
|
from .forms import PublisherForm, AuthorForm, BookForm
|
||||||
|
from .models import Publisher, Author, Book
|
||||||
|
|
||||||
|
|
||||||
|
LIBRARY = Blueprint('library', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/publishers')
|
||||||
|
@login_required
|
||||||
|
def list_publishers():
|
||||||
|
"""
|
||||||
|
List all publishers
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
publishers = Publisher.objects.all()
|
||||||
|
return render_template('library/publishers.html',
|
||||||
|
publishers=publishers, title="Publishers")
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/publishers/add', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def add_publisher():
|
||||||
|
"""
|
||||||
|
Add a publisher to the database
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = PublisherForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
publisher = Publisher()
|
||||||
|
publisher.name = form.name.data
|
||||||
|
try:
|
||||||
|
publisher.save()
|
||||||
|
flash('You have successfully added a new publisher.')
|
||||||
|
except PyMongoError:
|
||||||
|
flash('Error: publisher name already exists.')
|
||||||
|
return redirect(url_for('library.list_publishers'))
|
||||||
|
return render_template('simpleform.html', action="Add",
|
||||||
|
form=form, title="Add Publisher")
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/publishers/edit/<publisher_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_publisher(publisher_id):
|
||||||
|
"""
|
||||||
|
Edit a publisher
|
||||||
|
"""
|
||||||
|
publisher = Publisher.objects.get({'_id': ObjectId(publisher_id)})
|
||||||
|
form = PublisherForm(obj=publisher)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
publisher.name = form.name.data
|
||||||
|
publisher.save()
|
||||||
|
flash('You have successfully edited the publisher.')
|
||||||
|
return redirect(url_for('library.list_publishers'))
|
||||||
|
form.name.data = publisher.name
|
||||||
|
return render_template('simpleform.html', action="Edit",
|
||||||
|
form=form, publisher=publisher, title="Edit Publisher")
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/publishers/delete/<publisher_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_publisher(publisher_id):
|
||||||
|
"""
|
||||||
|
Delete a publisher
|
||||||
|
:param publisher_id: ObjectId of publisher
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
publisher = Publisher.objects.raw({'_id': ObjectId(publisher_id)})
|
||||||
|
if publisher:
|
||||||
|
publisher.delete()
|
||||||
|
flash('You have successfully deleted the publisher.')
|
||||||
|
return redirect(url_for('library.list_publishers'))
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/authors')
|
||||||
|
@login_required
|
||||||
|
def list_authors():
|
||||||
|
"""
|
||||||
|
List all authors
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
authors = Author.objects.all()
|
||||||
|
return render_template('library/authors.html',
|
||||||
|
authors=authors, title="Authors")
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/authors/edit/<author_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_author(author_id):
|
||||||
|
"""
|
||||||
|
Edit a book artist
|
||||||
|
"""
|
||||||
|
author = Author.objects.get({'_id': ObjectId(author_id)})
|
||||||
|
form = AuthorForm(obj=author)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
author.name = form.name.data
|
||||||
|
author.save()
|
||||||
|
flash('You have successfully edited the author.')
|
||||||
|
return redirect(url_for('library.list_authors'))
|
||||||
|
form.name.data = author.name
|
||||||
|
return render_template('simpleform.html', action="Edit",
|
||||||
|
form=form, author=author, title="Edit Author")
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/authors/add', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def add_author():
|
||||||
|
"""
|
||||||
|
Add a author
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = AuthorForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
author = Author()
|
||||||
|
author.name = form.name.data
|
||||||
|
try:
|
||||||
|
author.save()
|
||||||
|
flash('You have successfully added a new author.')
|
||||||
|
except PyMongoError:
|
||||||
|
flash('Error: author name already exists.')
|
||||||
|
return redirect(url_for('library.list_authors'))
|
||||||
|
return render_template('simpleform.html', action="Add",
|
||||||
|
form=form, title="Add Author")
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/authors/delete/<author_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_author(author_id):
|
||||||
|
"""
|
||||||
|
Delete a author
|
||||||
|
:param author_id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
author = Author.objects.raw({'_id': ObjectId(author_id)})
|
||||||
|
if author:
|
||||||
|
author.delete()
|
||||||
|
flash('You have successfully deleted the author.')
|
||||||
|
return redirect(url_for('library.list_authors'))
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/books')
|
||||||
|
@login_required
|
||||||
|
def list_books():
|
||||||
|
"""
|
||||||
|
List all comics
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
books = Book.objects.all()
|
||||||
|
return render_template('library/books.html',
|
||||||
|
books=books, title="Books")
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/books/edit/<book_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_book(book_id):
|
||||||
|
"""
|
||||||
|
Edit a book
|
||||||
|
"""
|
||||||
|
book = Book.objects.get({'_id': ObjectId(book_id)})
|
||||||
|
form = BookForm(obj=book)
|
||||||
|
form.publisher.choices = [(p.pk, p.name) for p in Publisher.objects.all()]
|
||||||
|
if book.publisher:
|
||||||
|
form.publisher.default = book.publisher.pk
|
||||||
|
form.publisher.process_data(book.publisher.pk)
|
||||||
|
form.author.choices = [(a.pk, a.name) for a in Author.objects.all()]
|
||||||
|
if book.author:
|
||||||
|
form.author.default = book.author.pk
|
||||||
|
form.author.process_data(book.author.pk)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
book.title = form.title.data
|
||||||
|
book.publisher = form.publisher.data
|
||||||
|
book.author = form.author.data
|
||||||
|
if form.isbn.data:
|
||||||
|
book.isbn = form.isbn.data
|
||||||
|
else:
|
||||||
|
book.isbn = None
|
||||||
|
book.year = form.year.data
|
||||||
|
book.save()
|
||||||
|
flash('You have successfully edited the book.')
|
||||||
|
return redirect(url_for('library.list_books'))
|
||||||
|
form.title.data = book.title
|
||||||
|
return render_template('simpleform.html', action="Edit",
|
||||||
|
form=form, book=book, title="Edit Book")
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/books/add', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def add_book():
|
||||||
|
"""
|
||||||
|
Add a book
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = BookForm()
|
||||||
|
form.publisher.choices = [(p.pk, p.name) for p in Publisher.objects.all()]
|
||||||
|
form.author.choices = [(a.pk, a.name) for a in Author.objects.all()]
|
||||||
|
if form.validate_on_submit():
|
||||||
|
book = Book()
|
||||||
|
book.title = form.title.data
|
||||||
|
book.publisher = form.publisher.data
|
||||||
|
book.author = form.author.data
|
||||||
|
book.year = form.year.data
|
||||||
|
try:
|
||||||
|
book.save()
|
||||||
|
flash('You have successfully added a new book.')
|
||||||
|
except PyMongoError:
|
||||||
|
flash('Error: book title already exists.')
|
||||||
|
return redirect(url_for('library.list_books'))
|
||||||
|
return render_template('simpleform.html', action="Add",
|
||||||
|
form=form, title="Add Book")
|
||||||
|
|
||||||
|
|
||||||
|
@LIBRARY.route('/books/delete/<book_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_book(book_id):
|
||||||
|
"""
|
||||||
|
Delete a book
|
||||||
|
:param book_id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
book = Book.objects.raw({'_id': ObjectId(book_id)})
|
||||||
|
if book:
|
||||||
|
book.delete()
|
||||||
|
flash('You have successfully deleted the book.')
|
||||||
|
return redirect(url_for('library.list_books'))
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
"""
|
||||||
|
This modules declares the model for Kontor users.
|
||||||
|
"""
|
||||||
|
from pymodm import MongoModel, fields
|
||||||
|
from pymongo.write_concern import WriteConcern
|
||||||
|
from flask_login import UserMixin
|
||||||
|
from werkzeug.security import generate_password_hash, check_password_hash
|
||||||
|
from kontor import _LOGIN_MANAGER_
|
||||||
|
|
||||||
|
|
||||||
|
class User(UserMixin, MongoModel):
|
||||||
|
"""
|
||||||
|
This class represents an user for the Kontor application.
|
||||||
|
"""
|
||||||
|
email = fields.EmailField()
|
||||||
|
username = fields.CharField(max_length=60)
|
||||||
|
first_name = fields.CharField(max_length=60)
|
||||||
|
last_name = fields.CharField(max_length=60)
|
||||||
|
password_hash = fields.CharField(max_length=128)
|
||||||
|
is_admin = fields.BooleanField(default=False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def password(self):
|
||||||
|
"""
|
||||||
|
Prevent pasword from being accessed
|
||||||
|
"""
|
||||||
|
raise AttributeError('password is not a readable attribute.')
|
||||||
|
|
||||||
|
@password.setter
|
||||||
|
def password(self, password):
|
||||||
|
"""
|
||||||
|
Set password to a hashed password
|
||||||
|
"""
|
||||||
|
self.password_hash = generate_password_hash(password)
|
||||||
|
|
||||||
|
def verify_password(self, password):
|
||||||
|
"""
|
||||||
|
Check if hashed password matches actual password
|
||||||
|
"""
|
||||||
|
return check_password_hash(self.password_hash, password)
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
"""
|
||||||
|
Get username as id
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return self.username
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<User: {}>'.format(self.username)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
# Set up user_loader
|
||||||
|
@_LOGIN_MANAGER_.user_loader
|
||||||
|
def load_user(user_name):
|
||||||
|
"""
|
||||||
|
Get list of users from database
|
||||||
|
:param user_name:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return User.objects.get({'username': user_name})
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
"""
|
||||||
|
Define routing rules for office related information
|
||||||
|
"""
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
/* app/static/css/style.css */
|
||||||
|
|
||||||
|
body, html {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body, h1, h2, h3 {
|
||||||
|
font-family: "Lato", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
a, .navbar-default .navbar-brand, .navbar-default .navbar-nav>li>a {
|
||||||
|
color: #aec251;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover, .navbar-default .navbar-brand:hover, .navbar-default .navbar-nav>li>a:hover {
|
||||||
|
color: #687430;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
padding: 50px 0;
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.copyright {
|
||||||
|
margin: 15px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-info {
|
||||||
|
width: 50%;
|
||||||
|
margin: auto;
|
||||||
|
color: #687430;
|
||||||
|
background-color: #e6ecca;
|
||||||
|
border-color: #aec251;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-default {
|
||||||
|
border-color: #aec251;
|
||||||
|
color: #aec251;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-default:hover {
|
||||||
|
background-color: #aec251;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
margin: auto;
|
||||||
|
width: 70%;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-section {
|
||||||
|
padding: 50px 0;
|
||||||
|
border-top: 1px solid #e7e7e7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer, .push {
|
||||||
|
clear: both;
|
||||||
|
height: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-divider {
|
||||||
|
width: 400px;
|
||||||
|
border-top: 1px solid #f8f8f8;
|
||||||
|
border-bottom: 1px solid rgba(0,0,0,0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-header {
|
||||||
|
padding-top: 50px;
|
||||||
|
padding-bottom: 50px;
|
||||||
|
text-align: center;
|
||||||
|
color: #f8f8f8;
|
||||||
|
background: url(../img/intro-bg.jpg) no-repeat center center;
|
||||||
|
background-size: cover;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-message {
|
||||||
|
position: relative;
|
||||||
|
padding-top: 20%;
|
||||||
|
padding-bottom: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-message > h1 {
|
||||||
|
margin: 0;
|
||||||
|
text-shadow: 2px 2px 3px rgba(0,0,0,0.6);
|
||||||
|
font-size: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-message > h3 {
|
||||||
|
text-shadow: 2px 2px 3px rgba(0,0,0,0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.topnav {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
min-height: 100%;
|
||||||
|
height: auto !important;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0 auto -4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outer {
|
||||||
|
display: table;
|
||||||
|
position: absolute;
|
||||||
|
height: 70%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.middle {
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<!-- app/templates/admin/employees/employee.html -->
|
||||||
|
|
||||||
|
{% import "bootstrap/wtf.html" as wtf %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Edit User{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<div class="center">
|
||||||
|
<h1> Edit User </h1>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
{{ wtf.quick_form(form) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<!-- app/templates/admin/users/users.html -->
|
||||||
|
|
||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Users{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">Users</h1>
|
||||||
|
{% if users %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="15%"> Name </th>
|
||||||
|
<th width="30%"> Email </th>
|
||||||
|
<th width="30%"> Username </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for user in users %}
|
||||||
|
{% if user.is_admin %}
|
||||||
|
<tr style="background-color: #aec251; color: white;">
|
||||||
|
<td> <i class="fa fa-key"></i> Admin </td>
|
||||||
|
<td> N/A </td>
|
||||||
|
<td> N/A </td>
|
||||||
|
<td> N/A </td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ user.first_name }} {{ user.last_name }} </td>
|
||||||
|
<td> {{ user.email }} </td>
|
||||||
|
<td> {{ user.username }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('admin.edit_user', user_id=user.username) }}">
|
||||||
|
<i class="fa fa-user-plus"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<!-- app/templates/auth/login.html -->
|
||||||
|
|
||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% import "bootstrap/wtf.html" as wtf %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Login{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<div class="center">
|
||||||
|
<h1>Login to your account</h1>
|
||||||
|
<br/>
|
||||||
|
{{ wtf.quick_form(form) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<!-- app/templates/auth/register.html -->
|
||||||
|
|
||||||
|
{% import "bootstrap/wtf.html" as wtf %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Register{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="center">
|
||||||
|
<h1>Register for an account</h1>
|
||||||
|
<br/>
|
||||||
|
{{ wtf.quick_form(form) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
<!-- app/templates/base.html -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>{{ title }} | Kontor</title>
|
||||||
|
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
|
||||||
|
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
|
||||||
|
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
|
||||||
|
<link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.ico') }}">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<nav class="navbar navbar-default navbar-fixed-top topnav" role="navigation">
|
||||||
|
<div class="container topnav">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
|
||||||
|
<span class="sr-only">Toggle navigation</span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
</button>
|
||||||
|
<a class="navbar-brand topnav" href="{{ url_for('home.homepage') }}">Kontor</a>
|
||||||
|
</div>
|
||||||
|
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
|
||||||
|
<ul class="nav navbar-nav navbar-right">
|
||||||
|
{% if current_user.is_authenticated %}
|
||||||
|
<li class="dropdown">
|
||||||
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button">Comics <span class="caret"></span> </a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="{{ url_for('comic.list_comics') }}">Comics</a></li>
|
||||||
|
<li><a href="{{ url_for('comic.list_artists') }}">Artists</a></li>
|
||||||
|
<li><a href="{{ url_for('comic.list_publishers') }}">Publishers</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown">
|
||||||
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button">Library <span class="caret"></span> </a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="{{ url_for('library.list_books') }}">Books</a></li>
|
||||||
|
<li><a href="{{ url_for('library.list_authors') }}">Authors</a></li>
|
||||||
|
<li><a href="{{ url_for('library.list_publishers') }}">Publishers</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown">
|
||||||
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button">HomeOffice <span class="caret"></span> </a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="{{ url_for('home.homepage') }}">HomeOffice</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown">
|
||||||
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button">Trading Cards <span class="caret"></span> </a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="{{ url_for('home.homepage') }}">Trading Cards</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="dropdown">
|
||||||
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button">TradeYourSportsCards <span class="caret"></span> </a>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li><a href="{{ url_for('tysc.list_sports') }}">Sports</a></li>
|
||||||
|
<li><a href="{{ url_for('tysc.list_positions') }}">Positions</a></li>
|
||||||
|
<li><a href="{{ url_for('tysc.list_teams') }}">Teams</a></li>
|
||||||
|
<li><a href="{{ url_for('tysc.list_players') }}">Players</a></li>
|
||||||
|
<li><a href="{{ url_for('tysc.list_manufacturers') }}">Manufacturers</a></li>
|
||||||
|
<li><a href="{{ url_for('tysc.list_card_sets') }}">Series</a></li>
|
||||||
|
<li><a href="{{ url_for('tysc.list_parallel_sets') }}">Parallel Sets</a></li>
|
||||||
|
<li><a href="{{ url_for('tysc.list_insert_sets') }}">Inserts</a></li>
|
||||||
|
<li><a href="{{ url_for('tysc.list_cards') }}">Cards</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
{% if current_user.is_admin %}
|
||||||
|
<li><a href="{{ url_for('home.admin_dashboard') }}">Dashboard</a></li>
|
||||||
|
<li><a href="{{ url_for('admin.list_users') }}">Users</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li><a href="{{ url_for('home.dashboard') }}">Dashboard</a></li>
|
||||||
|
{% endif %}
|
||||||
|
<li><a href="{{ url_for('auth.logout') }}">{{ current_user.username }}</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li><a href="{{ url_for('home.homepage') }}">Home</a></li>
|
||||||
|
<li><a href="{{ url_for('auth.register') }}">Register</a></li>
|
||||||
|
<li><a href="{{ url_for('auth.login') }}">Login</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="wrapper">
|
||||||
|
{% block body %}
|
||||||
|
{% endblock %}
|
||||||
|
<div class="push"></div>
|
||||||
|
</div>
|
||||||
|
<footer>
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<p class="col-lg-12">
|
||||||
|
<ul class="list-inline">
|
||||||
|
<li><a href="{{ url_for('home.homepage') }}">Home</a></li>
|
||||||
|
<li class="footer-menu-divider">⋅</li>
|
||||||
|
<li><a href="{{ url_for('auth.register') }}">Register</a></li>
|
||||||
|
<li class="footer-menu-divider">⋅</li>
|
||||||
|
<li><a href="{{ url_for('auth.login') }}">Login</a></li>
|
||||||
|
</ul>
|
||||||
|
<p class="copyright text-muted small">Version {{ version }}. Copyright © 2017. All Rights Reserved</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Comic Artists{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">Comic Artists</h1>
|
||||||
|
{% if artists %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="15%"> Name </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
<th width="15%"> Delete </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for artist in artists %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ artist.name }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('comic.edit_artist', artist_id=artist.pk) }}">
|
||||||
|
<i class="fa fa-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('comic.delete_artist', artist_id=artist.pk) }}">
|
||||||
|
<i class="fa fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center">
|
||||||
|
{% else %}
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3> No artists have been added. </h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('comic.add_artist') }}" class="btn btn-default btn-lg">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add Artist
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Comics{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">Comics</h1>
|
||||||
|
{% if comics %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="15%"> Title </th>
|
||||||
|
<th width="40%"> Publisher </th>
|
||||||
|
<th width="15%"> Current Order </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
<th width="15%"> Delete </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for comic in comics %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ comic.title }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('comic.edit_publisher', publisher_id=comic.publisher.pk) }}">
|
||||||
|
{{ comic.publisher.name }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td> {{ comic.current_order }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('comic.edit_comic', comic_id=comic.pk) }}">
|
||||||
|
<i class="fa fa-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('comic.delete_comic', comic_id=comic.pk) }}">
|
||||||
|
<i class="fa fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center">
|
||||||
|
{% else %}
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3> No comics have been added. </h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('comic.add_comic') }}" class="btn btn-default btn-lg">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add Comic
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Comic Publishers{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">Comic Publishers</h1>
|
||||||
|
{% if publishers %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="15%"> Name </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
<th width="15%"> Delete </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for publisher in publishers %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ publisher.name }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('comic.edit_publisher', publisher_id=publisher.pk) }}">
|
||||||
|
<i class="fa fa-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('comic.delete_publisher', publisher_id=publisher.pk) }}">
|
||||||
|
<i class="fa fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center">
|
||||||
|
{% else %}
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3> No comics have been added. </h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('comic.add_publisher') }}" class="btn btn-default btn-lg">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add Publisher
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
<!-- app/templates/home/admin_dashboard.html -->
|
||||||
|
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Admin Dashboard{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="intro-header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="intro-message">
|
||||||
|
<h2>Admin Dashboard</h2>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<ul>
|
||||||
|
<li><a href="{{ url_for('comic.list_comics') }}">Comics</a></li>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{{ url_for('comic.list_publishers') }}">Publishers</a> </li>
|
||||||
|
<li><a href="{{ url_for('comic.list_artists') }}">Artists</a> </li>
|
||||||
|
</ul>
|
||||||
|
<li><a href="{{ url_for('library.list_books') }}">Library</a></li>
|
||||||
|
<ul>
|
||||||
|
<li><a href="{{ url_for('library.list_publishers') }}">Publishers</a> </li>
|
||||||
|
<li><a href="{{ url_for('library.list_authors') }}">Authors</a> </li>
|
||||||
|
</ul>
|
||||||
|
<li><a href="{{ url_for('home.homepage') }}">Office</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<!-- app/templates/home/dashboard.html -->
|
||||||
|
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Dashboard{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="intro-header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="intro-message">
|
||||||
|
<h1>The Dashboard</h1>
|
||||||
|
<h3>We made it here!</h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<!-- app/templates/home/index.html -->
|
||||||
|
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Home{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="intro-header">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="intro-message">
|
||||||
|
<h1>Project Kontor</h1>
|
||||||
|
<h3>Store everything!</h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Library Authors{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">Library Authors</h1>
|
||||||
|
{% if authors %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="15%"> Name </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
<th width="15%"> Delete </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for author in authors %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ author.name }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('library.edit_author', author_id=author.pk) }}">
|
||||||
|
<i class="fa fa-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('library.delete_author', author_id=author.pk) }}">
|
||||||
|
<i class="fa fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center">
|
||||||
|
{% else %}
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3> No artists have been added. </h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('library.add_author') }}" class="btn btn-default btn-lg">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add Author
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Books{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">Books</h1>
|
||||||
|
{% if books %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="15%"> Title </th>
|
||||||
|
<th width="25%"> Author </th>
|
||||||
|
<th width="40%"> Publisher </th>
|
||||||
|
<th width="5%"> ISBN </th>
|
||||||
|
<th width="2%"> Year </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
<th width="15%"> Delete </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for book in books %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ book.title }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('library.edit_author', author_id=book.author.pk) }}">
|
||||||
|
{{ book.author.name }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('library.edit_publisher', publisher_id=book.publisher.pk) }}">
|
||||||
|
{{ book.publisher.name }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td> {{ book.isbn }} </td>
|
||||||
|
<td> {{ book.year }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('library.edit_book', book_id=book.pk) }}">
|
||||||
|
<i class="fa fa-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('library.delete_book', book_id=book.pk) }}">
|
||||||
|
<i class="fa fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center">
|
||||||
|
{% else %}
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3> No books have been added. </h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('library.add_book') }}" class="btn btn-default btn-lg">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add Book
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Book Publishers{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">Book Publishers</h1>
|
||||||
|
{% if publishers %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="15%"> Name </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
<th width="15%"> Delete </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for publisher in publishers %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ publisher.name }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('library.edit_publisher', publisher_id=publisher.pk) }}">
|
||||||
|
<i class="fa fa-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('library.delete_publisher', publisher_id=publisher.pk) }}">
|
||||||
|
<i class="fa fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center">
|
||||||
|
{% else %}
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3> No publishers have been added. </h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('library.add_publisher') }}" class="btn btn-default btn-lg">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add Publisher
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{% import "bootstrap/wtf.html" as wtf %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<div class="center">
|
||||||
|
<h1> {{ title }} </h1>
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
{{ wtf.quick_form(form) }}
|
||||||
|
<br/>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">{{ title }}</h1>
|
||||||
|
{% if manufacturers %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="15%"> Name </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
<th width="15%"> Delete </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for manufacturer in manufacturers %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ manufacturer.name }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('tysc.edit_manufacturer', manufacturer_id=manufacturer.pk) }}">
|
||||||
|
<i class="fa fa-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('tysc.delete_manufacturer', manufacturer_id=manufacturer.pk) }}">
|
||||||
|
<i class="fa fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center">
|
||||||
|
{% else %}
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3> No manufacturers have been added. </h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('tysc.add_manufacturer') }}" class="btn btn-default btn-lg">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add Manufacturer
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">{{ title }}</h1>
|
||||||
|
{% if players %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="15%"> First Name </th>
|
||||||
|
<th width="15%"> Last Name </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
<th width="15%"> Delete </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for player in players %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ player.first_name }} </td>
|
||||||
|
<td> {{ player.last_name }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('tysc.edit_player', player_id=player.pk) }}">
|
||||||
|
<i class="fa fa-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('tysc.delete_player', player_id=player.pk) }}">
|
||||||
|
<i class="fa fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center">
|
||||||
|
{% else %}
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3> No players have been added. </h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('tysc.add_player') }}" class="btn btn-default btn-lg">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add Player
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}{{ title }}{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">{{ title }}</h1>
|
||||||
|
{% if positions %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="25%"> Name </th>
|
||||||
|
<th width="15%"> Description </th>
|
||||||
|
<th width="15%"> Sport </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
<th width="15%"> Delete </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for position in positions %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ position.name }} </td>
|
||||||
|
<td> {{ position.description }} </td>
|
||||||
|
<td> {{ position.sport.name }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('tysc.edit_position', position_id=position.pk) }}">
|
||||||
|
<i class="fa fa-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('tysc.delete_position', position_id=position.pk) }}">
|
||||||
|
<i class="fa fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center">
|
||||||
|
{% else %}
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3> No positions have been added. </h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('tysc.add_position') }}" class="btn btn-default btn-lg">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add Position
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Sports{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">Sports</h1>
|
||||||
|
{% if sports %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="15%"> Name </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
<th width="15%"> Delete </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for sport in sports %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ sport.name }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('tysc.edit_sport', sport_id=sport.pk) }}">
|
||||||
|
<i class="fa fa-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('tysc.delete_sport', sport_id=sport.pk) }}">
|
||||||
|
<i class="fa fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center">
|
||||||
|
{% else %}
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3> No sports have been added. </h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('tysc.add_sport') }}" class="btn btn-default btn-lg">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add Sport
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
{% import "bootstrap/utils.html" as utils %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% block title %}Teams{% endblock %}
|
||||||
|
{% block body %}
|
||||||
|
<div class="content-section">
|
||||||
|
<div class="outer">
|
||||||
|
<div class="middle">
|
||||||
|
<div class="inner">
|
||||||
|
<br/>
|
||||||
|
{{ utils.flashed_messages() }}
|
||||||
|
<br/>
|
||||||
|
<h1 style="text-align:center;">Teams</h1>
|
||||||
|
{% if teams %}
|
||||||
|
<hr class="intro-divider">
|
||||||
|
<div class="center">
|
||||||
|
<table class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th width="25%"> Name </th>
|
||||||
|
<th width="15%"> Shortname </th>
|
||||||
|
<th width="15%"> Edit </th>
|
||||||
|
<th width="15%"> Delete </th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for team in teams %}
|
||||||
|
<tr>
|
||||||
|
<td> {{ team.name }} </td>
|
||||||
|
<td> {{ team.shortname }} </td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('tysc.edit_team', team_id=team.pk) }}">
|
||||||
|
<i class="fa fa-pencil"></i> Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<a href="{{ url_for('tysc.delete_team', team_id=team.pk) }}">
|
||||||
|
<i class="fa fa-trash"></i> Delete
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: center">
|
||||||
|
{% else %}
|
||||||
|
<div style="text-align: center">
|
||||||
|
<h3> No teams have been added. </h3>
|
||||||
|
<hr class="intro-divider">
|
||||||
|
{% endif %}
|
||||||
|
<a href="{{ url_for('tysc.add_team') }}" class="btn btn-default btn-lg">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
Add Team
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,377 @@
|
|||||||
|
"""
|
||||||
|
Define routing rules for TradeYourSportsCards related information
|
||||||
|
"""
|
||||||
|
from .models import Sport, Team, Position, Player
|
||||||
|
from .models import Manufacturer, CardSet, ParallelSet, InsertSet, Card
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_model():
|
||||||
|
"""
|
||||||
|
Initialize collections for module TradeYourSportsCards.
|
||||||
|
"""
|
||||||
|
initialize_sport()
|
||||||
|
initialize_position()
|
||||||
|
initialize_teams()
|
||||||
|
initialize_players()
|
||||||
|
initialize_manufacturers()
|
||||||
|
initialize_card_sets()
|
||||||
|
initialize_parallel_sets()
|
||||||
|
initialize_insert_sets()
|
||||||
|
initialize_cards()
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_sport():
|
||||||
|
"""
|
||||||
|
Initialize collection Sport with american sports.
|
||||||
|
"""
|
||||||
|
for sport in Sport.objects.all():
|
||||||
|
sport.delete()
|
||||||
|
sports = ["Baseball", "Basketball", "Football", "Hockey"]
|
||||||
|
for sport_name in sports:
|
||||||
|
sport = Sport()
|
||||||
|
sport.name = sport_name
|
||||||
|
sport.save()
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_position():
|
||||||
|
"""
|
||||||
|
Initialize collection Position with data.
|
||||||
|
"""
|
||||||
|
for position in Position.objects.all():
|
||||||
|
position.delete()
|
||||||
|
positions = {
|
||||||
|
'Quarterback': {'short': 'QB', 'sport': 'Football'},
|
||||||
|
'Wide Receiver': {'short': 'WR', 'sport': 'Football'},
|
||||||
|
'Runningback': {'short': 'RB', 'sport': 'Football'},
|
||||||
|
'Linebacker': {'short': 'LB', 'sport': 'Football'},
|
||||||
|
'Tight End': {'short': 'TE', 'sport': 'Football'},
|
||||||
|
'Fullback': {'short': 'FB', 'sport': 'Football'},
|
||||||
|
'Strong Safety': {'short': 'SS', 'sport': 'Football'},
|
||||||
|
'Defensive End': {'short': 'DE', 'sport': 'Football'},
|
||||||
|
'Kicker': {'short': 'K', 'sport': 'Football'},
|
||||||
|
'Punter': {'short': 'P', 'sport': 'Football'},
|
||||||
|
'Left Guard': {'short': 'LG', 'sport': 'Football'},
|
||||||
|
'Right Guard': {'short': 'RG', 'sport': 'Football'},
|
||||||
|
'Offensive Tackle': {'short': 'OT', 'sport': 'Football'},
|
||||||
|
'Defensive Back': {'short': 'DB', 'sport': 'Football'},
|
||||||
|
'Cornerback': {'short': 'CB', 'sport': 'Football'},
|
||||||
|
'Catcher': {'short': 'C', 'sport': 'Baseball'},
|
||||||
|
'First Base': {'short': '1B', 'sport': 'Baseball'},
|
||||||
|
'Second Base': {'short': '2B', 'sport': 'Baseball'},
|
||||||
|
'Third Base': {'short': '3B', 'sport': 'Baseball'},
|
||||||
|
'Shortstop': {'short': 'SS', 'sport': 'Baseball'},
|
||||||
|
'Left Field': {'short': 'LF', 'sport': 'Baseball'},
|
||||||
|
'Center Field': {'short': 'CF', 'sport': 'Baseball'},
|
||||||
|
'Right Field': {'short': 'RF', 'sport': 'Baseball'},
|
||||||
|
'Designated Hitter': {'short': 'DH', 'sport': 'Baseball'},
|
||||||
|
'Pitcher': {'short': 'P', 'sport': 'Baseball'}
|
||||||
|
}
|
||||||
|
for key in positions:
|
||||||
|
sport = Sport.objects.get({'name': positions.get(key)['sport']})
|
||||||
|
position = Position()
|
||||||
|
position.description = key
|
||||||
|
position.name = positions.get(key)['short']
|
||||||
|
position.sport = sport
|
||||||
|
position.save()
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_teams():
|
||||||
|
"""
|
||||||
|
Initialize collection Team with data.
|
||||||
|
"""
|
||||||
|
for team in Team.objects.all():
|
||||||
|
team.delete()
|
||||||
|
teams = {'Buffalo Bills': {'short': 'Bills', 'sport': 'Football'},
|
||||||
|
'Indianapolis Colts': {'short': 'Colts', 'sport': 'Football'},
|
||||||
|
'Miami Dolphins': {'short': 'Dolphins', 'sport': 'Football'},
|
||||||
|
'New England Patriots': {'short': 'Patriots', 'sport': 'Football'},
|
||||||
|
'New York Jets': {'short': 'Jets', 'sport': 'Football'},
|
||||||
|
'Baltimore Ravens': {'short': 'Ravens', 'sport': 'Football'},
|
||||||
|
'Cincinnati Bengals': {'short': 'Bengals', 'sport': 'Football'},
|
||||||
|
'Cleveland Browns': {'short': 'Browns', 'sport': 'Football'},
|
||||||
|
'Jacksonville Jaguars': {'short': 'Jaguars', 'sport': 'Football'},
|
||||||
|
'Pittsburgh Steelers': {'short': 'Steelers', 'sport': 'Football'},
|
||||||
|
'Tennessee Titans': {'short': 'Titans', 'sport': 'Football'},
|
||||||
|
'Denver Broncos': {'short': 'Broncos', 'sport': 'Football'},
|
||||||
|
'Kansas City Chiefs': {'short': 'Chiefs', 'sport': 'Football'},
|
||||||
|
'Oakland Raiders': {'short': 'Raiders', 'sport': 'Football'},
|
||||||
|
'San Diego Chargers': {'short': 'Chargers', 'sport': 'Football'},
|
||||||
|
'Seattle Seahawks': {'short': 'Seahawks', 'sport': 'Football'},
|
||||||
|
'Arizona Cardinals': {'short': 'Cardinals', 'sport': 'Football'},
|
||||||
|
'Dallas Cowboys': {'short': 'Cowboys', 'sport': 'Football'},
|
||||||
|
'New York Giants': {'short': 'Giants', 'sport': 'Football'},
|
||||||
|
'Philadelphia Eagles': {'short': 'Eagles', 'sport': 'Football'},
|
||||||
|
'Washington Redskins': {'short': 'Redskins', 'sport': 'Football'},
|
||||||
|
'Chicago Bears': {'short': 'Bears', 'sport': 'Football'},
|
||||||
|
'Detroit Lions': {'short': 'Lions', 'sport': 'Football'},
|
||||||
|
'Green Bay Packers': {'short': 'Packers', 'sport': 'Football'},
|
||||||
|
'Minnesota Vikings': {'short': 'Vikings', 'sport': 'Football'},
|
||||||
|
'Tampa Bay Buccaneers': {'short': 'Buccaneers', 'sport': 'Football'},
|
||||||
|
'Atlanta Falcons': {'short': 'Falcons', 'sport': 'Football'},
|
||||||
|
'Carolina Panthers': {'short': 'Panthers', 'sport': 'Football'},
|
||||||
|
'New Orleans Saints': {'short': 'Saints', 'sport': 'Football'},
|
||||||
|
'St.Louis Rams': {'short': 'Rams', 'sport': 'Football'},
|
||||||
|
'San Francisco 49ers': {'short': '49ers', 'sport': 'Football'},
|
||||||
|
'Baltimore Orioles': {'short': 'Orioles', 'sport': 'Baseball'},
|
||||||
|
'Boston Red Sox': {'short': 'Red Sox', 'sport': 'Baseball'},
|
||||||
|
'New York Yankees': {'short': 'Yankees', 'sport': 'Baseball'},
|
||||||
|
'Tampa Bay Devil Rays': {'short': 'Devil Rays', 'sport': 'Baseball'},
|
||||||
|
'Toronto Blue Jays': {'short': 'Blue Jays', 'sport': 'Baseball'},
|
||||||
|
'Chicago White Sox': {'short': 'White Sox', 'sport': 'Baseball'},
|
||||||
|
'Cleveland Indians': {'short': 'Indians', 'sport': 'Baseball'},
|
||||||
|
'Detroit Tigers': {'short': 'Tigers', 'sport': 'Baseball'},
|
||||||
|
'Kansas City Royals': {'short': 'Royals', 'sport': 'Baseball'},
|
||||||
|
'Minnesota Twins': {'short': 'Twins', 'sport': 'Baseball'},
|
||||||
|
'Anaheim Angels': {'short': 'Angels', 'sport': 'Baseball'},
|
||||||
|
'Oakland Athletics': {'short': 'Athletics', 'sport': 'Baseball'},
|
||||||
|
'Seattle Mariners': {'short': 'Mariners', 'sport': 'Baseball'},
|
||||||
|
'Texas Rangers': {'short': 'Rangers', 'sport': 'Baseball'},
|
||||||
|
'Atlanta Braves': {'short': 'Braves', 'sport': 'Baseball'},
|
||||||
|
'Florida Marlins': {'short': 'Marlins', 'sport': 'Baseball'},
|
||||||
|
'Montreal Expos': {'short': 'Expos', 'sport': 'Baseball'},
|
||||||
|
'New York Mets': {'short': 'Mets', 'sport': 'Baseball'},
|
||||||
|
'Philadelphia Phillies': {'short': 'Phillies', 'sport': 'Baseball'},
|
||||||
|
'Chicago Cubs': {'short': 'Cubs', 'sport': 'Baseball'},
|
||||||
|
'Cincinnati Reds': {'short': 'Reds', 'sport': 'Baseball'},
|
||||||
|
'Houston Astros': {'short': 'Astros', 'sport': 'Baseball'},
|
||||||
|
'Milwaukee Brewers': {'short': 'Brewers', 'sport': 'Baseball'},
|
||||||
|
'Pittsburgh Pirates': {'short': 'Pirates', 'sport': 'Baseball'},
|
||||||
|
'St.Louis Cardinals': {'short': 'Cardinals', 'sport': 'Baseball'},
|
||||||
|
'Arizona Diamondbacks': {'short': 'Diamondbacks', 'sport': 'Baseball'},
|
||||||
|
'Colorado Rockies': {'short': 'Rockies', 'sport': 'Baseball'},
|
||||||
|
'Los Angeles Dodgers': {'short': 'Dodgers', 'sport': 'Baseball'},
|
||||||
|
'San Diego Padres': {'short': 'Padres', 'sport': 'Baseball'},
|
||||||
|
'San Francisco Giants': {'short': 'Giants', 'sport': 'Baseball'},
|
||||||
|
'Boston Celtics': {'short': 'Celtics', 'sport': 'Basketball'},
|
||||||
|
'Miami Heat': {'short': 'Heat', 'sport': 'Basketball'},
|
||||||
|
'New Jersey Nets': {'short': 'Mets', 'sport': 'Basketball'},
|
||||||
|
'New York Knicks': {'short': 'Knicks', 'sport': 'Basketball'},
|
||||||
|
'Orlando Magic': {'short': 'Magic', 'sport': 'Basketball'},
|
||||||
|
'Philadelphia 76ers': {'short': '76ers', 'sport': 'Basketball'},
|
||||||
|
'Washington Wizards': {'short': 'Wizards', 'sport': 'Basketball'},
|
||||||
|
'Atlanta Hawks': {'short': 'Hawks', 'sport': 'Basketball'},
|
||||||
|
'Charlotte Hornets': {'short': 'Hornets', 'sport': 'Basketball'},
|
||||||
|
'Chicago Bulls': {'short': 'Bulls', 'sport': 'Basketball'},
|
||||||
|
'Cleveland Cavaliers': {'short': 'Cavaliers', 'sport': 'Basketball'},
|
||||||
|
'Detroit Pistons': {'short': 'Pistons', 'sport': 'Basketball'},
|
||||||
|
'Indiana Pacers': {'short': 'Pacers', 'sport': 'Basketball'},
|
||||||
|
'Milwaukee Bucks': {'short': 'Bucks', 'sport': 'Basketball'},
|
||||||
|
'Toronto Raptors': {'short': 'Raptors', 'sport': 'Basketball'},
|
||||||
|
'Dallas Mavericks': {'short': 'Mavericks', 'sport': 'Basketball'},
|
||||||
|
'Denver Nuggets': {'short': 'Nuggets', 'sport': 'Basketball'},
|
||||||
|
'Houston Rockets': {'short': 'Rockets', 'sport': 'Basketball'},
|
||||||
|
'Minnesota Timberwolves': {'short': 'Timberwolves', 'sport': 'Basketball'},
|
||||||
|
'San Antonio Spurs': {'short': 'Spurs', 'sport': 'Basketball'},
|
||||||
|
'Utah Jazz': {'short': 'Jazz', 'sport': 'Basketball'},
|
||||||
|
'Vancouver Grizzlies': {'short': 'Grizzlies', 'sport': 'Basketball'},
|
||||||
|
'Golden State Warriors': {'short': 'Warriors', 'sport': 'Basketball'},
|
||||||
|
'Los Angeles Clippers': {'short': 'Clippers', 'sport': 'Basketball'},
|
||||||
|
'Los Angeles Lakers': {'short': 'Lakers', 'sport': 'Basketball'},
|
||||||
|
'Phoenix Suns': {'short': 'Suns', 'sport': 'Basketball'},
|
||||||
|
'Portland Trail Blazers': {'short': 'Blazers', 'sport': 'Basketball'},
|
||||||
|
'Sacramento Kings': {'short': 'Kings', 'sport': 'Basketball'},
|
||||||
|
'Seattle SuperSonics': {'short': 'SuperSonics', 'sport': 'Basketball'},
|
||||||
|
'Boston Bruins': {'short': 'Bruins', 'sport': 'Hockey'},
|
||||||
|
'Buffalo Sabres': {'short': 'Sabres', 'sport': 'Hockey'},
|
||||||
|
'Montreal Canadiens': {'short': 'Canadiens', 'sport': 'Hockey'},
|
||||||
|
'Ottawa Senators': {'short': 'Senators', 'sport': 'Hockey'},
|
||||||
|
'Toronto Maple Leafs': {'short': 'Maple Leafs', 'sport': 'Hockey'},
|
||||||
|
'New Jersey Devils': {'short': 'Devils', 'sport': 'Hockey'},
|
||||||
|
'New York Islander': {'short': 'Islander', 'sport': 'Hockey'},
|
||||||
|
'New York Rangers': {'short': 'Rangers', 'sport': 'Hockey'},
|
||||||
|
'Philadelphia Flyers': {'short': 'Flyers', 'sport': 'Hockey'},
|
||||||
|
'Pittsburgh Penguins': {'short': 'Penguins', 'sport': 'Hockey'},
|
||||||
|
'Atlanta Trashers': {'short': 'Trashers', 'sport': 'Hockey'},
|
||||||
|
'Carolina Hurricanes': {'short': 'Hurricanes', 'sport': 'Hockey'},
|
||||||
|
'Florida Panthers': {'short': 'Panthers', 'sport': 'Hockey'},
|
||||||
|
'Tampa Bay Lightnings': {'short': 'Lightnings', 'sport': 'Hockey'},
|
||||||
|
'Washington Capitals': {'short': 'Capitals', 'sport': 'Hockey'},
|
||||||
|
'Chicago Blackhawks': {'short': 'Blackhawks', 'sport': 'Hockey'},
|
||||||
|
'Columbo Blue Jackets': {'short': 'Blue Jackets', 'sport': 'Hockey'},
|
||||||
|
'Detroit Red Wings': {'short': 'Red Wings', 'sport': 'Hockey'},
|
||||||
|
'Nashville Predators': {'short': 'Predators', 'sport': 'Hockey'},
|
||||||
|
'St.Louis Blues': {'short': 'Blues', 'sport': 'Hockey'},
|
||||||
|
'Calgary Flames': {'short': 'Flames', 'sport': 'Hockey'},
|
||||||
|
'Colorado Avalanche': {'short': 'Avalanche', 'sport': 'Hockey'},
|
||||||
|
'Edmonton Oilers': {'short': 'Oilers', 'sport': 'Hockey'},
|
||||||
|
'Minnesota Wild': {'short': 'Wild', 'sport': 'Hockey'},
|
||||||
|
'Vancouver Canucks': {'short': 'Canucks', 'sport': 'Hockey'},
|
||||||
|
'Anaheim Mighty Ducks': {'short': 'Mighty Ducks', 'sport': 'Hockey'},
|
||||||
|
'Dallas Stars': {'short': 'Stars', 'sport': 'Hockey'},
|
||||||
|
'Los Angeles Kings': {'short': 'Kings', 'sport': 'Hockey'},
|
||||||
|
'Phoenix Coyotes': {'short': 'Coyotes', 'sport': 'Hockey'},
|
||||||
|
'San Jose Sharks': {'short': 'Sharks', 'sport': 'Hockey'},
|
||||||
|
'Houston Texans': {'short': 'Texans', 'sport': 'Football'},
|
||||||
|
'Houston Oilers': {'short': 'Oilers', 'sport': 'Football'}
|
||||||
|
}
|
||||||
|
for key in teams:
|
||||||
|
sport = Sport.objects.get({'name': teams.get(key)['sport']})
|
||||||
|
team = Team()
|
||||||
|
team.name = key
|
||||||
|
team.shortname = teams.get(key)['short']
|
||||||
|
team.sport = sport
|
||||||
|
team.save()
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_players():
|
||||||
|
"""
|
||||||
|
Initialize collection Manufacturer with data.
|
||||||
|
"""
|
||||||
|
for player in Player.objects.all():
|
||||||
|
player.delete()
|
||||||
|
players = ['Pathon, Jerome', 'Bruschi, Tedy', 'Couch, Tim', 'Shea, Aaron',
|
||||||
|
'Lewis, Jamal', 'Lewis, Jermaine', 'Banks, Tony', 'Fuamatu-Ma\'Afala, Chris',
|
||||||
|
'Bettis, Jerome', 'Stewart, Kordell', 'Moon, Warren', 'Lockett, Kevin',
|
||||||
|
'Gannon, Rich', 'Jett, James', 'Strong, Mack', 'Huard, Brock',
|
||||||
|
'Watters, Ricky', 'Aikman, Troy', 'LaFleur, David', 'Brazzell, Chris',
|
||||||
|
'Dayne, Ron', 'Brown, Na', 'Small, Torrance', 'Lewis, Chad', 'Murrell, Adrian',
|
||||||
|
'Smith, Maurice', 'Chandler, Chris', 'Kanell, Danny', 'Williams, Ricky',
|
||||||
|
'Garcia, Jeff', 'Streets, Tai', 'Garner, Charlie', 'Rice, Jerry',
|
||||||
|
'Owens, Terrell', 'Bruce, Isaac', 'Canidate, Trung']
|
||||||
|
for player_name in players:
|
||||||
|
player = Player()
|
||||||
|
(last_name, first_name) = player_name.split(',')
|
||||||
|
player.last_name = last_name.strip()
|
||||||
|
player.first_name = first_name.strip()
|
||||||
|
player.save()
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_manufacturers():
|
||||||
|
"""
|
||||||
|
Initialize collection Manufacturer with data.
|
||||||
|
"""
|
||||||
|
for manufacturer in Manufacturer.objects.all():
|
||||||
|
manufacturer.delete()
|
||||||
|
manufacturers = ['Pacific', 'Fleer', 'Bowman', 'Topps', 'Donruss',
|
||||||
|
'Score', 'Flair', 'Upper Deck']
|
||||||
|
for manufacturer_name in manufacturers:
|
||||||
|
manufacturer = Manufacturer()
|
||||||
|
manufacturer.name = manufacturer_name
|
||||||
|
manufacturer.save()
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_card_sets():
|
||||||
|
"""
|
||||||
|
Initialize collection CardSet with data.
|
||||||
|
"""
|
||||||
|
for card_set in CardSet.objects.all():
|
||||||
|
card_set.delete()
|
||||||
|
card_sets = {'Pacific': 'Pacific',
|
||||||
|
'Fleer': 'Fleer',
|
||||||
|
'Bowman': 'Bowman',
|
||||||
|
'Leaf': 'Topps',
|
||||||
|
'Ultra': 'Fleer',
|
||||||
|
'Mystique': 'Fleer',
|
||||||
|
'Finest Hour': 'Pacific',
|
||||||
|
'SP': 'Upper Deck',
|
||||||
|
'SPx': 'Upper Deck',
|
||||||
|
'SP Authentic': 'Upper Deck',
|
||||||
|
'Black Diamond': 'Upper Deck'
|
||||||
|
}
|
||||||
|
for set_name in card_sets:
|
||||||
|
card_set = CardSet()
|
||||||
|
card_set.name = set_name
|
||||||
|
card_set.manufacturer = Manufacturer.objects.get({'name': card_sets.get(set_name)})
|
||||||
|
card_set.save()
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_parallel_sets():
|
||||||
|
"""
|
||||||
|
Initialize collection ParallelSet with data.
|
||||||
|
"""
|
||||||
|
for parallel_set in ParallelSet.objects.all():
|
||||||
|
parallel_set.delete()
|
||||||
|
parallel_sets = {'Mystique Gold': 'Fleer',
|
||||||
|
'Pacific Copper': 'Pacific',
|
||||||
|
'Pacific Gold': 'Pacific'
|
||||||
|
}
|
||||||
|
for key in parallel_sets:
|
||||||
|
manufacturer = Manufacturer.objects.get({'name': parallel_sets.get(key)})
|
||||||
|
parallel_set = ParallelSet()
|
||||||
|
parallel_set.name = key
|
||||||
|
parallel_set.manufacturer = manufacturer
|
||||||
|
parallel_set.save()
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_insert_sets():
|
||||||
|
"""
|
||||||
|
Initialize collection InsertSet with data.
|
||||||
|
"""
|
||||||
|
for insert_set in InsertSet.objects.all():
|
||||||
|
insert_set.delete()
|
||||||
|
manufacturer = Manufacturer.objects.get({'name': 'Fleer'})
|
||||||
|
insert_set = InsertSet()
|
||||||
|
insert_set.name = 'Mystique Big Buzz'
|
||||||
|
insert_set.manufacturer = manufacturer
|
||||||
|
insert_set.save()
|
||||||
|
|
||||||
|
|
||||||
|
def initialize_cards():
|
||||||
|
"""
|
||||||
|
Initialize collection Card with data.
|
||||||
|
"""
|
||||||
|
for card in Card.objects.all():
|
||||||
|
card.delete()
|
||||||
|
players = ['Pathon, Jerome', 'Bruschi, Tedy', 'Couch, Tim', 'Shea, Aaron',
|
||||||
|
'Lewis, Jamal', 'Lewis, Jermaine', 'Banks, Tony', 'Fuamatu-Ma\'Afala, Chris',
|
||||||
|
'Bettis, Jerome', 'Stewart, Kordell', 'Moon, Warren', 'Lockett, Kevin',
|
||||||
|
'Gannon, Rich', 'Jett, James', 'Strong, Mack', 'Huard, Brock',
|
||||||
|
'Watters, Ricky', 'Aikman, Troy', 'LaFleur, David', 'Brazzell, Chris',
|
||||||
|
'Dayne, Ron', 'Brown, Na', 'Small, Torrance', 'Lewis, Chad', 'Murrell, Adrian',
|
||||||
|
'Smith, Maurice', 'Chandler, Chris', 'Kanell, Danny', 'Williams, Ricky',
|
||||||
|
'Garcia, Jeff', 'Streets, Tai', 'Garner, Charlie', 'Rice, Jerry',
|
||||||
|
'Owens, Terrell', 'Bruce, Isaac', 'Canidate, Trung']
|
||||||
|
cards = [
|
||||||
|
[0, 'Indianapolis Colts', 'Pacific', 'Pacific', None, None, False, 2001, 185],
|
||||||
|
[1, 'Indianapolis Colts', 'Pacific', 'Pacific', None, None, False, 2001, 250],
|
||||||
|
[2, 'Cleveland Browns', 'Pacific', 'Pacific', None, None, False, 2001, 103],
|
||||||
|
[3, 'Cleveland Browns', 'Pacific', 'Pacific', None, None, False, 2001, 112],
|
||||||
|
[4, 'Baltimore Ravens', 'Pacific', 'Pacific', None, None, False, 2001, 37],
|
||||||
|
[5, 'Baltimore Ravens', 'Pacific', 'Pacific', None, None, False, 2001, 38],
|
||||||
|
[6, 'Baltimore Ravens', 'Pacific', 'Pacific', None, None, False, 2001, 31],
|
||||||
|
[7, 'Pittsburgh Steelers', 'Pacific', 'Pacific', None, None, False, 2001, 338],
|
||||||
|
[8, 'Pittsburgh Steelers', 'Pacific', 'Pacific', None, None, False, 2001, 335],
|
||||||
|
[9, 'Pittsburgh Steelers', 'Pacific', 'Pacific', None, None, False, 2001, 345],
|
||||||
|
[10, 'Kansas City Chiefs', 'Pacific', 'Pacific', None, None, False, 2001, 213],
|
||||||
|
[11, 'Kansas City Chiefs', 'Pacific', 'Pacific', None, None, False, 2001, 212],
|
||||||
|
[12, 'Oakland Raiders', 'Pacific', 'Pacific', None, None, False, 2001, 311],
|
||||||
|
[13, 'Oakland Raiders', 'Pacific', 'Pacific', None, None, False, 2001, 312],
|
||||||
|
[14, 'Seattle Seahawks', 'Pacific', 'Pacific', None, None, False, 2001, 403],
|
||||||
|
[15, 'Seattle Seahawks', 'Pacific', 'Pacific', None, None, False, 2001, 397],
|
||||||
|
[16, 'Seattle Seahawks', 'Pacific', 'Pacific', None, None, False, 2001, 404],
|
||||||
|
[17, 'Dallas Cowboys', 'Pacific', 'Pacific', None, None, False, 2001, 116],
|
||||||
|
[18, 'Dallas Cowboys', 'Pacific', 'Pacific', None, None, False, 2001, 122],
|
||||||
|
[19, 'Dallas Cowboys', 'Pacific', 'Pacific', None, None, False, 2001, 117],
|
||||||
|
[20, 'New York Giants', 'Pacific', 'Pacific', None, None, False, 2001, 281],
|
||||||
|
[21, 'New York Giants', 'Pacific', 'Pacific', None, None, False, 2001, 321],
|
||||||
|
[22, 'Philadelphia Eagles', 'Pacific', 'Pacific', None, None, False, 2001, 331],
|
||||||
|
[23, 'Philadelphia Eagles', 'Pacific', 'Pacific', None, None, False, 2001, 324],
|
||||||
|
[24, 'Washington Redskins', 'Pacific', 'Pacific', None, None, False, 2001, 445],
|
||||||
|
[25, 'Atlanta Falcons', 'Pacific', 'Pacific', None, None, False, 2001, 28],
|
||||||
|
[26, 'Atlanta Falcons', 'Pacific', 'Pacific', None, None, False, 2001, 17],
|
||||||
|
[27, 'Atlanta Falcons', 'Pacific', 'Pacific', None, None, False, 2001, 23],
|
||||||
|
[28, 'New Orleans Saints', 'Pacific', 'Pacific', None, None, False, 2001, 273],
|
||||||
|
[29, 'San Francisco 49ers', 'Pacific', 'Pacific', None, None, False, 2001, 380],
|
||||||
|
[30, 'San Francisco 49ers', 'Pacific', 'Pacific', None, None, False, 2001, 390],
|
||||||
|
[31, 'San Francisco 49ers', 'Pacific', 'Pacific', None, None, False, 2001, 381],
|
||||||
|
[32, 'San Francisco 49ers', 'Pacific', 'Pacific', None, None, False, 2001, 387],
|
||||||
|
[33, 'San Francisco 49ers', 'Pacific', 'Pacific', None, None, False, 2001, 386],
|
||||||
|
[34, 'St.Louis Rams', 'Pacific', 'Pacific', None, None, False, 2001, 349],
|
||||||
|
[35, 'St.Louis Rams', 'Pacific', 'Pacific', None, None, False, 2001, 350],
|
||||||
|
]
|
||||||
|
for card_data in cards:
|
||||||
|
card = Card()
|
||||||
|
player_name = players[card_data[0]]
|
||||||
|
(last_name, first_name) = player_name.split(',')
|
||||||
|
card.player = Player.objects.get(
|
||||||
|
{'last_name': last_name.strip(), 'first_name': first_name.strip()}
|
||||||
|
)
|
||||||
|
card.team = Team.objects.get({'name': card_data[1]})
|
||||||
|
card.manufacturer = Manufacturer.objects.get({'name': card_data[2]})
|
||||||
|
card.card_set = CardSet.objects.get({'name': card_data[3]})
|
||||||
|
card.parallel_set = card_data[4]
|
||||||
|
card.insert_set = card_data[5]
|
||||||
|
card.rookie = card_data[6]
|
||||||
|
card.year = card_data[7]
|
||||||
|
card.number = card_data[8]
|
||||||
|
card.save()
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
"""
|
||||||
|
Define form to edit sport, teams, player, manufacturers and card types
|
||||||
|
"""
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField, SubmitField, SelectField, IntegerField, BooleanField
|
||||||
|
from wtforms.validators import DataRequired, Optional, NumberRange
|
||||||
|
from bson import ObjectId
|
||||||
|
|
||||||
|
|
||||||
|
class SportForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit a Sport
|
||||||
|
"""
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class TeamForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit a team
|
||||||
|
"""
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
shortname = StringField('Shortname', validators=[DataRequired()])
|
||||||
|
sport = SelectField('Sport', coerce=ObjectId)
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit a player.
|
||||||
|
"""
|
||||||
|
first_name = StringField('First name', validators=[DataRequired()])
|
||||||
|
last_name = StringField('Last name', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class PositionForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit field positions for sports
|
||||||
|
"""
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
description = StringField('Description', validators=[DataRequired()])
|
||||||
|
sport = SelectField('Sport', coerce=ObjectId)
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class ManufacturerForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit a card manufacturer.
|
||||||
|
"""
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class CardSetForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit a regular card set
|
||||||
|
"""
|
||||||
|
name = StringField('Name', validators=[DataRequired()])
|
||||||
|
manufacturer = SelectField('Manufacturer', coerce=ObjectId)
|
||||||
|
submit = SubmitField('Submit')
|
||||||
|
|
||||||
|
|
||||||
|
class CardForm(FlaskForm):
|
||||||
|
"""
|
||||||
|
Form to add and edit a trading card
|
||||||
|
"""
|
||||||
|
player = SelectField('Player', coerce=ObjectId)
|
||||||
|
team = SelectField('Team', coerce=ObjectId)
|
||||||
|
manufacturer = SelectField('Manufacturer', coerce=ObjectId)
|
||||||
|
card_set = SelectField('Card Set', coerce=ObjectId)
|
||||||
|
parallel_set = SelectField('Parallel Set', coerce=ObjectId, validators=[Optional()])
|
||||||
|
insert_set = SelectField('Inserts', coerce=ObjectId, validators=[Optional()])
|
||||||
|
rookie = BooleanField('Rookie', validators=[DataRequired()])
|
||||||
|
year = IntegerField('Year', validators=[NumberRange(min=1956, max=2020)])
|
||||||
|
number = IntegerField('Number', validators=[DataRequired()])
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
"""This modules declares the model for TradingCards related information."""
|
||||||
|
from pymongo.write_concern import WriteConcern
|
||||||
|
from pymodm import MongoModel, fields
|
||||||
|
|
||||||
|
|
||||||
|
class Sport(MongoModel):
|
||||||
|
"""Class Sport represents a sport."""
|
||||||
|
name = fields.CharField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Returns printable version of Sport object."""
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Returns printable version of Sport object."""
|
||||||
|
return "Sport({})".format(self.name)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Position(MongoModel):
|
||||||
|
"""Class Position represents the position of a player for a sport."""
|
||||||
|
name = fields.CharField(max_length=4)
|
||||||
|
description = fields.CharField(max_length=30)
|
||||||
|
sport = fields.ReferenceField(Sport)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Returns printable version of Position object."""
|
||||||
|
return "{0}({1})".format(self.name, self.description)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Returns printable version of Position object."""
|
||||||
|
return "Position({0}, {1})".format(self.name, self.description)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Team(MongoModel):
|
||||||
|
"""Class Team represents a team for a sport."""
|
||||||
|
name = fields.CharField(max_length=60)
|
||||||
|
shortname = fields.CharField(max_length=30)
|
||||||
|
sport = fields.ReferenceField(Sport, blank=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Returns printable version of Team object."""
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Returns printable version of Team object."""
|
||||||
|
return "Team({0}{1})".format(self.name, self.shortname)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Player(MongoModel):
|
||||||
|
"""
|
||||||
|
Class Player represents a player.
|
||||||
|
"""
|
||||||
|
first_name = fields.CharField(max_length=60)
|
||||||
|
last_name = fields.CharField(max_length=60)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Returns printable version of Team object."""
|
||||||
|
return "{0} {1}".format(self.first_name, self.last_name)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
"""Returns printable version of Team object."""
|
||||||
|
return "Player({0} {1})".format(self.first_name, self.last_name)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Manufacturer(MongoModel):
|
||||||
|
"""
|
||||||
|
Class Manufacturer represents a manufacturer of trading cards.
|
||||||
|
"""
|
||||||
|
name = fields.CharField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class CardSet(MongoModel):
|
||||||
|
"""
|
||||||
|
Class CardSet represents the regular card set.
|
||||||
|
"""
|
||||||
|
name = fields.CharField()
|
||||||
|
manufacturer = fields.ReferenceField(Manufacturer)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class ParallelSet(MongoModel):
|
||||||
|
"""
|
||||||
|
Class CardSet represents the parallel card set.
|
||||||
|
"""
|
||||||
|
name = fields.CharField()
|
||||||
|
manufacturer = fields.ReferenceField(Manufacturer)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class InsertSet(MongoModel):
|
||||||
|
"""
|
||||||
|
Class CardSet represents the inserts card set.
|
||||||
|
"""
|
||||||
|
name = fields.CharField()
|
||||||
|
manufacturer = fields.ReferenceField(Manufacturer)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Card(MongoModel):
|
||||||
|
"""
|
||||||
|
Class CardSet represents the regular card set.
|
||||||
|
"""
|
||||||
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
# Nine is reasonable in this case.
|
||||||
|
player = fields.ReferenceField(Player)
|
||||||
|
team = fields.ReferenceField(Team)
|
||||||
|
manufacturer = fields.ReferenceField(Manufacturer)
|
||||||
|
card_set = fields.ReferenceField(CardSet, blank=True)
|
||||||
|
parallel_set = fields.ReferenceField(ParallelSet, blank=True)
|
||||||
|
insert_set = fields.ReferenceField(InsertSet, blank=True)
|
||||||
|
rookie = fields.BooleanField(default=False)
|
||||||
|
year = fields.IntegerField(min_value=1956, max_value=2020)
|
||||||
|
number = fields.IntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Sets the connection and connections details."""
|
||||||
|
connection_alias = 'kontor'
|
||||||
|
write_concern = WriteConcern(j=True)
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
"""
|
||||||
|
Define BLueprint and Views for TradeYourSportsCards
|
||||||
|
"""
|
||||||
|
from flask import Blueprint, url_for, render_template, redirect, flash
|
||||||
|
from flask_login import login_required
|
||||||
|
from bson import ObjectId
|
||||||
|
from pymongo.errors import PyMongoError
|
||||||
|
from .forms import SportForm, TeamForm, PlayerForm, PositionForm
|
||||||
|
from .forms import ManufacturerForm
|
||||||
|
from .models import Sport, Team, Player, Position
|
||||||
|
from .models import Manufacturer, CardSet, ParallelSet, InsertSet, Card
|
||||||
|
|
||||||
|
|
||||||
|
TYSC = Blueprint('tysc', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/sport')
|
||||||
|
@login_required
|
||||||
|
def list_sports():
|
||||||
|
"""
|
||||||
|
List sports.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
sports = Sport.objects.all()
|
||||||
|
return render_template('tysc/sports.html', sports=sports, title="Sports")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/sport/edit/<sport_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_sport(sport_id):
|
||||||
|
"""
|
||||||
|
Edit a sport
|
||||||
|
"""
|
||||||
|
sport = Sport.objects.get({'_id': ObjectId(sport_id)})
|
||||||
|
form = SportForm(obj=sport)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
sport.name = form.name.data
|
||||||
|
sport.save()
|
||||||
|
flash('You have successfully edited the sport.')
|
||||||
|
return redirect(url_for('tysc.list_sports'))
|
||||||
|
form.name.data = sport.name
|
||||||
|
return render_template('simpleform.html', action="Edit",
|
||||||
|
form=form, sport=sport, title="Edit Sport")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/sport/add', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def add_sport():
|
||||||
|
"""
|
||||||
|
Add a sport
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = SportForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
sport = Sport()
|
||||||
|
sport.name = form.name.data
|
||||||
|
try:
|
||||||
|
sport.save()
|
||||||
|
flash('You have successfully added a new sport.')
|
||||||
|
except PyMongoError:
|
||||||
|
flash('Error: sport name already exists.')
|
||||||
|
return redirect(url_for('tysc.list_sports'))
|
||||||
|
return render_template('simpleform.html', action="Add",
|
||||||
|
form=form, title="Add Sport")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/sport/delete/<sport_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_sport(sport_id):
|
||||||
|
"""
|
||||||
|
Delete a sport
|
||||||
|
:param sport_id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
sport = Sport.objects.raw({'_id': ObjectId(sport_id)})
|
||||||
|
if sport:
|
||||||
|
sport.delete()
|
||||||
|
flash('You have successfully deleted the sport.')
|
||||||
|
return redirect(url_for('tysc.list_sports'))
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/team')
|
||||||
|
@login_required
|
||||||
|
def list_teams():
|
||||||
|
"""
|
||||||
|
List teams.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
teams = Team.objects.all()
|
||||||
|
return render_template('tysc/teams.html', teams=teams, title="Teams")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/team/edit/<team_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_team(team_id):
|
||||||
|
"""
|
||||||
|
Edit a team
|
||||||
|
"""
|
||||||
|
team = Team.objects.get({'_id': ObjectId(team_id)})
|
||||||
|
form = TeamForm(obj=team)
|
||||||
|
form.sport.choices = [(s.pk, s.name) for s in Sport.objects.all()]
|
||||||
|
if team.sport:
|
||||||
|
form.sport.default = team.sport.pk
|
||||||
|
form.sport.process_data(team.sport.pk)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
team.name = form.name.data
|
||||||
|
team.shortname = form.shortname.data
|
||||||
|
team.sport = form.sport.data
|
||||||
|
team.save()
|
||||||
|
flash('You have successfully edited the team.')
|
||||||
|
return redirect(url_for('tysc.list_teams'))
|
||||||
|
form.name.data = team.name
|
||||||
|
return render_template('simpleform.html', action="Edit",
|
||||||
|
form=form, team=team, title="Edit Team")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/team/add', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def add_team():
|
||||||
|
"""
|
||||||
|
Add a team
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = TeamForm()
|
||||||
|
form.sport.choices = [(s.pk, s.name) for s in Sport.objects.all()]
|
||||||
|
if form.validate_on_submit():
|
||||||
|
team = Team()
|
||||||
|
team.name = form.name.data
|
||||||
|
team.shortname = form.shortname.data
|
||||||
|
team.sport = form.sport.data
|
||||||
|
try:
|
||||||
|
team.save()
|
||||||
|
flash('You have successfully added a new team.')
|
||||||
|
except PyMongoError:
|
||||||
|
flash('Error: team name already exists.')
|
||||||
|
return redirect(url_for('tysc.list_teams'))
|
||||||
|
return render_template('simpleform.html', action="Add",
|
||||||
|
form=form, title="Add Team")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/team/delete/<team_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_team(team_id):
|
||||||
|
"""
|
||||||
|
Delete a team
|
||||||
|
:param team_id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
team = Team.objects.raw({'_id': ObjectId(team_id)})
|
||||||
|
if team:
|
||||||
|
team.delete()
|
||||||
|
return redirect(url_for('tysc.list_teams'))
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/position')
|
||||||
|
@login_required
|
||||||
|
def list_positions():
|
||||||
|
"""
|
||||||
|
List positions.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
positions = Position.objects.all()
|
||||||
|
return render_template('tysc/positions.html', positions=positions, title="Positions")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/position/edit/<position_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_position(position_id):
|
||||||
|
"""
|
||||||
|
Edit a position
|
||||||
|
"""
|
||||||
|
position = Position.objects.get({'_id': ObjectId(position_id)})
|
||||||
|
form = PositionForm(obj=position)
|
||||||
|
form.sport.choices = [(s.pk, s.name) for s in Sport.objects.all()]
|
||||||
|
if position.sport:
|
||||||
|
form.sport.default = position.sport.pk
|
||||||
|
form.sport.process_data(position.sport.pk)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
position.name = form.name.data
|
||||||
|
position.description = form.description.data
|
||||||
|
position.sport = form.sport.data
|
||||||
|
position.save()
|
||||||
|
flash('You have successfully edited the position.')
|
||||||
|
return redirect(url_for('tysc.list_positions'))
|
||||||
|
form.name.data = position.name
|
||||||
|
form.description.data = position.description
|
||||||
|
return render_template('simpleform.html', action="Edit",
|
||||||
|
form=form, position=position, title="Edit position")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/position/add', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def add_position():
|
||||||
|
"""
|
||||||
|
Add a position
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = PositionForm()
|
||||||
|
form.sport.choices = [(s.pk, s.name) for s in Sport.objects.all()]
|
||||||
|
if form.validate_on_submit():
|
||||||
|
position = Position()
|
||||||
|
position.name = form.name.data
|
||||||
|
position.description = form.description.data
|
||||||
|
position.sport = form.sport.data
|
||||||
|
try:
|
||||||
|
position.save()
|
||||||
|
flash('You have successfully added a new position.')
|
||||||
|
except PyMongoError:
|
||||||
|
flash('Error: team position already exists.')
|
||||||
|
return redirect(url_for('tysc.list_positions'))
|
||||||
|
return render_template('simpleform.html', action="Add",
|
||||||
|
form=form, title="Add Position")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/position/delete/<position_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_position(position_id):
|
||||||
|
"""
|
||||||
|
Delete a position
|
||||||
|
:param position_id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
position = Position.objects.raw({'_id': ObjectId(position_id)})
|
||||||
|
if position:
|
||||||
|
position.delete()
|
||||||
|
return redirect(url_for('tysc.list_positions'))
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/player')
|
||||||
|
@login_required
|
||||||
|
def list_players():
|
||||||
|
"""
|
||||||
|
List players.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
players = Player.objects.all()
|
||||||
|
return render_template('tysc/players.html', players=players, title="Players")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/player/edit/<player_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_player(player_id):
|
||||||
|
"""
|
||||||
|
Edit a player
|
||||||
|
"""
|
||||||
|
player = Player.objects.get({'_id': ObjectId(player_id)})
|
||||||
|
form = PlayerForm(obj=player)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
player.first_name = form.first_name.data
|
||||||
|
player.last_name = form.last_name.data
|
||||||
|
player.save()
|
||||||
|
flash('You have successfully edited the player.')
|
||||||
|
return redirect(url_for('tysc.list_players'))
|
||||||
|
form.first_name.data = player.first_name
|
||||||
|
form.last_name.data = player.last_name
|
||||||
|
return render_template('simpleform.html', action="Edit",
|
||||||
|
form=form, player=player, title="Edit player")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/player/add', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def add_player():
|
||||||
|
"""
|
||||||
|
Add a player
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = PlayerForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
player = Player()
|
||||||
|
player.first_name = form.first_name.data
|
||||||
|
player.last_name = form.last_name.data
|
||||||
|
try:
|
||||||
|
player.save()
|
||||||
|
flash('You have successfully added a new player.')
|
||||||
|
except PyMongoError:
|
||||||
|
flash('Error: player name already exists.')
|
||||||
|
return redirect(url_for('tysc.list_players'))
|
||||||
|
return render_template('simpleform.html', action="Add",
|
||||||
|
form=form, title="Add player")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/player/delete/<player_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_player(player_id):
|
||||||
|
"""
|
||||||
|
Delete a player
|
||||||
|
:param player_id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
player = Player.objects.raw({'_id': ObjectId(player_id)})
|
||||||
|
if player:
|
||||||
|
player.delete()
|
||||||
|
return redirect(url_for('tysc.list_players'))
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/manufacturer')
|
||||||
|
@login_required
|
||||||
|
def list_manufacturers():
|
||||||
|
"""
|
||||||
|
List manufacturers.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
manufacturers = Manufacturer.objects.all()
|
||||||
|
return render_template('tysc/manufacturers.html',
|
||||||
|
manufacturers=manufacturers,
|
||||||
|
title="Manufacturers")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/manufacturer/edit/<manufacturer_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def edit_manufacturer(manufacturer_id):
|
||||||
|
"""
|
||||||
|
Edit a manufacturer
|
||||||
|
"""
|
||||||
|
manufacturer = Manufacturer.objects.get({'_id': ObjectId(manufacturer_id)})
|
||||||
|
form = ManufacturerForm(obj=manufacturer)
|
||||||
|
if form.validate_on_submit():
|
||||||
|
manufacturer.name = form.name.data
|
||||||
|
manufacturer.save()
|
||||||
|
flash('You have successfully edited the manufacturer.')
|
||||||
|
return redirect(url_for('tysc.list_manufacturers'))
|
||||||
|
form.name.data = manufacturer.name
|
||||||
|
return render_template('simpleform.html', action="Edit",
|
||||||
|
form=form, manufacturer=manufacturer, title="Edit Manufacturer")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/manufacturer/add', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def add_manufacturer():
|
||||||
|
"""
|
||||||
|
Add a manufacturer
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
form = ManufacturerForm()
|
||||||
|
if form.validate_on_submit():
|
||||||
|
manufacturer = Manufacturer()
|
||||||
|
manufacturer.name = form.name.data
|
||||||
|
try:
|
||||||
|
manufacturer.save()
|
||||||
|
flash('You have successfully added a new manufacturer.')
|
||||||
|
except PyMongoError:
|
||||||
|
flash('Error: manufacturer name already exists.')
|
||||||
|
return redirect(url_for('tysc.list_manufacturers'))
|
||||||
|
return render_template('simpleform.html', action="Add",
|
||||||
|
form=form, title="Add Manufacturer")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/manufacturer/delete/<manufacturer_id>', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def delete_manufacturer(manufacturer_id):
|
||||||
|
"""
|
||||||
|
Delete a manufacturer
|
||||||
|
:param manufacturer_id:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
manufacturer = Manufacturer.objects.raw({'_id': ObjectId(manufacturer_id)})
|
||||||
|
if manufacturer:
|
||||||
|
manufacturer.delete()
|
||||||
|
flash('You have successfully deleted the manufacturer.')
|
||||||
|
return redirect(url_for('tysc.list_manufacturers'))
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/cardsets')
|
||||||
|
@login_required
|
||||||
|
def list_card_sets():
|
||||||
|
"""
|
||||||
|
List card sets.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
card_sets = CardSet.objects.all()
|
||||||
|
return render_template('tysc/cardsets.html', card_sets=card_sets, title="Card Sets")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/parallelsets')
|
||||||
|
@login_required
|
||||||
|
def list_parallel_sets():
|
||||||
|
"""
|
||||||
|
List card sets.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
parallel_sets = ParallelSet.objects.all()
|
||||||
|
return render_template('tysc/parallelsets.html',
|
||||||
|
parallel_sets=parallel_sets, title="Parallel Sets")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/insertsets')
|
||||||
|
@login_required
|
||||||
|
def list_insert_sets():
|
||||||
|
"""
|
||||||
|
List card sets.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
insert_sets = InsertSet.objects.all()
|
||||||
|
return render_template('tysc/insertsets.html', insert_sets=insert_sets, title="Inserts")
|
||||||
|
|
||||||
|
|
||||||
|
@TYSC.route('/cards')
|
||||||
|
@login_required
|
||||||
|
def list_cards():
|
||||||
|
"""
|
||||||
|
List card sets.
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
cards = Card.objects.all()
|
||||||
|
return render_template('tysc/cards.html', cards=cards, title="Cards")
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
"""This module declares the version of the Kontor application."""
|
||||||
|
__version__ = '0.0.7'
|
||||||
@@ -0,0 +1,382 @@
|
|||||||
|
[MASTER]
|
||||||
|
|
||||||
|
# Specify a configuration file.
|
||||||
|
#rcfile=
|
||||||
|
|
||||||
|
# Python code to execute, usually for sys.path manipulation such as
|
||||||
|
# pygtk.require().
|
||||||
|
init-hook='sys.path = list(); sys.path.append("./lib/python3.4/site-packages/")'
|
||||||
|
|
||||||
|
# Profiled execution.
|
||||||
|
profile=no
|
||||||
|
|
||||||
|
# Add files or directories to the blacklist. They should be base names, not
|
||||||
|
# paths.
|
||||||
|
ignore=CVS
|
||||||
|
|
||||||
|
# Pickle collected data for later comparisons.
|
||||||
|
persistent=yes
|
||||||
|
|
||||||
|
# List of plugins (as comma separated values of python modules names) to load,
|
||||||
|
# usually to register additional checkers.
|
||||||
|
load-plugins=
|
||||||
|
|
||||||
|
# Use multiple processes to speed up Pylint.
|
||||||
|
jobs=1
|
||||||
|
|
||||||
|
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||||
|
# active Python interpreter and may run arbitrary code.
|
||||||
|
unsafe-load-any-extension=no
|
||||||
|
|
||||||
|
# A comma-separated list of package or module names from where C extensions may
|
||||||
|
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||||
|
# run arbitrary code
|
||||||
|
extension-pkg-whitelist=
|
||||||
|
|
||||||
|
# Allow optimization of some AST trees. This will activate a peephole AST
|
||||||
|
# optimizer, which will apply various small optimizations. For instance, it can
|
||||||
|
# be used to obtain the result of joining multiple strings with the addition
|
||||||
|
# operator. Joining a lot of strings can lead to a maximum recursion error in
|
||||||
|
# Pylint and this flag can prevent that. It has one side effect, the resulting
|
||||||
|
# AST will be different than the one from reality.
|
||||||
|
optimize-ast=no
|
||||||
|
|
||||||
|
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
|
||||||
|
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||||
|
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
|
||||||
|
confidence=
|
||||||
|
|
||||||
|
# Enable the message, report, category or checker with the given id(s). You can
|
||||||
|
# either give multiple identifier separated by comma (,) or put this option
|
||||||
|
# multiple time. See also the "--disable" option for examples.
|
||||||
|
#enable=
|
||||||
|
|
||||||
|
# Disable the message, report, category or checker with the given id(s). You
|
||||||
|
# can either give multiple identifiers separated by comma (,) or put this
|
||||||
|
# option multiple times (only on the command line, not in the configuration
|
||||||
|
# file where it should appear only once).You can also use "--disable=all" to
|
||||||
|
# disable everything first and then reenable specific checks. For example, if
|
||||||
|
# you want to run only the similarities checker, you can use "--disable=all
|
||||||
|
# --enable=similarities". If you want to run only the classes checker, but have
|
||||||
|
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||||
|
# --disable=W"
|
||||||
|
disable=W1601,W1606,W1604,W1630,E1605,E1604,W1636,W1603,W1633,W1610,W1615,W1626,E1606,W0704,I0020,W1622,E1608,W1612,W1638,W1614,W1616,W1605,W1613,W1634,E1603,I0021,W1640,W1621,W1625,E1602,W1639,W1602,W1620,W1617,W1624,W1609,W1618,E1601,E1607,W1637,W1632,W1629,W1635,W1611,W1623,W1608,W1627,W1628,W1607,W1619,E1101,R0201,R0903,R0801,locally-disabled
|
||||||
|
|
||||||
|
|
||||||
|
[REPORTS]
|
||||||
|
|
||||||
|
# Set the output format. Available formats are text, parseable, colorized, msvs
|
||||||
|
# (visual studio) and html. You can also give a reporter class, eg
|
||||||
|
# mypackage.mymodule.MyReporterClass.
|
||||||
|
output-format=parseable
|
||||||
|
|
||||||
|
# Put messages in a separate file for each module / package specified on the
|
||||||
|
# command line instead of printing them on stdout. Reports (if any) will be
|
||||||
|
# written in a file name "pylint_global.[txt|html]".
|
||||||
|
files-output=no
|
||||||
|
|
||||||
|
# Tells whether to display a full report or only the messages
|
||||||
|
reports=yes
|
||||||
|
|
||||||
|
# Python expression which should return a note less than 10 (10 is the highest
|
||||||
|
# note). You have access to the variables errors warning, statement which
|
||||||
|
# respectively contain the number of errors / warnings messages and the total
|
||||||
|
# number of statements analyzed. This is used by the global evaluation report
|
||||||
|
# (RP0004).
|
||||||
|
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||||
|
|
||||||
|
# Add a comment according to your evaluation note. This is used by the global
|
||||||
|
# evaluation report (RP0004).
|
||||||
|
comment=no
|
||||||
|
|
||||||
|
# Template used to display messages. This is a python new-style format string
|
||||||
|
# used to format the message information. See doc for all details
|
||||||
|
#msg-template=
|
||||||
|
|
||||||
|
|
||||||
|
[SPELLING]
|
||||||
|
|
||||||
|
# Spelling dictionary name. Available dictionaries: none. To make it working
|
||||||
|
# install python-enchant package.
|
||||||
|
spelling-dict=
|
||||||
|
|
||||||
|
# List of comma separated words that should not be checked.
|
||||||
|
spelling-ignore-words=
|
||||||
|
|
||||||
|
# A path to a file that contains private dictionary; one word per line.
|
||||||
|
spelling-private-dict-file=
|
||||||
|
|
||||||
|
# Tells whether to store unknown words to indicated private dictionary in
|
||||||
|
# --spelling-private-dict-file option instead of raising a message.
|
||||||
|
spelling-store-unknown-words=no
|
||||||
|
|
||||||
|
|
||||||
|
[TYPECHECK]
|
||||||
|
|
||||||
|
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||||
|
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||||
|
ignore-mixin-members=yes
|
||||||
|
|
||||||
|
# List of module names for which member attributes should not be checked
|
||||||
|
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||||
|
# and thus existing member attributes cannot be deduced by static analysis
|
||||||
|
ignored-modules=
|
||||||
|
|
||||||
|
# List of classes names for which member attributes should not be checked
|
||||||
|
# (useful for classes with attributes dynamically set).
|
||||||
|
ignored-classes=SQLObject
|
||||||
|
|
||||||
|
# When zope mode is activated, add a predefined set of Zope acquired attributes
|
||||||
|
# to generated-members.
|
||||||
|
zope=no
|
||||||
|
|
||||||
|
# List of members which are set dynamically and missed by pylint inference
|
||||||
|
# system, and so shouldn't trigger E0201 when accessed. Python regular
|
||||||
|
# expressions are accepted.
|
||||||
|
generated-members=REQUEST,acl_users,aq_parent
|
||||||
|
|
||||||
|
|
||||||
|
[LOGGING]
|
||||||
|
|
||||||
|
# Logging modules to check that the string format arguments are in logging
|
||||||
|
# function parameter format
|
||||||
|
logging-modules=logging
|
||||||
|
|
||||||
|
|
||||||
|
[VARIABLES]
|
||||||
|
|
||||||
|
# Tells whether we should check for unused import in __init__ files.
|
||||||
|
init-import=no
|
||||||
|
|
||||||
|
# A regular expression matching the name of dummy variables (i.e. expectedly
|
||||||
|
# not used).
|
||||||
|
dummy-variables-rgx=_$|dummy
|
||||||
|
|
||||||
|
# List of additional names supposed to be defined in builtins. Remember that
|
||||||
|
# you should avoid to define new builtins when possible.
|
||||||
|
additional-builtins=
|
||||||
|
|
||||||
|
# List of strings which can identify a callback function by name. A callback
|
||||||
|
# name must start or end with one of those strings.
|
||||||
|
callbacks=cb_,_cb
|
||||||
|
|
||||||
|
|
||||||
|
[FORMAT]
|
||||||
|
|
||||||
|
# Maximum number of characters on a single line.
|
||||||
|
max-line-length=100
|
||||||
|
|
||||||
|
# Regexp for a line that is allowed to be longer than the limit.
|
||||||
|
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||||
|
|
||||||
|
# Allow the body of an if to be on the same line as the test if there is no
|
||||||
|
# else.
|
||||||
|
single-line-if-stmt=no
|
||||||
|
|
||||||
|
# List of optional constructs for which whitespace checking is disabled
|
||||||
|
no-space-check=trailing-comma,dict-separator
|
||||||
|
|
||||||
|
# Maximum number of lines in a module
|
||||||
|
max-module-lines=1000
|
||||||
|
|
||||||
|
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||||
|
# tab).
|
||||||
|
indent-string=' '
|
||||||
|
|
||||||
|
# Number of spaces of indent required inside a hanging or continued line.
|
||||||
|
indent-after-paren=4
|
||||||
|
|
||||||
|
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||||
|
expected-line-ending-format=
|
||||||
|
|
||||||
|
|
||||||
|
[BASIC]
|
||||||
|
|
||||||
|
# Required attributes for module, separated by a comma
|
||||||
|
#required-attributes=
|
||||||
|
|
||||||
|
# List of builtins function names that should not be used, separated by a comma
|
||||||
|
bad-functions=map,filter
|
||||||
|
|
||||||
|
# Good variable names which should always be accepted, separated by a comma
|
||||||
|
good-names=i,j,k,ex,Run,_
|
||||||
|
|
||||||
|
# Bad variable names which should always be refused, separated by a comma
|
||||||
|
bad-names=foo,bar,baz,toto,tutu,tata
|
||||||
|
|
||||||
|
# Colon-delimited sets of names that determine each other's naming style when
|
||||||
|
# the name regexes allow several styles.
|
||||||
|
name-group=
|
||||||
|
|
||||||
|
# Include a hint for the correct naming format with invalid-name
|
||||||
|
include-naming-hint=yes
|
||||||
|
|
||||||
|
# Regular expression matching correct class attribute names
|
||||||
|
class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
||||||
|
|
||||||
|
# Naming hint for class attribute names
|
||||||
|
class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$
|
||||||
|
|
||||||
|
# Regular expression matching correct function names
|
||||||
|
function-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Naming hint for function names
|
||||||
|
function-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression matching correct attribute names
|
||||||
|
attr-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Naming hint for attribute names
|
||||||
|
attr-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression matching correct method names
|
||||||
|
method-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Naming hint for method names
|
||||||
|
method-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression matching correct argument names
|
||||||
|
argument-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Naming hint for argument names
|
||||||
|
argument-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression matching correct inline iteration names
|
||||||
|
inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
|
||||||
|
|
||||||
|
# Naming hint for inline iteration names
|
||||||
|
inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$
|
||||||
|
|
||||||
|
# Regular expression matching correct module names
|
||||||
|
module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||||
|
|
||||||
|
# Naming hint for module names
|
||||||
|
module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
|
||||||
|
|
||||||
|
# Regular expression matching correct constant names
|
||||||
|
const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||||
|
|
||||||
|
# Naming hint for constant names
|
||||||
|
const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$
|
||||||
|
|
||||||
|
# Regular expression matching correct class names
|
||||||
|
class-rgx=[A-Z_][a-zA-Z0-9]+$
|
||||||
|
|
||||||
|
# Naming hint for class names
|
||||||
|
class-name-hint=[A-Z_][a-zA-Z0-9]+$
|
||||||
|
|
||||||
|
# Regular expression matching correct variable names
|
||||||
|
variable-rgx=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Naming hint for variable names
|
||||||
|
variable-name-hint=[a-z_][a-z0-9_]{2,30}$
|
||||||
|
|
||||||
|
# Regular expression which should only match function or class names that do
|
||||||
|
# not require a docstring.
|
||||||
|
no-docstring-rgx=__.*__
|
||||||
|
|
||||||
|
# Minimum line length for functions/classes that require docstrings, shorter
|
||||||
|
# ones are exempt.
|
||||||
|
docstring-min-length=-1
|
||||||
|
|
||||||
|
|
||||||
|
[MISCELLANEOUS]
|
||||||
|
|
||||||
|
# List of note tags to take in consideration, separated by a comma.
|
||||||
|
notes=FIXME,XXX,TODO
|
||||||
|
|
||||||
|
|
||||||
|
[SIMILARITIES]
|
||||||
|
|
||||||
|
# Minimum lines number of a similarity.
|
||||||
|
min-similarity-lines=4
|
||||||
|
|
||||||
|
# Ignore comments when computing similarities.
|
||||||
|
ignore-comments=yes
|
||||||
|
|
||||||
|
# Ignore docstrings when computing similarities.
|
||||||
|
ignore-docstrings=yes
|
||||||
|
|
||||||
|
# Ignore imports when computing similarities.
|
||||||
|
ignore-imports=no
|
||||||
|
|
||||||
|
|
||||||
|
[CLASSES]
|
||||||
|
|
||||||
|
# List of interface methods to ignore, separated by a comma. This is used for
|
||||||
|
# instance to not check methods defines in Zope's Interface base class.
|
||||||
|
#ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
|
||||||
|
|
||||||
|
# List of method names used to declare (i.e. assign) instance attributes.
|
||||||
|
defining-attr-methods=__init__,__new__,setUp
|
||||||
|
|
||||||
|
# List of valid names for the first argument in a class method.
|
||||||
|
valid-classmethod-first-arg=cls
|
||||||
|
|
||||||
|
# List of valid names for the first argument in a metaclass class method.
|
||||||
|
valid-metaclass-classmethod-first-arg=mcs
|
||||||
|
|
||||||
|
# List of member names, which should be excluded from the protected access
|
||||||
|
# warning.
|
||||||
|
exclude-protected=_asdict,_fields,_replace,_source,_make
|
||||||
|
|
||||||
|
|
||||||
|
[DESIGN]
|
||||||
|
|
||||||
|
# Maximum number of arguments for function / method
|
||||||
|
max-args=5
|
||||||
|
|
||||||
|
# Argument names that match this expression will be ignored. Default to name
|
||||||
|
# with leading underscore
|
||||||
|
ignored-argument-names=_.*
|
||||||
|
|
||||||
|
# Maximum number of locals for function / method body
|
||||||
|
max-locals=15
|
||||||
|
|
||||||
|
# Maximum number of return / yield for function / method body
|
||||||
|
max-returns=6
|
||||||
|
|
||||||
|
# Maximum number of branch for function / method body
|
||||||
|
max-branches=12
|
||||||
|
|
||||||
|
# Maximum number of statements in function / method body
|
||||||
|
max-statements=50
|
||||||
|
|
||||||
|
# Maximum number of parents for a class (see R0901).
|
||||||
|
max-parents=7
|
||||||
|
|
||||||
|
# Maximum number of attributes for a class (see R0902).
|
||||||
|
max-attributes=7
|
||||||
|
|
||||||
|
# Minimum number of public methods for a class (see R0903).
|
||||||
|
min-public-methods=2
|
||||||
|
|
||||||
|
# Maximum number of public methods for a class (see R0904).
|
||||||
|
max-public-methods=20
|
||||||
|
|
||||||
|
|
||||||
|
[IMPORTS]
|
||||||
|
|
||||||
|
# Deprecated modules which should not be used, separated by a comma
|
||||||
|
deprecated-modules=stringprep,optparse
|
||||||
|
|
||||||
|
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||||
|
# given file (report RP0402 must not be disabled)
|
||||||
|
import-graph=
|
||||||
|
|
||||||
|
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||||
|
# not be disabled)
|
||||||
|
ext-import-graph=
|
||||||
|
|
||||||
|
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||||
|
# not be disabled)
|
||||||
|
int-import-graph=
|
||||||
|
|
||||||
|
|
||||||
|
[EXCEPTIONS]
|
||||||
|
|
||||||
|
# Exceptions that will emit a warning when being caught. Defaults to
|
||||||
|
# "Exception"
|
||||||
|
overgeneral-exceptions=Exception
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
flask
|
||||||
|
flask-login
|
||||||
|
flask-bootstrap
|
||||||
|
Flask-WTF
|
||||||
|
flask-testing
|
||||||
|
pymodm
|
||||||
|
nose
|
||||||
|
nose-htmloutput
|
||||||
|
coverage
|
||||||
|
pylint
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
from flask_testing import TestCase
|
||||||
|
from kontor import create_app
|
||||||
|
from kontor.models import User
|
||||||
|
|
||||||
|
|
||||||
|
class TestBase(TestCase):
|
||||||
|
|
||||||
|
def create_app(self):
|
||||||
|
|
||||||
|
# pass in test configuration
|
||||||
|
config_name = 'testing'
|
||||||
|
app = create_app(config_name)
|
||||||
|
return app
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Will be called before every test
|
||||||
|
"""
|
||||||
|
# create test admin user
|
||||||
|
admin = User()
|
||||||
|
admin.username="admin"
|
||||||
|
admin.password="admin2016"
|
||||||
|
admin.is_admin=True
|
||||||
|
admin.save()
|
||||||
|
|
||||||
|
# create test non-admin user
|
||||||
|
employee = User()
|
||||||
|
employee.username="test_user"
|
||||||
|
employee.password="test2016"
|
||||||
|
employee.save()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""
|
||||||
|
Will be called after every test
|
||||||
|
"""
|
||||||
|
users = User.objects.all()
|
||||||
|
for user in users:
|
||||||
|
user.delete()
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
"""
|
||||||
|
This module cantains tests for the comic data model
|
||||||
|
"""
|
||||||
|
import unittest
|
||||||
|
from . import TestBase
|
||||||
|
from kontor.comics.models import Artist, Comic, Publisher
|
||||||
|
|
||||||
|
|
||||||
|
class TestComicsModel(TestBase):
|
||||||
|
"""This TestCase contains tests for comic data model."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
publisher = Publisher()
|
||||||
|
publisher.name = "Publisher1"
|
||||||
|
publisher.save()
|
||||||
|
artist = Artist()
|
||||||
|
artist.name = "Artist1"
|
||||||
|
artist.save()
|
||||||
|
comic = Comic()
|
||||||
|
comic.title = "Title1"
|
||||||
|
comic.save()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
for comic in Comic.objects.all():
|
||||||
|
comic.delete()
|
||||||
|
for artist in Artist.objects.all():
|
||||||
|
artist.delete()
|
||||||
|
for publisher in Publisher.objects.all():
|
||||||
|
publisher.delete()
|
||||||
|
|
||||||
|
def test_comic_model(self):
|
||||||
|
comic_list = Comic.objects.all()
|
||||||
|
self.assertEqual(comic_list.count(), 1)
|
||||||
|
|
||||||
|
def test_publisher_model(self):
|
||||||
|
self.assertEqual(Publisher.objects.all().count(), 1)
|
||||||
|
|
||||||
|
def test_artist_model(self):
|
||||||
|
self.assertEqual(Artist.objects.all().count(), 1)
|
||||||
|
|
||||||
|
def test_assign_publisher(self):
|
||||||
|
comic = Comic.objects.first()
|
||||||
|
publisher = Publisher.objects.first()
|
||||||
|
comic.publisher = publisher
|
||||||
|
comic.save()
|
||||||
|
self.assertEqual(publisher.name, comic.publisher.name)
|
||||||
|
self.assertEqual(publisher.comics.count(), 1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
"""This module contains tests for Comic related urls."""
|
||||||
|
import unittest
|
||||||
|
from flask import url_for
|
||||||
|
from . import TestBase
|
||||||
|
|
||||||
|
|
||||||
|
class TestComicsViews(TestBase):
|
||||||
|
|
||||||
|
def test_comics_index(self):
|
||||||
|
"""
|
||||||
|
Test that comics page is inaccessible without login
|
||||||
|
and redirects to login page then to comics page
|
||||||
|
"""
|
||||||
|
target_url = url_for('comic.list_comics')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_comics_artist(self):
|
||||||
|
"""
|
||||||
|
Test that comics page is inaccessible without login
|
||||||
|
and redirects to login page then to comics page
|
||||||
|
"""
|
||||||
|
target_url = url_for('comic.list_artists')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_comics_publisher(self):
|
||||||
|
"""
|
||||||
|
Test that comics page is inaccessible without login
|
||||||
|
and redirects to login page then to comics page
|
||||||
|
"""
|
||||||
|
target_url = url_for('comic.list_publishers')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# instance/config.py
|
||||||
|
|
||||||
|
SECRET_KEY = 'p9Bv<3Eid9%$i01'
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
"""
|
||||||
|
This module cantains tests for the general Kontor data model.
|
||||||
|
"""
|
||||||
|
from . import TestBase
|
||||||
|
from kontor.models import User
|
||||||
|
|
||||||
|
|
||||||
|
class TestKontorModel(TestBase):
|
||||||
|
"""This TestCase contains tests for users."""
|
||||||
|
def test_user_model(self):
|
||||||
|
"""
|
||||||
|
Test number of records in User collection
|
||||||
|
"""
|
||||||
|
self.assertEqual(User.objects.all().count(), 2)
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
"""This module contains tests for Comic related urls."""
|
||||||
|
import unittest
|
||||||
|
from flask import url_for
|
||||||
|
from . import TestBase
|
||||||
|
|
||||||
|
|
||||||
|
class TestKontorViews(TestBase):
|
||||||
|
|
||||||
|
def test_homepage_view(self):
|
||||||
|
"""
|
||||||
|
Test that homepage is accessible without login
|
||||||
|
"""
|
||||||
|
response = self.client.get(url_for('home.homepage'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_login_view(self):
|
||||||
|
"""
|
||||||
|
Test that login page is accessible without login
|
||||||
|
"""
|
||||||
|
response = self.client.get(url_for('auth.login'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_logout_view(self):
|
||||||
|
"""
|
||||||
|
Test that logout link is inaccessible without login
|
||||||
|
and redirects to login page then to logout
|
||||||
|
"""
|
||||||
|
target_url = url_for('auth.logout')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_dashboard_view(self):
|
||||||
|
"""
|
||||||
|
Test that dashboard is inaccessible without login
|
||||||
|
and redirects to login page then to dashboard
|
||||||
|
"""
|
||||||
|
target_url = url_for('home.dashboard')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_admin_dashboard_view(self):
|
||||||
|
"""
|
||||||
|
Test that dashboard is inaccessible without login
|
||||||
|
and redirects to login page then to dashboard
|
||||||
|
"""
|
||||||
|
target_url = url_for('home.admin_dashboard')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
"""
|
||||||
|
This module cantains tests for the comic data model
|
||||||
|
"""
|
||||||
|
import unittest
|
||||||
|
from . import TestBase
|
||||||
|
from kontor.library.models import Author, Publisher, Book
|
||||||
|
|
||||||
|
|
||||||
|
class TestComicsModel(TestBase):
|
||||||
|
"""This TestCase contains tests for comic data model."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
publisher = Publisher()
|
||||||
|
publisher.name = "Publisher1"
|
||||||
|
publisher.save()
|
||||||
|
author = Author()
|
||||||
|
author.name = "Autor1"
|
||||||
|
author.save()
|
||||||
|
book = Book()
|
||||||
|
book.title = "Title1"
|
||||||
|
book.save()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
for book in Book.objects.all():
|
||||||
|
book.delete()
|
||||||
|
for author in Author.objects.all():
|
||||||
|
author.delete()
|
||||||
|
for publisher in Publisher.objects.all():
|
||||||
|
publisher.delete()
|
||||||
|
|
||||||
|
def test_comic_model(self):
|
||||||
|
book_list = Book.objects.all()
|
||||||
|
self.assertEqual(book_list.count(), 1)
|
||||||
|
|
||||||
|
def test_publisher_model(self):
|
||||||
|
self.assertEqual(Publisher.objects.all().count(), 1)
|
||||||
|
|
||||||
|
def test_artist_model(self):
|
||||||
|
self.assertEqual(Author.objects.all().count(), 1)
|
||||||
|
|
||||||
|
def test_assign_publisher(self):
|
||||||
|
book = Book.objects.first()
|
||||||
|
publisher = Publisher.objects.first()
|
||||||
|
book.publisher = publisher
|
||||||
|
book.save()
|
||||||
|
self.assertEqual(publisher.name, book.publisher.name)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
"""This module contains tests for Comic related urls."""
|
||||||
|
import unittest
|
||||||
|
from flask import url_for
|
||||||
|
from . import TestBase
|
||||||
|
|
||||||
|
|
||||||
|
class TestLibraryViews(TestBase):
|
||||||
|
|
||||||
|
def test_library_index(self):
|
||||||
|
"""
|
||||||
|
Test that comics page is inaccessible without login
|
||||||
|
and redirects to login page then to comics page
|
||||||
|
"""
|
||||||
|
target_url = url_for('library.list_books')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_library_author(self):
|
||||||
|
"""
|
||||||
|
Test that comics page is inaccessible without login
|
||||||
|
and redirects to login page then to comics page
|
||||||
|
"""
|
||||||
|
target_url = url_for('library.list_authors')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_library_publisher(self):
|
||||||
|
"""
|
||||||
|
Test that comics page is inaccessible without login
|
||||||
|
and redirects to login page then to comics page
|
||||||
|
"""
|
||||||
|
target_url = url_for('library.list_publishers')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
"""
|
||||||
|
This module cantains tests for the TYSC data model
|
||||||
|
"""
|
||||||
|
import unittest
|
||||||
|
from . import TestBase
|
||||||
|
from kontor.tysc import initialize_model
|
||||||
|
from kontor.tysc.models import Sport, Position, Team, Player
|
||||||
|
from kontor.tysc.models import Manufacturer, CardSet, ParallelSet, InsertSet, Card
|
||||||
|
|
||||||
|
|
||||||
|
class TestTYSCModel(TestBase):
|
||||||
|
"""
|
||||||
|
This TestCase contains tests for TYSC data model.
|
||||||
|
"""
|
||||||
|
_initialize_db_ = True
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
if self._initialize_db_:
|
||||||
|
initialize_model()
|
||||||
|
self._initialize_db_ = False
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_sport_model(self):
|
||||||
|
self.assertEqual(Sport.objects.all().count(), 4)
|
||||||
|
|
||||||
|
def test_position_model(self):
|
||||||
|
self.assertEqual(Position.objects.all().count(), 25)
|
||||||
|
|
||||||
|
def test_team_model(self):
|
||||||
|
self.assertEqual(Team.objects.all().count(), 122)
|
||||||
|
|
||||||
|
def test_manufacturer_model(self):
|
||||||
|
self.assertEqual(Manufacturer.objects.all().count(), 8)
|
||||||
|
|
||||||
|
def test_cardset_model(self):
|
||||||
|
self.assertEqual(CardSet.objects.all().count(), 11)
|
||||||
|
|
||||||
|
def test_parallelset_model(self):
|
||||||
|
self.assertEqual(ParallelSet.objects.all().count(), 3)
|
||||||
|
|
||||||
|
def test_insertset_model(self):
|
||||||
|
self.assertEqual(InsertSet.objects.all().count(), 1)
|
||||||
|
|
||||||
|
def test_playerset_model(self):
|
||||||
|
self.assertEqual(Player.objects.all().count(), 36)
|
||||||
|
|
||||||
|
def test_card_model(self):
|
||||||
|
self.assertEqual(Card.objects.all().count(), 36)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
"""This module contains tests for TradeYourSportsCards related urls."""
|
||||||
|
import unittest
|
||||||
|
from flask import url_for
|
||||||
|
from . import TestBase
|
||||||
|
|
||||||
|
|
||||||
|
class TestTYSCViews(TestBase):
|
||||||
|
|
||||||
|
def test_tysc_index(self):
|
||||||
|
"""
|
||||||
|
Test that TYSC page is inaccessible without login
|
||||||
|
and redirects to login page then to TYSC page
|
||||||
|
"""
|
||||||
|
target_url = url_for('tysc.list_cards')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_tysc_sport(self):
|
||||||
|
"""
|
||||||
|
Test that TYSC page is inaccessible without login
|
||||||
|
and redirects to login page then to TYSC page
|
||||||
|
"""
|
||||||
|
target_url = url_for('tysc.list_sports')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_tysc_team(self):
|
||||||
|
"""
|
||||||
|
Test that TYSC page is inaccessible without login
|
||||||
|
and redirects to login page then to TYSC page
|
||||||
|
"""
|
||||||
|
target_url = url_for('tysc.list_teams')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_tysc_player(self):
|
||||||
|
"""
|
||||||
|
Test that TYSC page is inaccessible without login
|
||||||
|
and redirects to login page then to TYSC page
|
||||||
|
"""
|
||||||
|
target_url = url_for('tysc.list_players')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_tysc_manufacturer(self):
|
||||||
|
"""
|
||||||
|
Test that TYSC page is inaccessible without login
|
||||||
|
and redirects to login page then to TYSC page
|
||||||
|
"""
|
||||||
|
target_url = url_for('tysc.list_manufacturers')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_tysc_card_set(self):
|
||||||
|
"""
|
||||||
|
Test that TYSC page is inaccessible without login
|
||||||
|
and redirects to login page then to TYSC page
|
||||||
|
"""
|
||||||
|
target_url = url_for('tysc.list_card_sets')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_tysc_parallel_set(self):
|
||||||
|
"""
|
||||||
|
Test that TYSC page is inaccessible without login
|
||||||
|
and redirects to login page then to TYSC page
|
||||||
|
"""
|
||||||
|
target_url = url_for('tysc.list_parallel_sets')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_tysc_insert_set(self):
|
||||||
|
"""
|
||||||
|
Test that TYSC page is inaccessible without login
|
||||||
|
and redirects to login page then to TYSC page
|
||||||
|
"""
|
||||||
|
target_url = url_for('tysc.list_insert_sets')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
def test_tysc_card(self):
|
||||||
|
"""
|
||||||
|
Test that TYSC page is inaccessible without login
|
||||||
|
and redirects to login page then to TYSC page
|
||||||
|
"""
|
||||||
|
target_url = url_for('tysc.list_cards')
|
||||||
|
redirect_url = url_for('auth.login', next=target_url)
|
||||||
|
response = self.client.get(target_url)
|
||||||
|
self.assertEqual(response.status_code, 302)
|
||||||
|
self.assertRedirects(response, redirect_url)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user