diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/HabitTracker.iml b/.idea/HabitTracker.iml
new file mode 100644
index 0000000..2cdb1e3
--- /dev/null
+++ b/.idea/HabitTracker.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..c73c374
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ sqlite.xerial
+ true
+ org.sqlite.JDBC
+ jdbc:sqlite:C:\Users\alter\Documents\Schule\Informatik\HabitTracker\db\db.sqlite
+ $ProjectFileDir$
+
+
+ file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/Xerial SQLiteJDBC/3.43.0/org/xerial/sqlite-jdbc/3.43.0.0/sqlite-jdbc-3.43.0.0.jar
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..dae6042
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..fef1093
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
new file mode 100644
index 0000000..6af65ab
--- /dev/null
+++ b/.idea/sqldialects.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app.py b/app.py
new file mode 100644
index 0000000..a4921ba
--- /dev/null
+++ b/app.py
@@ -0,0 +1,79 @@
+import datetime
+
+from flask import Flask, render_template, redirect, url_for, request
+from flask_login import login_required, LoginManager
+
+from models.User import User
+
+# Create a new Flask instance
+app = Flask(__name__)
+
+
+# Create a new route
+@app.route('/')
+def index():
+ # return 'Hello World'
+ return render_template('index.html', title='Home', utc_dt=datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S"))
+
+@app.route('/test')
+@login_required
+def secret():
+ return 'Pssst!'
+
+@app.route('/login')
+def login():
+ return render_template('auth/login.html')
+
+
+@app.route('/signup')
+def signup():
+ return render_template('auth/signup.html', errors={})
+
+@app.route('/signup', methods=['POST'])
+def signup_post():
+ email = request.form.get('email')
+ name = request.form.get('name')
+ password = request.form.get('password')
+
+ # Check for errors
+ errors = {}
+ if not email:
+ errors['email'] = 'Email is required.'
+ if not name:
+ errors['name'] = 'Name is required.'
+ if not password:
+ errors['password'] = 'Password is required.'
+
+ return render_template(
+ 'auth/signup.html',
+ email=email,
+ name=name,
+ password=password,
+ errors=errors
+ )
+
+ # Save user to database. Maybe log the user in directly.
+
+ # Redirect to login page
+ return redirect(url_for('login'))
+
+
+@app.route('/logout')
+@login_required
+def logout():
+ # Log out functionality
+ return redirect(url_for('index'))
+
+
+# Run the application
+if __name__ == '__main__':
+ login_manager = LoginManager()
+ login_manager.login_view = 'login'
+ login_manager.init_app(app)
+ app.secret_key = 'PSSSSSHHHT!'
+
+ @login_manager.user_loader
+ def load_user(user_id):
+ return User.get(user_id)
+
+ app.run(port=5000, debug=True)
diff --git a/db/SQLiteClient.py b/db/SQLiteClient.py
new file mode 100644
index 0000000..4fa6c8c
--- /dev/null
+++ b/db/SQLiteClient.py
@@ -0,0 +1,28 @@
+import hashlib
+import sqlite3
+
+
+def con3():
+ conn = sqlite3.connect('db/db.sqlite')
+ return conn
+
+
+def create_user(name, email, password):
+ password = hashlib.md5(password.encode()).hexdigest()
+ query = f"INSERT INTO users (name, email, password) VALUES ({name}, {email}, {password})"
+ conn = con3()
+ cursor = conn.cursor()
+ cursor.execute(query)
+ conn.commit()
+ conn.close()
+ return cursor.lastrowid
+
+
+def get_user(id):
+ query = f"SELECT * FROM users WHERE id = {id}"
+ conn = con3()
+ cursor = conn.cursor()
+ cursor.execute(query)
+ user = cursor.fetchone()
+ conn.close()
+ return user
diff --git a/db/create_migration.py b/db/create_migration.py
new file mode 100644
index 0000000..8b00067
--- /dev/null
+++ b/db/create_migration.py
@@ -0,0 +1,30 @@
+import datetime
+import os
+
+
+def is_snake_case(s):
+ if not s:
+ return False
+
+ return s.islower() and all(char.isalpha() or char == '_' for char in s)
+
+if __name__ == "__main__":
+ migration_name = ""
+ while not is_snake_case(migration_name):
+ if migration_name:
+ print(f"{migration_name} ist nicht im Camcel-Case")
+ migration_name = input('Gebe einen Namen für die Migration im snake_case an: ')
+
+ timestamp = datetime.datetime.now().timestamp()
+ migrations_path = os.path.join(os.path.dirname(__file__), 'migrations')
+ print(migrations_path)
+
+ try:
+ if not os.path.isdir(migrations_path):
+ raise FileNotFoundError
+
+ file = open(os.path.join(os.path.dirname(__file__), 'migrations', str(int(timestamp)) + '_' + migration_name + '.sql'), 'w')
+ file.close()
+
+ except FileNotFoundError:
+ print("migrations/ Directive could not be found within the db directory, contained in the project main directory.")
\ No newline at end of file
diff --git a/db/db.sqlite b/db/db.sqlite
new file mode 100644
index 0000000..5602513
Binary files /dev/null and b/db/db.sqlite differ
diff --git a/db/migrate.py b/db/migrate.py
new file mode 100644
index 0000000..16dbe07
--- /dev/null
+++ b/db/migrate.py
@@ -0,0 +1,63 @@
+import datetime
+import os
+import sqlite3
+from pathlib import Path
+from sqlite3 import Connection
+
+CREATE_MIGRATIONS_TABLE = """
+CREATE TABLE IF NOT EXISTS migrations (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ file TEXT,
+ created_at TEXT
+);
+"""
+
+def init_db():
+ conn = sqlite3.connect("db.sqlite")
+
+ # Create migration table, if not exists yet
+ conn.execute(CREATE_MIGRATIONS_TABLE)
+ conn.commit()
+
+def create_migration(conn: Connection, migration: str):
+ conn.execute(f"""
+ INSERT INTO migrations (file, created_at) VALUES ("{migration}", "{datetime.datetime.now()}");
+ """)
+
+"""
+Returns True, if the migration has already been migrates once.
+"""
+def check_migration(conn: Connection, migration: str):
+ res = conn.cursor().execute(f"""
+ SELECT EXISTS(SELECT file FROM migrations WHERE file = "{migration}");
+ """)
+ return res.fetchone()[0] == 1
+
+if __name__ == "__main__":
+ # Init DB with migrations table
+ init_db()
+
+ migrations_path = os.path.join(os.path.dirname(__file__), 'migrations')
+
+ try:
+ if not os.path.isdir(migrations_path):
+ raise FileNotFoundError
+
+ conn = sqlite3.connect("db.sqlite")
+ for file in os.listdir(migrations_path):
+ if not file.endswith('.sql'):
+ continue
+ with open(os.path.join(os.path.dirname(__file__), 'migrations', file)) as migration:
+ migration_name = Path(file).stem
+
+ if check_migration(conn, migration_name):
+ continue
+
+ print(f"Migrating {migration_name}...")
+ create_migration(conn, migration_name)
+ conn.execute(migration.read())
+ print(f"Migrated {migration_name}\n")
+
+ conn.commit()
+ except FileNotFoundError:
+ print("migrations/ Directive could not be found within the db directory, contained in the project main directory.")
\ No newline at end of file
diff --git a/db/migrations/1705052036_create_user_table.sql b/db/migrations/1705052036_create_user_table.sql
new file mode 100644
index 0000000..da295c4
--- /dev/null
+++ b/db/migrations/1705052036_create_user_table.sql
@@ -0,0 +1,9 @@
+CREATE TABLE IF NOT EXISTS users
+(
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL,
+ email TEXT NOT NULL,
+ password TEXT NOT NULL,
+ created_at TEXT NOT NULL,
+ updated_at TEXT NOT NULL
+);
diff --git a/models/User.py b/models/User.py
new file mode 100644
index 0000000..466b112
--- /dev/null
+++ b/models/User.py
@@ -0,0 +1,18 @@
+from db.SQLiteClient import create_user, get_user
+
+
+class User:
+ def __init__(self, id, name, email):
+ self.id = id
+ self.name = name
+ self.email = email
+
+ @staticmethod
+ def create(name, email, password):
+ id = create_user(name, email, password)
+ return User(id, name, email)
+
+ @staticmethod
+ def get(id):
+ user = get_user(id)
+ return User(user[0], user[1], user[2]) if user else None
\ No newline at end of file
diff --git a/templates/auth/login.html b/templates/auth/login.html
new file mode 100644
index 0000000..c4af8fa
--- /dev/null
+++ b/templates/auth/login.html
@@ -0,0 +1,28 @@
+{% extends 'layouts/main.html' %}
+
+{% block content %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/auth/signup.html b/templates/auth/signup.html
new file mode 100644
index 0000000..bbbf537
--- /dev/null
+++ b/templates/auth/signup.html
@@ -0,0 +1,33 @@
+{% extends 'layouts/main.html' %}
+
+{% block content %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..b0170d6
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,17 @@
+{% extends 'layouts/main.html' %}
+
+{% block content %}
+Hello World!
+{{ utc_dt }}
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/layouts/main.html b/templates/layouts/main.html
new file mode 100644
index 0000000..6667d19
--- /dev/null
+++ b/templates/layouts/main.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+ {{ title }} - HabitTracker
+
+
+
+
+
+
+
+ {% block content %} {% endblock %}
+
+
+
+
+
+
+
\ No newline at end of file