Compare commits
6 Commits
b2b5bd9770
...
main
Author | SHA1 | Date | |
---|---|---|---|
0fd7679ac7 | |||
41a1bb3530 | |||
751c67c05b | |||
0e55fd55fd | |||
e7e70daec0 | |||
1231787e08 |
30
app.py
30
app.py
@ -1,30 +0,0 @@
|
||||
from flask import Flask, render_template, request, jsonify
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
# Initialize the database object
|
||||
db = SQLAlchemy()
|
||||
|
||||
def create_app():
|
||||
# Create a new Flask application instance
|
||||
app = Flask(__name__)
|
||||
|
||||
# Configure the SQLite database URI for the application
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///mydatabase.db'
|
||||
|
||||
# Disable tracking modifications to SQLAlchemy objects
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
|
||||
# Initialize the database object with the Flask application instance
|
||||
db.init_app(app)
|
||||
|
||||
# Import and initialize models, then create tables within the app context
|
||||
with app.app_context():
|
||||
from models import User # Assuming there is a file named models.py containing the User model
|
||||
from utils import check_and_create_tables # Assuming there is a file named utils.py containing the check_and_create_tables function
|
||||
check_and_create_tables(db) # Call the function to ensure tables are created if they don't exist
|
||||
return app
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Create and run the Flask application instance with debugging enabled
|
||||
app = create_app()
|
||||
app.run(debug=True)
|
@ -1,3 +0,0 @@
|
||||
class Config:
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///learn.db'
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
22
lernplattform/__init__.py
Normal file
22
lernplattform/__init__.py
Normal file
@ -0,0 +1,22 @@
|
||||
from flask import Flask
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
# Initialize a new Flask app instance with the name of the current module (main).
|
||||
app = Flask(__name__)
|
||||
|
||||
# Set the secret key for this application. This is required for session management in Flask.
|
||||
# The secret key should be a cryptographically secure random value, and it's used to sign cookies.
|
||||
# For more information: https://flask.palletsprojects.com/en/2.3.x/config/#SECRET_KEY
|
||||
app.secret_key = 'faab0674dd8d9a4554cbeb53da2dad1414cc4c64b778e282da0b1662df7e0f85'
|
||||
|
||||
# Configure the database URI for SQLAlchemy. Here, it uses an SQLite database named 'learn.db'.
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///learn.db'
|
||||
|
||||
# Disable tracking modifications to the database, which is not necessary in this case and can improve performance.
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
|
||||
# Initialize SQLAlchemy with the Flask app instance for database management.
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
# Import the routes module from the lernplattform package, which is assumed to be in the same directory.
|
||||
import lernplattform.routes
|
6
lernplattform/app.py
Normal file
6
lernplattform/app.py
Normal file
@ -0,0 +1,6 @@
|
||||
from lernplattform import app
|
||||
from lernplattform.models import initialize_database
|
||||
|
||||
if __name__ == '__main__':
|
||||
initialize_database() # Initialisiere die Datenbank beim Start der Anwendung
|
||||
app.run(debug=True)
|
82
lernplattform/classes.py
Normal file
82
lernplattform/classes.py
Normal file
@ -0,0 +1,82 @@
|
||||
from flask import Flask, request, session, make_response, redirect, url_for
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from lernplattform import app, db
|
||||
from lernplattform.models import User
|
||||
import random
|
||||
import string
|
||||
|
||||
def generate_random_string(length=8):
|
||||
characters = string.ascii_letters + string.digits
|
||||
random_string = ''.join(random.choice(characters) for _ in range(length))
|
||||
return random_string
|
||||
|
||||
class login():
|
||||
"""
|
||||
A class for handling user login functionality.
|
||||
|
||||
Attributes:
|
||||
user_name (str): The username to be checked for authentication.
|
||||
|
||||
Methods:
|
||||
__init__(self, user_name=None): Initializes the login object with a given user name.
|
||||
check(user_name): A static method that checks if the provided user name is valid.
|
||||
login(self): Prints "Login" to indicate that the login method has been called.
|
||||
"""
|
||||
def __init__(self, user_name=None, login_data=None):
|
||||
self.user_name = user_name # Initialize the user name attribute for the login class
|
||||
|
||||
def check_logged_in():
|
||||
if request.cookies.get('username') is None:
|
||||
session['logged_in'] = False
|
||||
session['username'] = 'Gast'
|
||||
return False # Return False if the user name is not provided
|
||||
else:
|
||||
session['logged_in'] = True
|
||||
session['username'] = request.cookies.get('username')
|
||||
session['userid'] = request.cookies.get('userid')
|
||||
|
||||
return True # Otherwise, return True indicating a valid user name
|
||||
|
||||
def login(login_data):
|
||||
# get password from db
|
||||
user = User.query.filter_by(username = login_data.get('username')).first()
|
||||
|
||||
if user and check_password_hash(user.password, login_data.get('password')):
|
||||
if not user.email_check == "":
|
||||
return False
|
||||
|
||||
session['userid'] = user.id
|
||||
session['username'] = user.username
|
||||
session['logged_in'] = True
|
||||
|
||||
if login_data['remember'] == 'on':
|
||||
resp = make_response(redirect(url_for('index')))
|
||||
resp.set_cookie('userid', user.id)
|
||||
resp.set_cookie('username', user.username)
|
||||
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def logout():
|
||||
resp = make_response(redirect(url_for('index')))
|
||||
resp.set_cookie('username', '', expires=0)
|
||||
resp.set_cookie('userid', '', expires=0)
|
||||
session['logged_in'] = False
|
||||
|
||||
return resp
|
||||
|
||||
def register(request):
|
||||
username = request.form.get('username')
|
||||
email = request.form.get('email')
|
||||
password = request.form.get('password')
|
||||
hashed_password = generate_password_hash(password, method='pbkdf2:sha256')
|
||||
|
||||
if User.query.filter_by(username = username).first():
|
||||
return False # Return False if the user name is already in use
|
||||
else:
|
||||
random_string = generate_random_string()
|
||||
user = User(username = username, email = email, password = hashed_password, email_check = random_string)
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
return True
|
36
lernplattform/models.py
Normal file
36
lernplattform/models.py
Normal file
@ -0,0 +1,36 @@
|
||||
from lernplattform import db
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
# Define a User model that represents the user table in the database
|
||||
class User(db.Model):
|
||||
"""
|
||||
A class representing the user table in the database.
|
||||
|
||||
Attributes:
|
||||
id (int): The unique identifier for each user, set as the primary key.
|
||||
username (str): The username of the user, must be unique and cannot be null.
|
||||
email (str): The email address of the user, must be unique and cannot be null.
|
||||
password (str): The hashed password of the user, cannot be null.
|
||||
email_check (str): A string field to store email verification status.
|
||||
register_date (datetime): The date and time when the user registered, defaults to the current timestamp.
|
||||
|
||||
Methods:
|
||||
__repr__(): Returns a string representation of the User object in the format "User('username', 'email')".
|
||||
"""
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(20), unique=True, nullable=False)
|
||||
email = db.Column(db.String(120), unique=True, nullable=False)
|
||||
password = db.Column(db.String(60), nullable=False)
|
||||
email_check = db.Column(db.String(10), nullable=True)
|
||||
register_date = db.Column(db.DateTime, nullable=False, default=func.now())
|
||||
|
||||
# Magic method to represent the object as a string when printed or used in string context
|
||||
def __repr__(self):
|
||||
return f"User('{self.username}', '{self.email}')"
|
||||
|
||||
# Function to initialize the database by creating all tables defined in models if they do not exist
|
||||
def initialize_database():
|
||||
from lernplattform import app
|
||||
# Create an application context before running operations on the database within this function
|
||||
with app.app_context():
|
||||
db.create_all()
|
53
lernplattform/routes.py
Normal file
53
lernplattform/routes.py
Normal file
@ -0,0 +1,53 @@
|
||||
from flask import render_template, url_for, flash, redirect, jsonify, request, session
|
||||
from lernplattform import app
|
||||
from lernplattform.models import User
|
||||
|
||||
from lernplattform import db
|
||||
from lernplattform.classes import login
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
"""
|
||||
Renders the main page of the application.
|
||||
This function checks if a user is logged in by checking for a 'user_name' cookie.
|
||||
If the user is logged in, it retrieves the username from the database using the cookie value.
|
||||
If not, it defaults to 'Gast'. The main page is then rendered with the current username.
|
||||
|
||||
Returns:
|
||||
A rendered HTML template with the current username.
|
||||
"""
|
||||
login.check_logged_in()
|
||||
|
||||
# Render the main page with the current username
|
||||
return render_template('index.html', page='index', user_name=session['username'])
|
||||
|
||||
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def userlogin():
|
||||
if request.method == 'POST':
|
||||
#data = request.form
|
||||
if request.form.get('form_id') == 'login':
|
||||
if login.login(request.form):
|
||||
# get username, password, remember from form
|
||||
flash('Login erfolgreich!', 'login')
|
||||
return redirect(url_for('index'))
|
||||
else:
|
||||
flash('Ungültiger Benutzername, Passwort oder Registrierung noch nicht bestätigt.', 'login')
|
||||
|
||||
# Render the main page with the current username
|
||||
return render_template('index.html', page='index', user_name=session['username'])
|
||||
|
||||
|
||||
@app.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
if request.method == 'POST':
|
||||
if login.register(request):
|
||||
flash('''
|
||||
Du erhältst in den nächsten Minuten eine eMail mit einem Bestätigungslink.
|
||||
Bitte klicke auf diesen Link, um deine Registrierung abzuschließen.
|
||||
''', 'register')
|
||||
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
return render_template('index.html', page='register', user_name=session['username'])
|
BIN
lernplattform/static/images/logo.png
Normal file
BIN
lernplattform/static/images/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 403 KiB |
20
lernplattform/static/javascript.js
Normal file
20
lernplattform/static/javascript.js
Normal file
@ -0,0 +1,20 @@
|
||||
function validateForm() {
|
||||
var username = document.forms["loginForm"]["username"].value;
|
||||
var password = document.forms["loginForm"]["password"].value;
|
||||
if (username == "" || password == "") {
|
||||
alert("Benutzername und Passwort müssen ausgefüllt werden.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkPasswords() {
|
||||
var password = document.forms["registerForm"]["password"].value;
|
||||
var confirmPassword = document.forms["registerForm"]["password_repeat"].value;
|
||||
|
||||
if (password != confirmPassword) {
|
||||
alert("Passwörter stimmen nicht überein.");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
103
lernplattform/static/style.css
Normal file
103
lernplattform/static/style.css
Normal file
@ -0,0 +1,103 @@
|
||||
:root {
|
||||
--primary-color: #102037; /* Definieren Sie die primäre Farbe */
|
||||
--secondary-color: #f78b47; /* Definieren Sie die sekundäre Farbe */
|
||||
--link-default-color: #ffffff; /* Definieren Sie die Link-Hover-Farbe */
|
||||
--link-hover-color: #ffcc00; /* Definieren Sie die Link-Hover-Farbe */
|
||||
--link-active-color: #ffcc00; /* Definieren Sie die Link-Hover-Farbe */
|
||||
--background-color: #d4c4c4; /* Definieren Sie die Hintergrundfarbe */
|
||||
--nav-background-color: #102037; /* Definieren Sie die Hintergrundfarbe */
|
||||
--box-background-color: #102037;
|
||||
}
|
||||
body {
|
||||
color: #ffffff;
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
.light {
|
||||
color: #cdcdcd;
|
||||
font-weight: 400;
|
||||
font-size: small;
|
||||
}
|
||||
.sidebar {
|
||||
height: 100%;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
.main-content {
|
||||
background-color: var(--background-color);
|
||||
}
|
||||
.profile-card,
|
||||
.story,
|
||||
.post,
|
||||
.who-to-follow,
|
||||
.news {
|
||||
background-color: var(--box-background-color);
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.headbar {
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.profile-card img,
|
||||
.who-to-follow img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.story input {
|
||||
border-radius: 30px;
|
||||
}
|
||||
.story button {
|
||||
border-radius: 20px;
|
||||
}
|
||||
.post img {
|
||||
width: 100%;
|
||||
border-radius: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.who-to-follow button {
|
||||
border-radius: 20px;
|
||||
}
|
||||
.navbar {
|
||||
background-color: var(--nav-background-color);
|
||||
padding: 0;
|
||||
}
|
||||
.navbar-nav .nav-link {
|
||||
color: var(--link-default-color)
|
||||
}
|
||||
|
||||
.navbar-nav .nav-link:hover {
|
||||
color: var(--link-hover-color);
|
||||
}
|
||||
|
||||
.navbar-nav .nav-link.active {
|
||||
color: var(--link-active-color);
|
||||
}
|
||||
|
||||
a {
|
||||
color: #ffcc00;
|
||||
}
|
||||
a:hover {
|
||||
color: #c07427;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display:inline;
|
||||
width: 50px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: var(--nav-background-color);
|
||||
padding: 0;
|
||||
box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.errormsg {
|
||||
color: red;
|
||||
font-size: 15px;
|
||||
}
|
48
lernplattform/templates/block_content_index.html
Normal file
48
lernplattform/templates/block_content_index.html
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
<!-- Post a Story -->
|
||||
<!--
|
||||
<div class="story p-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<img
|
||||
src="https://via.placeholder.com/50"
|
||||
class="rounded-circle me-3"
|
||||
alt="Profile Picture" />
|
||||
<input type="text" class="form-control" placeholder="Share your thoughts..." />
|
||||
</div>
|
||||
<div class="d-flex justify-content-around mt-3">
|
||||
<button class="btn btn-outline-primary">Photo</button>
|
||||
<button class="btn btn-outline-primary">Video</button>
|
||||
<button class="btn btn-outline-primary">Event</button>
|
||||
<button class="btn btn-outline-primary">Feeling/Activity</button>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<!-- Posts -->
|
||||
<div class="post p-3">
|
||||
|
||||
<div class="d-flex">
|
||||
<img
|
||||
src="../static/images/logo.png"
|
||||
class="rounded-circle me-3"
|
||||
style="width: 300px; height: 300px;"
|
||||
alt="Profile Picture" />
|
||||
<div class="my-3">
|
||||
<h6>Lernplattform für <br>Rettungs- und Notfallsanitäter</h6>
|
||||
<p class="light" style="text-align: justify;">
|
||||
Willkommen auf unserer Lernplattform für Rettungssanitäter und Notfallsanitäter,
|
||||
basierend auf dem bewährten Kartensystem nach Leitner.
|
||||
Unsere Plattform bietet Dir die Möglichkeit,
|
||||
Deine Kenntnisse und Fähigkeiten effektiv zu vertiefen und zu erweitern. <br><br>
|
||||
Bei Interesse bist Du herzlich eingeladen, an der Entwicklung von Fragen und Aufgaben mitzuwirken.
|
||||
Unsere Plattform ist und bleibt kostenlos, um alle Mitarbeiter im Rettungsdienst bestmöglich zu unterstützen.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="mt-3">
|
||||
I'm thrilled to share that I've completed a graduate certificate course in project
|
||||
management with the president's honor roll.
|
||||
</p>
|
||||
<img src="https://via.placeholder.com/600x300" alt="Post Image" />
|
||||
</div>
|
39
lernplattform/templates/block_lernfelder.html
Normal file
39
lernplattform/templates/block_lernfelder.html
Normal file
@ -0,0 +1,39 @@
|
||||
<div class="who-to-follow p-3">
|
||||
<h6>Lernfelder</h6>
|
||||
<div class="d-flex align-items-center my-2">
|
||||
<img
|
||||
src="https://via.placeholder.com/50"
|
||||
class="rounded-circle me-3"
|
||||
alt="Profile Picture" />
|
||||
<div>
|
||||
<h6 class="mb-0">Anatomie</h6>
|
||||
<small class="light">RettSan</small>
|
||||
</div>
|
||||
<button class="btn btn-outline-primary ms-auto">+</button>
|
||||
</div>
|
||||
<div class="d-flex align-items-center my-2">
|
||||
<img
|
||||
src="https://via.placeholder.com/50"
|
||||
class="rounded-circle me-3"
|
||||
alt="Profile Picture" />
|
||||
<div>
|
||||
<h6 class="mb-0">Medikamente</h6>
|
||||
<small class="light">NotSan</small>
|
||||
</div>
|
||||
<button class="btn btn-outline-primary ms-auto">+</button>
|
||||
</div>
|
||||
<div class="d-flex align-items-center my-2">
|
||||
<img
|
||||
src="https://via.placeholder.com/50"
|
||||
class="rounded-circle me-3"
|
||||
alt="Profile Picture" />
|
||||
<div>
|
||||
<h6 class="mb-0">Herzkreislauf</h6>
|
||||
<small class="light">RettSan</small>
|
||||
</div>
|
||||
<button class="btn btn-outline-primary ms-auto">+</button>
|
||||
</div>
|
||||
<div class="text-center mt-3">
|
||||
<button class="btn btn-link">Mehr Lernfelder</button>
|
||||
</div>
|
||||
</div>
|
54
lernplattform/templates/block_login.html
Normal file
54
lernplattform/templates/block_login.html
Normal file
@ -0,0 +1,54 @@
|
||||
<!-- wenn logged out -->
|
||||
{% if not session['logged_in'] %}
|
||||
<h5>{{ user_name }}</h5>
|
||||
<p>Login oder <a href="/register">Registrieren</a></p>
|
||||
<p class="light">Warum registrieren? Zum Schutz der Daten und dem Speichern deines Lernfortschrittes.</p>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
{% if category == 'login' %}
|
||||
<p class="errormsg">{{message}}</p>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
<form name="loginForm" action="/login" method="post" onsubmit="return validateForm()">
|
||||
<div class="row mb-3">
|
||||
<label for="inputEmail3" class="col-sm-3 col-form-label">Benutzer</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" class="form-control" id="username" name="username">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="inputPassword3" class="col-sm-3 col-form-label">Password</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="password" class="form-control" id="password" name="password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-sm-8 offset-2">
|
||||
<input type="checkbox" name="remember" id="remember">
|
||||
<label for="remember">Eingeloggt bleiben</label>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="form_id" value="login">
|
||||
<button type="submit" class="btn btn-primary">Einloggen</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<!-- wenn logged out -->
|
||||
|
||||
<!-- wenn logged in -->
|
||||
{% if session['logged_in'] %}
|
||||
<!--<img src="https://via.placeholder.com/100" alt="Profile Picture" />-->
|
||||
<h5>{{ user_name }}</h5>
|
||||
<p>Web Developer at Webestica</p>
|
||||
<p class="light">I'd love to change the world, but they won’t give me the source code.</p>
|
||||
<div class="d-flex justify-content-around">
|
||||
<span>256 Posts</span>
|
||||
<span>2.5K Followers</span>
|
||||
<span>365 Following</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- wenn logged in -->
|
28
lernplattform/templates/block_register.html
Normal file
28
lernplattform/templates/block_register.html
Normal file
@ -0,0 +1,28 @@
|
||||
<div class="story p-3 shadow-sm">
|
||||
<div>
|
||||
<h5>Registrierung</h5>
|
||||
<p>Registriere dich, um die Plattform zu nutzen. Deine Daten werden nicht an Dritte weitergegeben oder anderweitig anderweitig
|
||||
verarbeitet. Die dienen lediglich zur Authentifizierung und Speicherung deines Lernfortschrittes.</p>
|
||||
</div>
|
||||
<form name="registerForm" method="post" onsubmit="return checkPasswords()">
|
||||
<div>
|
||||
<div class="row">
|
||||
<div class="col py-3">
|
||||
Benutzername: <input type="text" name="username" class="form-control" placeholder="Benutzername" tabindex="1" required />
|
||||
<br>
|
||||
Passwort: <input type="password" id="password" name="password" class="form-control" placeholder="Passwort" tabindex="3" required />
|
||||
</div>
|
||||
<div class="col py-3">
|
||||
EMail: <input type="email" name="email" class="form-control" placeholder="EMail" tabindex="2" required />
|
||||
<br>
|
||||
Passwort wiederholen: <input type="password" id="password_repeat" name="password_repeat" class="form-control" tabindex="4" placeholder="Passwort wiederholen" required />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="d-flex justify-content-around mt-3">
|
||||
<button type="submit" class="btn btn-outline-primary">Registrieren</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
81
lernplattform/templates/index.html
Normal file
81
lernplattform/templates/index.html
Normal file
@ -0,0 +1,81 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Lernplattform EMT/Paramedic</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="../static/style.css" rel="stylesheet"/>
|
||||
<script src="../static/javascript.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
{% include 'nav.html' %}
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<!-- Sidebar -->
|
||||
<div class="col-12 col-md-3 sidebar mb-3">
|
||||
<div class="profile-card text-center p-3">
|
||||
|
||||
{% include 'block_login.html' %}
|
||||
|
||||
</div>
|
||||
<!--
|
||||
<div class="list-group">
|
||||
<a href="#" class="list-group-item list-group-item-action">Feed</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Connections</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Latest News</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Events</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Groups</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Notifications</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Settings</a>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="col-12 col-md-6 main-content mb-3">
|
||||
|
||||
{% if not page or page == '' or page == 'index' %}
|
||||
{% include 'block_content_index.html' %}
|
||||
{% elif page == 'register' %}
|
||||
{% include 'block_register.html' %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Right Sidebar -->
|
||||
<div class="col-12 col-md-3 mb-3">
|
||||
<!-- Who to Follow -->
|
||||
{% include 'block_lernfelder.html' %}
|
||||
|
||||
<!-- Today's News -->
|
||||
<div class="news p-3">
|
||||
<h6>Datenbank - Inhalte</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">
|
||||
Lernfelder <small class="light">3</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
Fragen RettSan <small class="light">38</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
Fragen NotSan <small class="light">40</small>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer text-center py-3 mt-3">
|
||||
<div class="container">
|
||||
<small>© 2024 Manuel Weiser. Alle Rechte vorbehalten.</small>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</html>
|
34
lernplattform/templates/nav.html
Normal file
34
lernplattform/templates/nav.html
Normal file
@ -0,0 +1,34 @@
|
||||
<nav class="navbar navbar-expand-lg navbar-light shadow-sm mb-3">
|
||||
<div class="container-fluid headbar">
|
||||
<a class="navbar-brand" href="/"><img src="../static/images/logo.png" alt="EMT Paramedic Learn" class="logo" /></a>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="/">Start</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Lernfelder</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Account</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Impressum</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<input class="form-control me-2" type="search" placeholder="Suche" aria-label="Suche" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
@ -1,7 +0,0 @@
|
||||
from app import db
|
||||
|
||||
class User(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.String(80), unique=True, nullable=False)
|
||||
password = db.Column(db.String(120), unique=True, nullable=False)
|
||||
email = db.Column(db.String(120), unique=True, nullable=False)
|
15
setup.py
Normal file
15
setup.py
Normal file
@ -0,0 +1,15 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
VERSION = '0.0.1'
|
||||
|
||||
setup(
|
||||
name='lernplattform',
|
||||
version=VERSION,
|
||||
license='MIT',
|
||||
description='Eine Plattform zum lernen nach dem Leitner System',
|
||||
author='Manuel Weiser',
|
||||
author_email='manuel.weiser@me.com',
|
||||
url='https://gitlab.fire-devils.org/ManuelW/Lerndatenbank',
|
||||
packages=find_packages(exclude=('tests', 'docs', instance, env, _idee, __pycache__, templates)),
|
||||
python_requires='>=3.10'
|
||||
)
|
@ -1,233 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Social Media Layout</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<style>
|
||||
.sidebar {
|
||||
height: 100%;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background-color: #f0f2f5;
|
||||
}
|
||||
.main-content {
|
||||
background-color: #f0f2f5;
|
||||
padding: 20px;
|
||||
}
|
||||
.profile-card,
|
||||
.story,
|
||||
.post,
|
||||
.who-to-follow,
|
||||
.news {
|
||||
background-color: #ffffff;
|
||||
padding: 20px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.profile-card img,
|
||||
.who-to-follow img {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.story input {
|
||||
border-radius: 30px;
|
||||
}
|
||||
.story button {
|
||||
border-radius: 20px;
|
||||
}
|
||||
.post img {
|
||||
width: 100%;
|
||||
border-radius: 15px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.who-to-follow button {
|
||||
border-radius: 20px;
|
||||
}
|
||||
.navbar,
|
||||
footer,
|
||||
body {
|
||||
background-color: #f0f2f5;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm mb-3">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="#"><img src="https://via.placeholder.com/30" alt="Logo" /></a>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
data-bs-target="#navbarNav"
|
||||
aria-controls="navbarNav"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="#">Demo</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Pages</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">Account</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="#">My Network</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex">
|
||||
<input class="form-control me-2" type="search" placeholder="Search" aria-label="Search" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<!-- Sidebar -->
|
||||
<div class="col-12 col-md-3 sidebar mb-3">
|
||||
<div class="profile-card text-center p-3">
|
||||
<img src="https://via.placeholder.com/100" alt="Profile Picture" />
|
||||
<h5>Sam Lanson</h5>
|
||||
<p>Web Developer at Webestica</p>
|
||||
<p class="text-muted">I'd love to change the world, but they won’t give me the source code.</p>
|
||||
<div class="d-flex justify-content-around">
|
||||
<span>256 Posts</span>
|
||||
<span>2.5K Followers</span>
|
||||
<span>365 Following</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<a href="#" class="list-group-item list-group-item-action">Feed</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Connections</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Latest News</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Events</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Groups</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Notifications</a>
|
||||
<a href="#" class="list-group-item list-group-item-action">Settings</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main Content -->
|
||||
<div class="col-12 col-md-6 main-content mb-3">
|
||||
<!-- Post a Story -->
|
||||
<div class="story p-3 shadow-sm">
|
||||
<div class="d-flex align-items-center">
|
||||
<img
|
||||
src="https://via.placeholder.com/50"
|
||||
class="rounded-circle me-3"
|
||||
alt="Profile Picture" />
|
||||
<input type="text" class="form-control" placeholder="Share your thoughts..." />
|
||||
</div>
|
||||
<div class="d-flex justify-content-around mt-3">
|
||||
<button class="btn btn-outline-primary">Photo</button>
|
||||
<button class="btn btn-outline-primary">Video</button>
|
||||
<button class="btn btn-outline-primary">Event</button>
|
||||
<button class="btn btn-outline-primary">Feeling/Activity</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Posts -->
|
||||
<div class="post p-3 shadow-sm">
|
||||
<div class="d-flex">
|
||||
<img
|
||||
src="https://via.placeholder.com/50"
|
||||
class="rounded-circle me-3"
|
||||
alt="Profile Picture" />
|
||||
<div>
|
||||
<h6>Lori Ferguson</h6>
|
||||
<small class="text-muted">Web Developer at Webestica • 2hr</small>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-3">
|
||||
I'm thrilled to share that I've completed a graduate certificate course in project
|
||||
management with the president's honor roll.
|
||||
</p>
|
||||
<img src="https://via.placeholder.com/600x300" alt="Post Image" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right Sidebar -->
|
||||
<div class="col-12 col-md-3 mb-3">
|
||||
<!-- Who to Follow -->
|
||||
<div class="who-to-follow p-3 shadow-sm">
|
||||
<h6>Who to follow</h6>
|
||||
<div class="d-flex align-items-center my-2">
|
||||
<img
|
||||
src="https://via.placeholder.com/50"
|
||||
class="rounded-circle me-3"
|
||||
alt="Profile Picture" />
|
||||
<div>
|
||||
<h6 class="mb-0">Judy Nguyen</h6>
|
||||
<small class="text-muted">News anchor</small>
|
||||
</div>
|
||||
<button class="btn btn-outline-primary ms-auto">+</button>
|
||||
</div>
|
||||
<div class="d-flex align-items-center my-2">
|
||||
<img
|
||||
src="https://via.placeholder.com/50"
|
||||
class="rounded-circle me-3"
|
||||
alt="Profile Picture" />
|
||||
<div>
|
||||
<h6 class="mb-0">Amanda Reed</h6>
|
||||
<small class="text-muted">Web Developer</small>
|
||||
</div>
|
||||
<button class="btn btn-outline-primary ms-auto">+</button>
|
||||
</div>
|
||||
<div class="d-flex align-items-center my-2">
|
||||
<img
|
||||
src="https://via.placeholder.com/50"
|
||||
class="rounded-circle me-3"
|
||||
alt="Profile Picture" />
|
||||
<div>
|
||||
<h6 class="mb-0">Billy Vasquez</h6>
|
||||
<small class="text-muted">News anchor</small>
|
||||
</div>
|
||||
<button class="btn btn-outline-primary ms-auto">+</button>
|
||||
</div>
|
||||
<div class="text-center mt-3">
|
||||
<button class="btn btn-link">View more</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Today's News -->
|
||||
<div class="news p-3 shadow-sm">
|
||||
<h6>Today's news</h6>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2">
|
||||
Ten questions you should answer truthfully <small class="text-muted">2hr</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
Five unbelievable facts about money <small class="text-muted">3hr</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
Best Pinterest Boards for learning about business <small class="text-muted">4hr</small>
|
||||
</li>
|
||||
<li class="mb-2">
|
||||
Skills that you can learn from ... <small class="text-muted">4hr</small>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="bg-white text-center py-3 shadow-sm mt-3">
|
||||
<div class="container">
|
||||
<small>© 2024 Webestica. Alle Rechte vorbehalten.</small>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</html>
|
30
utils.py
30
utils.py
@ -1,30 +0,0 @@
|
||||
from sqlalchemy import inspect, Integer, String
|
||||
|
||||
def check_and_create_tables(db):
|
||||
inspector = inspect(db.engine)
|
||||
|
||||
# Überprüfen, ob die Tabelle 'user' existiert
|
||||
if not inspector.has_table('user'):
|
||||
print("Tabelle 'user' existiert nicht, sie wird erstellt.")
|
||||
db.create_all()
|
||||
else:
|
||||
print("Tabelle 'user' existiert.")
|
||||
# Überprüfen der Spalten
|
||||
columns = inspector.get_columns('user')
|
||||
column_names = [col['name'] for col in columns]
|
||||
|
||||
# Benötigte Spalten
|
||||
required_columns = {
|
||||
'id': Integer,
|
||||
'username': String,
|
||||
'email': String
|
||||
}
|
||||
|
||||
for col_name, col_type in required_columns.items():
|
||||
if col_name not in column_names:
|
||||
print(f"Spalte '{col_name}' fehlt, sie wird hinzugefügt.")
|
||||
with db.engine.connect() as conn:
|
||||
if col_type == Integer:
|
||||
conn.execute(f'ALTER TABLE user ADD COLUMN {col_name} INTEGER')
|
||||
elif col_type == String:
|
||||
conn.execute(f'ALTER TABLE user ADD COLUMN {col_name} STRING')
|
Reference in New Issue
Block a user