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