Merge branch 'feature/4-import-kontor-flask-into-directory-flask' into 'develop/0.1.0'
Import kontor-flask into directory flask Closes #4 See merge request tpeetz/kontor!2
This commit was merged in pull request #46.
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)
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
from flask import Flask, render_template
|
||||||
|
from flask_jwt_extended import JWTManager
|
||||||
|
|
||||||
|
from kontor import config
|
||||||
|
from kontor.extensions import db, ma
|
||||||
|
from logging.config import dictConfig
|
||||||
|
|
||||||
|
dictConfig({
|
||||||
|
'version': 1,
|
||||||
|
'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__)
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
with app.app_context():
|
||||||
|
# db.create_all()
|
||||||
|
db.reflect()
|
||||||
|
|
||||||
|
# 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')
|
||||||
|
|
||||||
|
from kontor.media import media_bp
|
||||||
|
app.register_blueprint(media_bp, url_prefix='/media')
|
||||||
|
from kontor.media import media_api
|
||||||
|
app.register_blueprint(media_api, url_prefix='/api/v1/media')
|
||||||
|
# from kontor.auth.auth import auth_bp
|
||||||
|
# from kontor.cart.cart import cart_bp
|
||||||
|
# from kontor.general.general import general_bp
|
||||||
|
# app.register_blueprint(auth_bp)
|
||||||
|
# app.register_blueprint(cart_bp, url_prefix='/cart')
|
||||||
|
# app.register_blueprint(general_bp)
|
||||||
|
|
||||||
|
return app
|
||||||
@@ -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}!'}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import bcrypt
|
||||||
|
from flask import session
|
||||||
|
from flask_httpauth import HTTPBasicAuth
|
||||||
|
|
||||||
|
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
|
||||||
@@ -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"))
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
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)
|
||||||
@@ -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 %}
|
||||||
@@ -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()
|
||||||
@@ -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 %}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
from kontor.extensions import db, ma
|
||||||
|
from sqlalchemy.sql import func
|
||||||
|
|
||||||
|
|
||||||
|
class Publisher(db.Model):
|
||||||
|
# __table__ = db.metadata.tables["publisher"]
|
||||||
|
__tablename__ = "publisher"
|
||||||
|
__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)
|
||||||
|
comics = db.relationship('Comic', back_populates='publisher', lazy='dynamic')
|
||||||
|
|
||||||
|
|
||||||
|
class PublisherSchema(ma.SQLAlchemySchema):
|
||||||
|
class Meta:
|
||||||
|
model = Publisher
|
||||||
|
|
||||||
|
comics = ma.List(ma.HyperlinkRelated("comics_api.index"))
|
||||||
|
|
||||||
|
|
||||||
|
publisher_schema = PublisherSchema()
|
||||||
|
publishers_schema = PublisherSchema(many=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Comic(db.Model):
|
||||||
|
# __table__ = db.metadata.tables["comic"]
|
||||||
|
__tablename__ = "comic"
|
||||||
|
__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)
|
||||||
|
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)
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{% block title %} {% endblock %} - FlaskApp</title>
|
||||||
|
<style>
|
||||||
|
h2 {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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>
|
||||||
|
<body>
|
||||||
|
<nav>
|
||||||
|
<a href="{{ url_for('main.index') }}">Kontor</a>
|
||||||
|
<a href="{{ url_for('comics_bp.index') }}">Comics</a>
|
||||||
|
<a href="{{ url_for('media_bp.mediafile_list') }}">Media</a>
|
||||||
|
</nav>
|
||||||
|
<hr>
|
||||||
|
<div class="content">
|
||||||
|
{% block content %} {% endblock %}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -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 %}
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
project kontor "Kontor" "0.1.0" 2024-12-05 +5m {
|
||||||
|
timezone "Europe/Berlin"
|
||||||
|
timeformat "%d.%m.%Y"
|
||||||
|
numberformat "-" "" "" "," 1
|
||||||
|
currencyformat "-" "" "" "," 0
|
||||||
|
currency "EUR"
|
||||||
|
|
||||||
|
scenario plan "Plan" {
|
||||||
|
scenario real "Realität"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource gcpce "Google Cloud Compute Engine" {
|
||||||
|
efficiency 0.0
|
||||||
|
rate 0.25
|
||||||
|
}
|
||||||
|
|
||||||
|
task flask "Kontor-Flask" {
|
||||||
|
task import "Import repository kontor-flask into directory flask"
|
||||||
|
}
|
||||||
|
task springboot "Springboot Vaadin" {
|
||||||
|
task import "Import repository kontor-spring into directory springboot"
|
||||||
|
}
|
||||||
|
|
||||||
|
taskreport "Arbeitsliste" {
|
||||||
|
formats html
|
||||||
|
hidetask ~isleaf()
|
||||||
|
sorttasks plan.end.up
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user