Browse Source

webflask init

master
Ulrich Carmesin 2 years ago
parent
commit
b4f4711f76
  1. 5
      .gitignore
  2. 0
      utils/flask_tool.py
  3. 8
      webdev.sh
  4. 57
      webflask/__init__.py
  5. 31
      webflask/application.py
  6. 92
      webflask/auth.py
  7. 31
      webflask/components.py
  8. 31
      webflask/datest.py
  9. 41
      webflask/db.py
  10. 31
      webflask/environment.py
  11. 23
      webflask/index.py
  12. 31
      webflask/reports.py
  13. 17
      webflask/schema.sql
  14. 20
      webflask/static/script.js
  15. 31
      webflask/static/style.css
  16. 9
      webflask/templates/application/overview.html
  17. 16
      webflask/templates/auth/login.html
  18. 17
      webflask/templates/auth/register.html
  19. 40
      webflask/templates/base.html
  20. 9
      webflask/templates/datest/overview.html
  21. 9
      webflask/templates/environment/overview.html
  22. 12
      webflask/templates/index.html
  23. 9
      webflask/templates/reports/overview.html
  24. 32
      webflask/templates/testcase/overview.html
  25. 9
      webflask/templates/testsuite/overview.html
  26. 41
      webflask/testcase.py
  27. 31
      webflask/testsuite.py

5
.gitignore

@ -7,4 +7,9 @@ test/environment
test/conf
.idea
config
instance
doc
venv
lib

0
utils/flask_tool.py

8
webdev.sh

@ -0,0 +1,8 @@
#!/usr/bin/bash
# script to start development web in webflask - port 5000
export FLASK_APP=webflask
export FLASK_DEBUG=1
# flask init-db
flask run

57
webflask/__init__.py

@ -0,0 +1,57 @@
import os
from flask import Flask
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'),
)
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# load the test config if passed in
app.config.from_mapping(test_config)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
from . import db
db.init_app(app)
from . import auth
from . import index
from . import testcase
from . import testsuite
from . import components
from . import reports
from . import environment
app.register_blueprint(auth.bp)
app.register_blueprint(index.bp)
app.register_blueprint(testcase.bp)
app.register_blueprint(testsuite.bp)
app.register_blueprint(components.bp)
app.register_blueprint(reports.bp)
app.register_blueprint(environment.bp)
# a simple page that says hello
@app.route('/hello')
def hello():
return 'Hello, World!'
return app
@app.route('/')
def index():
return 'Index Page'
@app.route('/index')
def index():
return 'Index Page 2'

31
webflask/application.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('testcase', __name__, url_prefix='/testcase')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/overview.html')

92
webflask/auth.py

@ -0,0 +1,92 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('auth', __name__, url_prefix='/auth')
@bp.route('/login', methods=('GET', 'POST'))
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
user = db.execute(
'SELECT * FROM user WHERE username = ?', (username,)
).fetchone()
if user is None:
error = 'Incorrect username.'
elif not check_password_hash(user['password'], password):
error = 'Incorrect password.'
if error is None:
session.clear()
session['user_id'] = user['id']
return redirect(url_for('index'))
flash(error)
return render_template('auth/login.html')
@bp.route('/register', methods=('GET', 'POST'))
def register():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
if not username:
error = 'Username is required.'
elif not password:
error = 'Password is required.'
if error is None:
try:
db.execute(
"INSERT INTO user (username, password) VALUES (?, ?)",
(username, generate_password_hash(password)),
)
db.commit()
except db.IntegrityError:
error = f"User {username} is already registered."
else:
return redirect(url_for("auth.login"))
flash(error)
return render_template('auth/register.html')
@bp.before_app_request
def load_logged_in_user():
user_id = session.get('user_id')
if user_id is None:
g.user = None
else:
g.user = get_db().execute(
'SELECT * FROM user WHERE id = ?', (user_id,)
).fetchone()
@bp.route('/logout')
def logout():
session.clear()
return redirect(url_for('index'))
def login_required(view):
@functools.wraps(view)
def wrapped_view(**kwargs):
if g.user is None:
return redirect(url_for('auth.login'))
return view(**kwargs)
return wrapped_view

31
webflask/components.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('components', __name__, url_prefix='/components')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('components/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('components/overview.html')

31
webflask/datest.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('testcase', __name__, url_prefix='/testcase')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/overview.html')

41
webflask/db.py

@ -0,0 +1,41 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/database/
# ----------------------------------------------------------------------------
import sqlite3
import click
from flask import current_app, g
from flask.cli import with_appcontext
def init_db():
db = get_db()
with current_app.open_resource('schema.sql') as f:
db.executescript(f.read().decode('utf8'))
def get_db():
if 'db' not in g:
g.db = sqlite3.connect(
current_app.config['DATABASE'],
detect_types=sqlite3.PARSE_DECLTYPES
)
g.db.row_factory = sqlite3.Row
return g.db
def close_db(e=None):
db = g.pop('db', None)
if db is not None:
db.close()
@click.command('init-db')
@with_appcontext
def init_db_command():
"""Clear the existing data and create new tables."""
init_db()
click.echo('Initialized the database.')
def init_app(app):
app.teardown_appcontext(close_db)
app.cli.add_command(init_db_command)

31
webflask/environment.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('environment', __name__, url_prefix='/environment')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('environment/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('environment/overview.html')

23
webflask/index.py

@ -0,0 +1,23 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('index', __name__, url_prefix='/')
@bp.route('/', methods=('GET', 'POST'))
def index():
if request.method == 'POST':
error = None
flash(error)
return render_template('index.html')

31
webflask/reports.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('reports', __name__, url_prefix='/reports')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('reports/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('reports/overview.html')

17
webflask/schema.sql

@ -0,0 +1,17 @@
DROP TABLE IF EXISTS user;
DROP TABLE IF EXISTS post;
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
CREATE TABLE post (
id INTEGER PRIMARY KEY AUTOINCREMENT,
author_id INTEGER NOT NULL,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
title TEXT NOT NULL,
body TEXT NOT NULL,
FOREIGN KEY (author_id) REFERENCES user (id)
);

20
webflask/static/script.js

@ -0,0 +1,20 @@
addEventListener("load", addCollaps);
function addCollaps() {
var coll = document.getElementsByClassName("collapsible");
for (var c of coll) {
c.addEventListener("click", function() {
this.nextElementSibling.classList.toggle("collapsed");
});
}
}
// TODO : kann raus
function loadJson(filename, callback) {
var xhr = XMLHttpRequest();
xhr.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
callback(JSON.parse(xhr.responseText));
}
}
xhr.open(get, filename);
xhr.send();
}

31
webflask/static/style.css

@ -0,0 +1,31 @@
html { font-family: sans-serif; background: #eee; padding: 1rem; }
body { max-width: 960px; margin: 0 auto; background: white; }
h1 { font-family: serif; color: #377ba8; margin: 1rem 0; }
a { color: #377ba8; }
hr { border: none; border-top: 1px solid lightgray; }
nav { background: lightgray; display: flex; align-items: center; padding: 0 0.5rem; }
nav h1 { flex: auto; margin: 0; }
nav h1 a { text-decoration: none; padding: 0.25rem 0.5rem; }
nav ul { display: flex; list-style: none; margin: 0; padding: 0; }
nav ul li a, nav ul li span, header .action { display: block; padding: 0.5rem; }
.content { padding: 0 1rem 1rem; }
.content > header { border-bottom: 1px solid lightgray; display: flex; align-items: flex-end; }
.content > header h1 { flex: auto; margin: 1rem 0 0.25rem 0; }
.flash { margin: 1em 0; padding: 1em; background: #cae6f6; border: 1px solid #377ba8; }
.post > header { display: flex; align-items: flex-end; font-size: 0.85em; }
.post > header > div:first-of-type { flex: auto; }
.post > header h1 { font-size: 1.5em; margin-bottom: 0; }
.post .about { color: slategray; font-style: italic; }
.post .body { white-space: pre-line; }
.content:last-child { margin-bottom: 0; }
.content form { margin: 1em 0; display: flex; flex-direction: column; }
.content label { font-weight: bold; margin-bottom: 0.5em; }
.content input, .content textarea { margin-bottom: 1em; }
.content textarea { min-height: 12em; resize: vertical; }
input.danger { color: #cc2f2e; }
input[type=submit] { align-self: start; min-width: 10em; }
.collapsible { background-color: #777; color: white; cursor: pointer; padding: 5px; width: 100%;
border: none; text-align: left; outline: none; font-size: 15px; }
.collapsed { padding: 0 18px; display: none; overflow: hidden; }
.active, .collapsible:hover { background-color: #ccc; }

9
webflask/templates/application/overview.html

@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein</p>
{% endblock %}

16
webflask/templates/auth/login.html

@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Log In{% endblock %}</h1>
{% endblock %}
{% block content %}
<form method="post">
<label for="username">Username</label>
<input name="username" id="username" required>
<label for="password">Password</label>
<input type="password" name="password" id="password" required>
<input type="submit" value="Log In">
</form>
<p>hiermit sollte ein Workspace geoeffnet werden <br/> - bestehend aus eigenem testdata-repo und components-repo.</p>
{% endblock %}

17
webflask/templates/auth/register.html

@ -0,0 +1,17 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Register{% endblock %}</h1>
{% endblock %}
{% block content %}
<form method="post">
<label for="username">Username</label>
<input name="username" id="username" required>
<label for="password">Password</label>
<input type="password" name="password" id="password" required>
<input type="submit" value="Register">
</form>
<p>Durch das Registrieren sollte ein Workspace angelegt werden <br/>
- bestehend aus eine testdata-repo und einem components-repo </p>
{% endblock %}

40
webflask/templates/base.html

@ -0,0 +1,40 @@
<!doctype html>
<head>
<title>{% block title %}{% endblock %} - Datest</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<script type="text/javascript" src="{{ url_for('static', filename='script.js') }}"></script>
<script>
var jsonData = "{{ get_json() }}";
</script>
</head>
<body>
<nav>
<h1>Datest</h1>
<ul>
{% if g.user %}
<li><span>{{ g.user['username'] }}</span>
<li><a href="{{ url_for('testcase.overview') }}">Testcase</a>
<li><a href="{{ url_for('testsuite.overview') }}">Testsuite</a>
<li><a href="{{ url_for('reports.overview') }}">Reports</a>
<li><a href="{{ url_for('components.overview') }}">Components</a>
<li><a href="{{ url_for('auth.logout') }}">Log Out</a>
{% else %}
<li><a href="{{ url_for('testcase.overview') }}">Testcase</a>
<li><a href="{{ url_for('testsuite.overview') }}">Testsuite</a>
<li><a href="{{ url_for('reports.overview') }}">Reports</a>
<li><a href="{{ url_for('components.overview') }}">Components</a>
<li><a href="{{ url_for('auth.register') }}">Register</a>
<li><a href="{{ url_for('auth.login') }}">Log In</a>
{% endif %}
</ul>
</nav>
<section class="content">
<header>
{% block header %}{% endblock %}
</header>
{% for message in get_flashed_messages() %}
<div class="flash">{{ message }}</div>
{% endfor %}
{% block content %}{% endblock %}
</section>
</body>

9
webflask/templates/datest/overview.html

@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein</p>
{% endblock %}

9
webflask/templates/environment/overview.html

@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein</p>
{% endblock %}

12
webflask/templates/index.html

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein:<br/>
- info Datest allgemein -> ucarmesin.de/joomla
- Bereiche Testfaelle (anzahl)
- letzte Updates (git log) </p>
{% endblock %}

9
webflask/templates/reports/overview.html

@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein</p>
{% endblock %}

32
webflask/templates/testcase/overview.html

@ -0,0 +1,32 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein. <br/>
Bei angemeldetem User kann auf das eigene Repo schreibend zugegriffen werden,
als Gast nur lesend auf die Vorlage.</p>
<p>Statistische Daten zu dem Testfaellen: <br/>
- Anzahl in Projekten und Anwendungen <br/>
- letzte Durchfuehrungen (log) <br/>
- letzte Aenderungen (git log) <br/>
- info zu Begriff Testfall -> ucarmesin.de/joomla <br/>
- Suchfeld fuer speziellen Testfall -> Anzeige des Testfalls </p>
<button type="button" class="collapsible" data-toggle="collapse">Block Head/Option</button>
<div class="content collapsed" id="collapseOptions">
<p>Diese Attribute ordnen den Testfall in einen Kontext ein und setzen Bearbeitungsparameter fuer die Durchfuehrung.</p>
</div>
<button type="button" class="collapsible" data-toggle="collapse">Block Steps</button>
<div class="content collapsed" id="collapseSteps">
<p>Diese Attribute beschreiben die Schritte, die waehrend der Durchfuehrungsphase ausgefuehrt werden.
Die benoetigten Testdaten werden aus den Tabellen referenziert.</p>
</div>
<button type="collapscontent" class="collapsible" data-toggle="collapse">Block Tabellen</button>
<div class="content collapsed" id="collapseTables">
<p>In den Tabellen sind die Testdaten hinterlegt, die waehrend der Initialisierungsphase vorbefuellt werden
oder waehrend der Durchfuehrungsphase eingespielt werden.</p>
</div>
{% endblock %}

9
webflask/templates/testsuite/overview.html

@ -0,0 +1,9 @@
{% extends 'base.html' %}
{% block header %}
<h1>{% block title %}Overview{% endblock %}</h1>
{% endblock %}
{% block content %}
<p>Hier kommt Inhalt rein</p>
{% endblock %}

41
webflask/testcase.py

@ -0,0 +1,41 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
import utils.config_tool
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('testcase', __name__, url_prefix='/testcase')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
"""
it shows an overview to testcase
:param: nothing
:return: rendered page with jsondata
* info with link to ucarmesin.de/joomla
* last git-updates (specifications)
* last executions (-> reports) with status running|to-check|with-faults|ok
* selection of conrete testcase by application, testsuite, project, regex?, full name
"""
if request.method == 'POST':
error = None
flash(error)
return render_template('testcase/overview.html')

31
webflask/testsuite.py

@ -0,0 +1,31 @@
# https://flask.palletsprojects.com/en/2.0.x/tutorial/views/
# --------------------------------------------------------------
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from webflask.db import get_db
bp = Blueprint('testsuite', __name__, url_prefix='/testsuite')
@bp.route('/selection', methods=('GET', 'POST'))
def selection():
if request.method == 'POST':
error = None
flash(error)
return render_template('testsuite/selection.html')
@bp.route('/overview', methods=('GET', 'POST'))
def overview():
if request.method == 'POST':
error = None
flash(error)
return render_template('testsuite/overview.html')
Loading…
Cancel
Save