import from kontor-flask branch develop/0.1.0
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
__pycache__/
|
||||||
|
.idea
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Kontor Flask
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from kontor import create_app
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app = create_app()
|
||||||
|
app.run(host="0.0.0.0", port=8000, debug=True)
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
# 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
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# 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)
|
|
||||||
@@ -1,200 +0,0 @@
|
|||||||
#!/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}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
.. 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`
|
|
||||||
+51
-64
@@ -1,74 +1,61 @@
|
|||||||
"""
|
from flask import Flask, render_template
|
||||||
Module kontor implements Kontor application.
|
from flask_jwt_extended import JWTManager
|
||||||
"""
|
|
||||||
|
|
||||||
from flask import Flask
|
from kontor import config
|
||||||
from flask_bootstrap import Bootstrap
|
from kontor.extensions import db, ma
|
||||||
from flask_login import LoginManager
|
from logging.config import dictConfig
|
||||||
from pymodm import connect
|
|
||||||
|
|
||||||
# local imports
|
dictConfig({
|
||||||
from config import app_config
|
'version': 1,
|
||||||
from .version import __version__
|
'formatters': {'default': {
|
||||||
|
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
|
||||||
|
}},
|
||||||
|
'handlers': {'wsgi': {
|
||||||
|
'class': 'logging.StreamHandler',
|
||||||
|
'stream': 'ext://flask.logging.wsgi_errors_stream',
|
||||||
|
'formatter': 'default'
|
||||||
|
}},
|
||||||
|
'root': {
|
||||||
|
'level': 'INFO',
|
||||||
|
'handlers': ['wsgi']
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
_LOGIN_MANAGER_ = LoginManager()
|
def create_app(config_class=config.Config):
|
||||||
|
app.config.from_object(config_class)
|
||||||
|
|
||||||
|
db.init_app(app)
|
||||||
|
ma.init_app(app)
|
||||||
|
# Initialize Flask extensions here
|
||||||
|
app.config["JWT_SECRET_KEY"] = "super-secret" # Change this!
|
||||||
|
jwt = JWTManager(app)
|
||||||
|
|
||||||
def get_host(config_name):
|
with app.app_context():
|
||||||
"""
|
# db.create_all()
|
||||||
Returns host address from configuration.
|
db.reflect()
|
||||||
:param config_name:
|
|
||||||
:return: host address
|
|
||||||
"""
|
|
||||||
host = app_config[config_name].host
|
|
||||||
return host
|
|
||||||
|
|
||||||
|
# Register blueprints here
|
||||||
|
from kontor.main import bp as main_bp
|
||||||
|
app.register_blueprint(main_bp)
|
||||||
|
from kontor.comics import comics_bp
|
||||||
|
app.register_blueprint(comics_bp, url_prefix='/comics')
|
||||||
|
from kontor.api import api_bp
|
||||||
|
app.register_blueprint(api_bp, url_prefix='/api/v1')
|
||||||
|
from kontor.comics import comics_api
|
||||||
|
app.register_blueprint(comics_api, url_prefix='/api/v1/comics')
|
||||||
|
|
||||||
def get_port(config_name):
|
from kontor.media import media_bp
|
||||||
"""
|
app.register_blueprint(media_bp, url_prefix='/media')
|
||||||
Returns port number from configuration.
|
from kontor.media import media_api
|
||||||
:param config_name:
|
app.register_blueprint(media_api, url_prefix='/api/v1/media')
|
||||||
:return: port number
|
# from kontor.auth.auth import auth_bp
|
||||||
"""
|
# from kontor.cart.cart import cart_bp
|
||||||
port = app_config[config_name].port
|
# from kontor.general.general import general_bp
|
||||||
return port
|
# app.register_blueprint(auth_bp)
|
||||||
|
# app.register_blueprint(cart_bp, url_prefix='/cart')
|
||||||
|
# app.register_blueprint(general_bp)
|
||||||
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
|
return app
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
"""
|
|
||||||
Module admin implements administration functions.
|
|
||||||
"""
|
|
||||||
from . import views
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
"""
|
|
||||||
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')
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
"""
|
|
||||||
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,5 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
api_bp = Blueprint('api_bp', __name__)
|
||||||
|
|
||||||
|
from kontor.api import routes
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
from flask import jsonify, request
|
||||||
|
from flask_jwt_extended import jwt_required, get_jwt_identity, create_access_token
|
||||||
|
|
||||||
|
from kontor.api import api_bp
|
||||||
|
|
||||||
|
|
||||||
|
@api_bp.route('/')
|
||||||
|
def index():
|
||||||
|
modules = ['comics']
|
||||||
|
return jsonify(modules)
|
||||||
|
|
||||||
|
|
||||||
|
# Create a route to authenticate your users and return JWTs. The
|
||||||
|
# create_access_token() function is used to actually generate the JWT.
|
||||||
|
@api_bp.route("/login", methods=["POST"])
|
||||||
|
def login():
|
||||||
|
username = request.json.get("username", None)
|
||||||
|
password = request.json.get("password", None)
|
||||||
|
if username != "test" or password != "test":
|
||||||
|
return jsonify({"msg": "Bad username or password"}), 401
|
||||||
|
|
||||||
|
access_token = create_access_token(identity=username)
|
||||||
|
return jsonify(access_token=access_token)
|
||||||
|
|
||||||
|
|
||||||
|
@api_bp.route('/protected', methods=['GET'])
|
||||||
|
@jwt_required()
|
||||||
|
def protected():
|
||||||
|
current_user = get_jwt_identity()
|
||||||
|
return {'message': f'Hello, {current_user}!'}
|
||||||
@@ -1,4 +1,22 @@
|
|||||||
"""
|
import bcrypt
|
||||||
Module auth implements authentication functions.
|
from flask import session
|
||||||
"""
|
from flask_httpauth import HTTPBasicAuth
|
||||||
from . import views
|
|
||||||
|
from kontor import app
|
||||||
|
from kontor.auth.models import User
|
||||||
|
|
||||||
|
auth = HTTPBasicAuth()
|
||||||
|
|
||||||
|
|
||||||
|
@auth.verify_password
|
||||||
|
def verify_password(username, password):
|
||||||
|
if username is None:
|
||||||
|
return False
|
||||||
|
# Add your authentication logic here
|
||||||
|
app.logger.info("login user %s", username)
|
||||||
|
user = User.query.filter_by(user_name=username).first()
|
||||||
|
app.logger.info("User: %s", user)
|
||||||
|
app.logger.info("Stored Password: '%s' Hashed Password: '%s'", user.password, bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()))
|
||||||
|
if 'user_name' in session and session['user_name'] == username:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
"""
|
|
||||||
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,45 @@
|
|||||||
|
from kontor.extensions import db, ma
|
||||||
|
from sqlalchemy.sql import func
|
||||||
|
|
||||||
|
|
||||||
|
class User(db.Model):
|
||||||
|
# __table__ = db.metadata.tables["publisher"]
|
||||||
|
__tablename__ = "user"
|
||||||
|
__table_args__ = {'extend_existing': True}
|
||||||
|
id = db.Column(db.String, primary_key=True)
|
||||||
|
created_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
|
last_modified_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
|
version = db.Column(db.Integer)
|
||||||
|
enabled = db.Column(db.SmallInteger)
|
||||||
|
email = db.Column(db.String)
|
||||||
|
first_name = db.Column(db.String)
|
||||||
|
last_name = db.Column(db.String)
|
||||||
|
user_name = db.Column(db.String)
|
||||||
|
password = db.Column(db.String)
|
||||||
|
token = db.Column(db.String)
|
||||||
|
token_expired = db.Column(db.SmallInteger)
|
||||||
|
|
||||||
|
def is_token_valid(self):
|
||||||
|
return self.review == 'b\x01'
|
||||||
|
|
||||||
|
def is_user_enabled(self):
|
||||||
|
return self.should_download == 'b\x01'
|
||||||
|
|
||||||
|
class Role(db.Model):
|
||||||
|
__tablename__ = "role"
|
||||||
|
__table_args__ = {'extend_existing': True}
|
||||||
|
id = db.Column(db.String, primary_key=True)
|
||||||
|
created_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
|
last_modified_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
|
version = db.Column(db.Integer)
|
||||||
|
name = db.Column(db.String)
|
||||||
|
|
||||||
|
class AuthorizationMatrix(db.Model):
|
||||||
|
__tablename__ = "authorization_matrix"
|
||||||
|
__table_args__ = {'extend_existing': True}
|
||||||
|
id = db.Column(db.String, primary_key=True)
|
||||||
|
created_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
|
last_modified_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
|
version = db.Column(db.Integer)
|
||||||
|
role_id = db.Column(db.String, db.ForeignKey("role.id"))
|
||||||
|
user_id = db.Column(db.String, db.ForeignKey("user.id"))
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
"""
|
|
||||||
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'))
|
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
"""
|
from flask import Blueprint
|
||||||
Define routing rules for comic related information
|
|
||||||
"""
|
comics_bp = Blueprint('comics_bp', __name__,
|
||||||
|
template_folder='templates',
|
||||||
|
static_folder='static', static_url_path='assets')
|
||||||
|
comics_api = Blueprint('comics_api', __name__)
|
||||||
|
|
||||||
|
from kontor.comics import routes
|
||||||
|
from kontor.comics import api
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
from flask import Blueprint, render_template, jsonify
|
||||||
|
|
||||||
|
from kontor.comics import comics_api
|
||||||
|
from kontor.models import Comic, comics_schema, Publisher, comic_schema, publisher_schema, publishers_schema
|
||||||
|
|
||||||
|
|
||||||
|
@comics_api.route('/')
|
||||||
|
def index():
|
||||||
|
comics = Comic.query.all()
|
||||||
|
return comics_schema.dump(comics)
|
||||||
|
|
||||||
|
|
||||||
|
@comics_api.route('/comic/<string:comic_id>')
|
||||||
|
def view(comic_id):
|
||||||
|
comic = Comic.query.get(comic_id)
|
||||||
|
return comic_schema.dump(comic)
|
||||||
|
|
||||||
|
|
||||||
|
@comics_api.route('/publisher/')
|
||||||
|
def publisher_all():
|
||||||
|
publishers = Publisher.query.all()
|
||||||
|
return publishers_schema.dump(publishers)
|
||||||
|
|
||||||
|
|
||||||
|
@comics_api.route('/publisher/<string:id>')
|
||||||
|
def publisher_detail(publisher_id):
|
||||||
|
publisher = Publisher.query.get(publisher_id)
|
||||||
|
return publisher_schema.dump(publisher)
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
"""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')
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
"""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,16 @@
|
|||||||
|
from flask import Blueprint, render_template
|
||||||
|
|
||||||
|
from kontor.comics import comics_bp
|
||||||
|
from kontor.models import Comic
|
||||||
|
|
||||||
|
|
||||||
|
@comics_bp.route('/')
|
||||||
|
def index():
|
||||||
|
comics = Comic.query.all()
|
||||||
|
return render_template('comics/list.html', comics=comics)
|
||||||
|
|
||||||
|
|
||||||
|
@comics_bp.route('/comic/<string:comic_id>')
|
||||||
|
def view(comic_id):
|
||||||
|
comic = Comic.query.get(comic_id)
|
||||||
|
return render_template('comics/view.html', comic=comic)
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<span class="title">
|
||||||
|
<h1>{% block title %} Comics {% endblock %}</h1>
|
||||||
|
</span>
|
||||||
|
<div class="comics">
|
||||||
|
<h2>Comics Blueprint</h2>
|
||||||
|
{% for comic in comics %}
|
||||||
|
<div class="comic">
|
||||||
|
<b>
|
||||||
|
<p class="name">
|
||||||
|
<a href="{{ url_for('comics_bp.view', comic_id=comic.id)}}">{{ comic.title }}</a>
|
||||||
|
</p>
|
||||||
|
</b>
|
||||||
|
<b><p class="identifier">{{ comic.publisher.name }}</p></b>
|
||||||
|
<div class="date">
|
||||||
|
<h4>Created</h4>
|
||||||
|
<p>{{ comic.created_date }}</p>
|
||||||
|
<h4>Modified</h4>
|
||||||
|
<p>{{ comic.last_modified_date }}</p>
|
||||||
|
</div>
|
||||||
|
<p>Version: {{ comic.version }}</p>
|
||||||
|
<p>Review: {{ comic.review }}</p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<span class="title">
|
||||||
|
<h1>{% block title %} Comics {% endblock %}</h1>
|
||||||
|
</span>
|
||||||
|
<div class="comics">
|
||||||
|
<h2>Comics Blueprint</h2>
|
||||||
|
<div class="comic">
|
||||||
|
<b>
|
||||||
|
<p class="name">
|
||||||
|
<a href="{{ url_for('comics_bp.view', comic_id=comic.id)}}">{{ comic.title }}</a>
|
||||||
|
</p>
|
||||||
|
</b>
|
||||||
|
<b>
|
||||||
|
<p class="identifier">{{ comic.id }}</p>
|
||||||
|
</b>
|
||||||
|
<div class="date">
|
||||||
|
<h4>Created</h4>
|
||||||
|
<p>{{ comic.created_date }}</p>
|
||||||
|
<h4>Modified</h4>
|
||||||
|
<p>{{ comic.last_modified_date }}</p>
|
||||||
|
</div>
|
||||||
|
<p>Version: {{ comic.version }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
"""
|
|
||||||
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,10 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
SECRET_KEY = os.environ.get('SECRET_KEY')
|
||||||
|
# SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URI') or 'sqlite:///' + os.path.join(basedir, 'kontor.db')
|
||||||
|
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URI') or 'mariadb+mariadbconnector://kontor:kontor@localhost/kontor'
|
||||||
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import flask_sqlalchemy
|
||||||
|
from flask_marshmallow import Marshmallow
|
||||||
|
|
||||||
|
db = flask_sqlalchemy.SQLAlchemy()
|
||||||
|
ma = Marshmallow()
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
"""
|
|
||||||
Module for the Kontor homepage.
|
|
||||||
"""
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
"""
|
|
||||||
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")
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
"""
|
|
||||||
Define routing rules for library related information
|
|
||||||
"""
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
"""
|
|
||||||
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')
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
"""
|
|
||||||
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)
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
"""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,5 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
bp = Blueprint('main', __name__)
|
||||||
|
|
||||||
|
from kontor.main import routes
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
from flask import render_template, session
|
||||||
|
|
||||||
|
from kontor import app
|
||||||
|
from kontor.main import bp
|
||||||
|
from kontor.auth import auth
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/')
|
||||||
|
@auth.login_required
|
||||||
|
def index():
|
||||||
|
return render_template('index.html')
|
||||||
|
|
||||||
|
@bp.route('/logout')
|
||||||
|
def logout():
|
||||||
|
app.logger.info("logout")
|
||||||
|
auth.current_user()
|
||||||
|
session['user_name'] = None
|
||||||
|
return render_template('index.html')
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
media_bp = Blueprint('media_bp', __name__,
|
||||||
|
template_folder='templates',
|
||||||
|
static_folder='static', static_url_path='assets')
|
||||||
|
media_api = Blueprint('media_api', __name__)
|
||||||
|
|
||||||
|
from kontor.media import routes
|
||||||
|
from kontor.media import api
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
from flask import Blueprint, render_template, jsonify
|
||||||
|
|
||||||
|
from kontor import app
|
||||||
|
from kontor.media import media_api
|
||||||
|
from kontor.media.models import MediaFile, mediafile_schema, mediafiles_schema
|
||||||
|
|
||||||
|
|
||||||
|
@media_api.route('/')
|
||||||
|
def mediafile_list():
|
||||||
|
app.logger.info("get all media files")
|
||||||
|
files = MediaFile.query.all()
|
||||||
|
return mediafiles_schema.dump(files)
|
||||||
|
|
||||||
|
|
||||||
|
@media_api.route('/mediafile/<string:mediafile_id>')
|
||||||
|
def mediafile_detail(file_id):
|
||||||
|
file = MediaFile.query.get(file_id)
|
||||||
|
return mediafile_schema.dump(file)
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
from kontor.extensions import db, ma
|
||||||
|
from sqlalchemy.sql import func
|
||||||
|
|
||||||
|
|
||||||
|
class MediaFile(db.Model):
|
||||||
|
# __table__ = db.metadata.tables["publisher"]
|
||||||
|
__tablename__ = "media_file"
|
||||||
|
__table_args__ = {'extend_existing': True}
|
||||||
|
id = db.Column(db.String, primary_key=True)
|
||||||
|
created_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
|
last_modified_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
|
version = db.Column(db.Integer)
|
||||||
|
title = db.Column(db.String)
|
||||||
|
file_name = db.Column(db.String)
|
||||||
|
url = db.Column(db.String)
|
||||||
|
path = db.Column(db.String)
|
||||||
|
cloud_link = db.Column(db.String)
|
||||||
|
review = db.Column(db.SmallInteger)
|
||||||
|
should_download = db.Column(db.Boolean)
|
||||||
|
|
||||||
|
def is_review(self):
|
||||||
|
return self.review == 'b\x00'
|
||||||
|
|
||||||
|
def is_download(self):
|
||||||
|
return self.should_download == 'b\x00'
|
||||||
|
|
||||||
|
|
||||||
|
class MediaFileSchema(ma.SQLAlchemySchema):
|
||||||
|
class Meta:
|
||||||
|
model = MediaFile
|
||||||
|
fields = ("id", "created_date", "last_modified_date", "version", "title", "file_name", "url", "path", "cloud_link", "review", "should_download", "_links")
|
||||||
|
|
||||||
|
# Smart hyperlinking
|
||||||
|
_links = ma.Hyperlinks(
|
||||||
|
{
|
||||||
|
"self": ma.URLFor("media_api.mediafile_detail", values=dict(mediafile_id="<id>")),
|
||||||
|
"collection": ma.URLFor("media_api.mediafile_list"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
mediafile_schema = MediaFileSchema()
|
||||||
|
mediafiles_schema = MediaFileSchema(many=True)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
from flask import Blueprint, render_template
|
||||||
|
|
||||||
|
from kontor.media import media_bp
|
||||||
|
from kontor.media.models import MediaFile
|
||||||
|
|
||||||
|
|
||||||
|
@media_bp.route('/')
|
||||||
|
def mediafile_list():
|
||||||
|
files = MediaFile.query.all()
|
||||||
|
return render_template('media/mediafile_list.html', mediafiles=files)
|
||||||
|
|
||||||
|
|
||||||
|
@media_bp.route('/mediafile/<string:mediafile_id>')
|
||||||
|
def mediafile_detail(mediafile_id):
|
||||||
|
mediafile = MediaFile.query.get(mediafile_id)
|
||||||
|
return render_template('media/mediafile_detail.html', mediafile=mediafile)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<span class="title">
|
||||||
|
<h1>{% block title %} MediaFile {% endblock %}</h1>
|
||||||
|
</span>
|
||||||
|
<div class="comics">
|
||||||
|
<h2>MediaFile Blueprint</h2>
|
||||||
|
<div class="comic">
|
||||||
|
<b>
|
||||||
|
<p class="name">
|
||||||
|
<a href="{{ url_for('media_bp.mediafile_detail', mediafile_id=mediafile.id)}}">{{ mediafile.title }}</a>
|
||||||
|
</p>
|
||||||
|
</b>
|
||||||
|
<b>
|
||||||
|
<p class="identifier">{{ mediafile.id }}</p>
|
||||||
|
</b>
|
||||||
|
<div class="date">
|
||||||
|
<h4>Created</h4>
|
||||||
|
<p>{{ mediafile.created_date }}</p>
|
||||||
|
<h4>Modified</h4>
|
||||||
|
<p>{{ mediafile.last_modified_date }}</p>
|
||||||
|
</div>
|
||||||
|
<p>Version: {{ mediafile.version }}</p>
|
||||||
|
<p>Review: {{ mediafile.is_review() }}</p>
|
||||||
|
<p>Should Download: {{mediafile.is_download() }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<span class="title">
|
||||||
|
<h1>{% block title %} MediaFile {% endblock %}</h1>
|
||||||
|
</span>
|
||||||
|
<div class="comic">
|
||||||
|
<h2>MediaFile Blueprint</h2>
|
||||||
|
{% for mediafile in mediafiles %}
|
||||||
|
<div class="comic">
|
||||||
|
<b>
|
||||||
|
<p class="name">
|
||||||
|
<a href="{{ url_for('media_bp.mediafile_detail', mediafile_id=mediafile.id)}}">{{ mediafile.title }}</a>
|
||||||
|
</p>
|
||||||
|
</b>
|
||||||
|
<b><p class="identifier">{{ mediafile.id }}</p></b>
|
||||||
|
<div class="date">
|
||||||
|
<h4>Created</h4>
|
||||||
|
<p>{{ mediafile.created_date }}</p>
|
||||||
|
<h4>Modified</h4>
|
||||||
|
<p>{{ mediafile.last_modified_date }}</p>
|
||||||
|
</div>
|
||||||
|
<p>Version: {{ mediafile.version }}</p>
|
||||||
|
<p>Review: {{ mediafile.is_review() }}</p>
|
||||||
|
<p>Should Download: {{ mediafile.is_download() }}</p>
|
||||||
|
<p>Links: {{ mediafile._links }}</p>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
+42
-59
@@ -1,66 +1,49 @@
|
|||||||
"""
|
from kontor.extensions import db, ma
|
||||||
This modules declares the model for Kontor users.
|
from sqlalchemy.sql import func
|
||||||
"""
|
|
||||||
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):
|
class Publisher(db.Model):
|
||||||
"""
|
# __table__ = db.metadata.tables["publisher"]
|
||||||
This class represents an user for the Kontor application.
|
__tablename__ = "publisher"
|
||||||
"""
|
__table_args__ = {'extend_existing': True}
|
||||||
email = fields.EmailField()
|
id = db.Column(db.String, primary_key=True)
|
||||||
username = fields.CharField(max_length=60)
|
created_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
first_name = fields.CharField(max_length=60)
|
last_modified_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
last_name = fields.CharField(max_length=60)
|
version = db.Column(db.Integer)
|
||||||
password_hash = fields.CharField(max_length=128)
|
name = db.Column(db.String)
|
||||||
is_admin = fields.BooleanField(default=False)
|
comics = db.relationship('Comic', back_populates='publisher', lazy='dynamic')
|
||||||
|
|
||||||
@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 PublisherSchema(ma.SQLAlchemySchema):
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Sets the connection and connections details."""
|
model = Publisher
|
||||||
connection_alias = 'kontor'
|
|
||||||
write_concern = WriteConcern(j=True)
|
comics = ma.List(ma.HyperlinkRelated("comics_api.index"))
|
||||||
|
|
||||||
|
|
||||||
# Set up user_loader
|
publisher_schema = PublisherSchema()
|
||||||
@_LOGIN_MANAGER_.user_loader
|
publishers_schema = PublisherSchema(many=True)
|
||||||
def load_user(user_name):
|
|
||||||
"""
|
|
||||||
Get list of users from database
|
class Comic(db.Model):
|
||||||
:param user_name:
|
# __table__ = db.metadata.tables["comic"]
|
||||||
:return:
|
__tablename__ = "comic"
|
||||||
"""
|
__table_args__ = {'extend_existing': True}
|
||||||
return User.objects.get({'username': user_name})
|
id = db.Column(db.String, primary_key=True)
|
||||||
|
created_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
|
last_modified_date = db.Column(db.DateTime(timezone=True), server_default=func.now())
|
||||||
|
version = db.Column(db.Integer)
|
||||||
|
title = db.Column(db.String)
|
||||||
|
publisher_id = db.Column(db.String, db.ForeignKey("publisher.id"))
|
||||||
|
publisher = db.relationship("Publisher", back_populates="comics")
|
||||||
|
|
||||||
|
|
||||||
|
class ComicSchema(ma.SQLAlchemyAutoSchema):
|
||||||
|
class Meta:
|
||||||
|
model = Comic
|
||||||
|
include_fk = True
|
||||||
|
publisher = ma.HyperlinkRelated("comics_api.publisher_detail")
|
||||||
|
|
||||||
|
|
||||||
|
comic_schema = ComicSchema()
|
||||||
|
comics_schema = ComicSchema(many=True)
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
"""
|
|
||||||
Define routing rules for office related information
|
|
||||||
"""
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
/* 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;
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
<!-- 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 %}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
<!-- 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 %}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
<!-- 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 %}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
<!-- 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 %}
|
|
||||||
@@ -1,107 +1,68 @@
|
|||||||
<!-- app/templates/base.html -->
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>{{ title }} | Kontor</title>
|
<meta charset="UTF-8">
|
||||||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
|
<title>{% block title %} {% endblock %} - FlaskApp</title>
|
||||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
|
<style>
|
||||||
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
|
h2 {
|
||||||
<link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
|
width: 100%;
|
||||||
<link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.ico') }}">
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
margin: 5px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
margin: 5px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.comic {
|
||||||
|
flex: 20%;
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
inline-size: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identifier a {
|
||||||
|
color: #00a36f;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
padding: 10px;
|
||||||
|
margin: 5px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #004835;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav a {
|
||||||
|
color: #d64161;
|
||||||
|
font-size: 3em;
|
||||||
|
margin-left: 50px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title a {
|
||||||
|
color: #00a36f;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar navbar-default navbar-fixed-top topnav" role="navigation">
|
<nav>
|
||||||
<div class="container topnav">
|
<a href="{{ url_for('main.index') }}">Kontor</a>
|
||||||
<div class="navbar-header">
|
<a href="{{ url_for('comics_bp.index') }}">Comics</a>
|
||||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
|
<a href="{{ url_for('media_bp.mediafile_list') }}">Media</a>
|
||||||
<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>
|
</nav>
|
||||||
<div class="wrapper">
|
<hr>
|
||||||
{% block body %}
|
<div class="content">
|
||||||
{% endblock %}
|
{% block content %} {% endblock %}
|
||||||
<div class="push"></div>
|
|
||||||
</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>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<!-- 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 %}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<!-- 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 %}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<!-- 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,8 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<span class="title"><h1>{% block title %} The Home Page of Kontor {% endblock %}</h1></span>
|
||||||
|
<div class="content">
|
||||||
|
<h2>This is the main Flask blueprint</h2>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
{% 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 %}
|
|
||||||
@@ -1,377 +0,0 @@
|
|||||||
"""
|
|
||||||
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()
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
"""
|
|
||||||
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()])
|
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
"""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)
|
|
||||||
@@ -1,405 +0,0 @@
|
|||||||
"""
|
|
||||||
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")
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
"""This module declares the version of the Kontor application."""
|
|
||||||
__version__ = '0.0.7'
|
|
||||||
@@ -1,382 +0,0 @@
|
|||||||
[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
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
flask
|
|
||||||
flask-login
|
|
||||||
flask-bootstrap
|
|
||||||
Flask-WTF
|
|
||||||
flask-testing
|
|
||||||
pymodm
|
|
||||||
nose
|
|
||||||
nose-htmloutput
|
|
||||||
coverage
|
|
||||||
pylint
|
|
||||||
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
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()
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
"""
|
|
||||||
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()
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
"""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()
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# instance/config.py
|
|
||||||
|
|
||||||
SECRET_KEY = 'p9Bv<3Eid9%$i01'
|
|
||||||
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
"""
|
|
||||||
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)
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
"""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()
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
"""
|
|
||||||
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()
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
"""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()
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
"""
|
|
||||||
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()
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
"""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