Compare commits
16 Commits
85a63f6c30
...
a47ffa0b0b
| Author | SHA1 | Date | |
|---|---|---|---|
| a47ffa0b0b | |||
| 39ceb520ba | |||
| 8e34e1056a | |||
| e73d020155 | |||
| b3c2bad3c2 | |||
| 51ba0fb693 | |||
| 837884bb02 | |||
| f6887d07df | |||
| 7015a79e07 | |||
| e28ceb2da1 | |||
| a1c9a6a3cc | |||
| 3af6652e68 | |||
| 50324f190b | |||
| 4fda1dd1df | |||
| cf433c60f6 | |||
| a6ff6e1eca |
138
.gitignore
vendored
Normal file
138
.gitignore
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
# Django #
|
||||
*.log
|
||||
*.pot
|
||||
*.pyc
|
||||
__pycache__
|
||||
db.sqlite3
|
||||
media
|
||||
|
||||
# Backup files #
|
||||
*.bak
|
||||
|
||||
# If you are using PyCharm #
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
|
||||
# Generated files
|
||||
.idea/**/contentModel.xml
|
||||
|
||||
# Sensitive or high-churn files
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
.idea/**/dbnavigator.xml
|
||||
|
||||
# Gradle
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# File-based project format
|
||||
*.iws
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Python #
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# Distribution / packaging
|
||||
.Python build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.whl
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
.pytest_cache/
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery
|
||||
celerybeat-schedule.*
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
# Sublime Text #
|
||||
*.tmlanguage.cache
|
||||
*.tmPreferences.cache
|
||||
*.stTheme.cache
|
||||
*.sublime-workspace
|
||||
*.sublime-project
|
||||
|
||||
# sftp configuration file
|
||||
sftp-config.json
|
||||
|
||||
# Package control specific files Package
|
||||
Control.last-run
|
||||
Control.ca-list
|
||||
Control.ca-bundle
|
||||
Control.system-ca-bundle
|
||||
GitHub.sublime-settings
|
||||
|
||||
# Visual Studio Code #
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history
|
||||
@ -9,8 +9,10 @@ https://docs.djangoproject.com/en/5.2/topics/settings/
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/5.2/ref/settings/
|
||||
"""
|
||||
|
||||
import pymysql
|
||||
pymysql.install_as_MySQLdb()
|
||||
from pathlib import Path
|
||||
from decouple import config
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
@ -25,7 +27,7 @@ SECRET_KEY = 'django-insecure-tulz*odc=l1x(g^-=ne!y7^@lg1uce)=ha^!0wi5qkifq&#^sg
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS = [ "albani.sternbauer.de" ]
|
||||
|
||||
|
||||
# Application definition
|
||||
@ -37,6 +39,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'member',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
@ -74,8 +77,12 @@ WSGI_APPLICATION = 'FloriCore.wsgi.application'
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': config('DB_NAME'),
|
||||
'USER': config('DB_USER'),
|
||||
'PASSWORD': config('DB_PASSWORD'),
|
||||
'HOST': config('DB_HOST', default='localhost'),
|
||||
'PORT': config('DB_PORT', default='3306'),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,8 +15,9 @@ Including another URLconf
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.urls import path,include
|
||||
|
||||
urlpatterns = [
|
||||
path('', include('member.urls')),
|
||||
path('admin/', admin.site.urls),
|
||||
]
|
||||
|
||||
0
member/__init__.py
Normal file
0
member/__init__.py
Normal file
3
member/admin.py
Normal file
3
member/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
member/apps.py
Normal file
6
member/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MemberConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'member'
|
||||
0
member/management/__init__.py
Normal file
0
member/management/__init__.py
Normal file
0
member/management/commands/__init__.py
Normal file
0
member/management/commands/__init__.py
Normal file
49
member/management/commands/seed.py
Normal file
49
member/management/commands/seed.py
Normal file
@ -0,0 +1,49 @@
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
from member.models import Person, UserAccount
|
||||
from django.contrib.auth.hashers import make_password
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "Füllt die DB mit Beispieldaten"
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
if not settings.DEBUG:
|
||||
raise CommandError("❌ Dieses Kommando darf nur in DEBUG-Umgebungen verwendet werden.")
|
||||
# Bestehende Daten löschen
|
||||
self.stdout.write("🧹 Lösche bestehende Personen und Benutzerkonten...")
|
||||
UserAccount.objects.all().delete()
|
||||
Person.objects.all().delete()
|
||||
|
||||
# Personen anlegen
|
||||
self.stdout.write("👤 Erstelle Personen...")
|
||||
p0 = Person.objects.create(vorname="Florian", nachname="von Lorch", geburtsdatum="0304-05-04", aktiv=True)
|
||||
p1 = Person.objects.create(vorname="Max", nachname="Mustermann", geburtsdatum="1980-05-01", aktiv=True)
|
||||
p2 = Person.objects.create(vorname="Erika", nachname="Beispiel", geburtsdatum="1992-03-15", aktiv=True)
|
||||
p3 = Person.objects.create(vorname="Thomas", nachname="Feuer", geburtsdatum=None, aktiv=True)
|
||||
p4 = Person.objects.create(vorname="Julia", nachname="Wehr", geburtsdatum="1999-11-20", aktiv=False)
|
||||
|
||||
# Benutzerkonten anlegen
|
||||
self.stdout.write("🔐 Erstelle Benutzerkonten...")
|
||||
UserAccount.objects.create(
|
||||
person=p0,
|
||||
userName="admin",
|
||||
passwort_hash=make_password("adminpass1234"),
|
||||
role="superadmin",
|
||||
isActive=True,
|
||||
)
|
||||
UserAccount.objects.create(
|
||||
person=p1,
|
||||
userName="maxadmin",
|
||||
passwort_hash=make_password("adminpass123"),
|
||||
role="admin",
|
||||
isActive=True,
|
||||
)
|
||||
UserAccount.objects.create(
|
||||
person=p2,
|
||||
userName="erikam",
|
||||
passwort_hash=make_password("mitglied456"),
|
||||
role="mitglied",
|
||||
isActive=True,
|
||||
)
|
||||
|
||||
self.stdout.write(self.style.SUCCESS("✅ Testdaten erfolgreich eingespielt."))
|
||||
37
member/migrations/0001_initial.py
Normal file
37
member/migrations/0001_initial.py
Normal file
@ -0,0 +1,37 @@
|
||||
# Generated by Django 5.2.1 on 2025-05-10 12:49
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Person',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('vorname', models.CharField(max_length=100)),
|
||||
('nachname', models.CharField(max_length=100)),
|
||||
('geburtsdatum', models.DateField(blank=True, null=True)),
|
||||
('aktiv', models.BooleanField(default=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserAccount',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('userName', models.CharField(max_length=150, unique=True)),
|
||||
('passwort_hash', models.CharField(max_length=128)),
|
||||
('role', models.CharField(choices=[('mitglied', 'Mitglied'), ('geraetewart', 'Gerätewart'), ('kommandant', 'Kommandant'), ('admin', 'Administrator')], max_length=50)),
|
||||
('isActive', models.BooleanField(default=True)),
|
||||
('lastLogin', models.DateTimeField(blank=True, null=True)),
|
||||
('person', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='benutzerkonten', to='member.person')),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
member/migrations/__init__.py
Normal file
0
member/migrations/__init__.py
Normal file
11
member/models/Person.py
Normal file
11
member/models/Person.py
Normal file
@ -0,0 +1,11 @@
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
class Person(models.Model):
|
||||
vorname = models.CharField(max_length=100)
|
||||
nachname = models.CharField(max_length=100)
|
||||
geburtsdatum = models.DateField(null=True, blank=True)
|
||||
aktiv = models.BooleanField(default=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.vorname} {self.nachname}"
|
||||
20
member/models/UserAccount.py
Normal file
20
member/models/UserAccount.py
Normal file
@ -0,0 +1,20 @@
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from .Person import Person
|
||||
|
||||
|
||||
class UserAccount(models.Model):
|
||||
person = models.ForeignKey(Person, on_delete=models.CASCADE, related_name='benutzerkonten')
|
||||
userName = models.CharField(max_length=150, unique=True)
|
||||
passwort_hash = models.CharField(max_length=128)
|
||||
role = models.CharField(max_length=50, choices=[
|
||||
('mitglied', 'Mitglied'),
|
||||
('geraetewart', 'Gerätewart'),
|
||||
('kommandant', 'Kommandant'),
|
||||
('admin', 'Administrator'),
|
||||
])
|
||||
isActive = models.BooleanField(default=True)
|
||||
lastLogin = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.benutzername} ({self.rolle})"
|
||||
2
member/models/__init__.py
Normal file
2
member/models/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .Person import Person
|
||||
from .UserAccount import UserAccount
|
||||
131
member/static/member/css/styles.css
Normal file
131
member/static/member/css/styles.css
Normal file
@ -0,0 +1,131 @@
|
||||
:root {
|
||||
--bg-dark: #121212;
|
||||
--bg-sidebar: #2a0000;
|
||||
--bg-panel: #1e1e1e;
|
||||
--bg-accent: #b22222;
|
||||
--text-light: #f1f1f1;
|
||||
--text-muted: #999;
|
||||
--highlight: #f5a623;
|
||||
--font-sans: 'Inter', 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: var(--font-sans);
|
||||
background-color: var(--bg-dark);
|
||||
color: var(--text-light);
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 220px;
|
||||
height: 100vh;
|
||||
background-color: var(--bg-sidebar);
|
||||
color: var(--text-light);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.sidebar h2 {
|
||||
font-size: 1.2rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.sidebar nav a {
|
||||
color: var(--text-light);
|
||||
text-decoration: none;
|
||||
margin: 10px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar nav a:hover,
|
||||
.sidebar nav a.active {
|
||||
background-color: var(--bg-accent);
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--bg-dark);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.members-table {
|
||||
flex: 2;
|
||||
margin-right: 20px;
|
||||
background-color: var(--bg-panel);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.members-table table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.members-table th,
|
||||
.members-table td {
|
||||
text-align: left;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.members-table th {
|
||||
color: var(--highlight);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.members-table tr:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.details-panel {
|
||||
flex: 1;
|
||||
background-color: var(--bg-panel);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.details-panel h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.details-panel .info {
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
background-color: var(--bg-accent);
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
color: #fff;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: var(--bg-accent);
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
font-size: 0.9rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #d63031;
|
||||
}
|
||||
|
||||
13
member/templates/details.html
Normal file
13
member/templates/details.html
Normal file
@ -0,0 +1,13 @@
|
||||
{% extends "master.html" %}
|
||||
|
||||
{% block title %}
|
||||
Details zu {{ mymember.firstname }} {{ mymember.lastname }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{{ mymember.vorname }} {{ mymember.nachname }}</h1>
|
||||
|
||||
<p>Geburtsdatum: {{ mymember.geburtsdatum }}</p>
|
||||
|
||||
<p>Back to <a href="/members">Members</a></p>
|
||||
{% endblock %}
|
||||
14
member/templates/master.html
Normal file
14
member/templates/master.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
<link rel="stylesheet" href="{% static 'member/css/styles.css' %}">
|
||||
</head>
|
||||
<body>
|
||||
{% include "partials/sidebar.html" %}
|
||||
<div class="main">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
19
member/templates/memberlist.html
Normal file
19
member/templates/memberlist.html
Normal file
@ -0,0 +1,19 @@
|
||||
{% extends "master.html" %}
|
||||
|
||||
{% block title %}
|
||||
List of all persons
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="members-table">
|
||||
<h3>Members</h3>
|
||||
<ul>
|
||||
{% for x in mymembers %}
|
||||
<li><a href="details/{{x.id}}"> {{ x.vorname }} {{ x.nachname }}</a></li>
|
||||
{% empty %}
|
||||
<li>Keine Datensaetze gefunden</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
7
member/templates/partials/sidebar.html
Normal file
7
member/templates/partials/sidebar.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div class="sidebar">
|
||||
<h2>🧯 VERWALTUNG</h2>
|
||||
|
||||
<nav>
|
||||
<a href={% url 'members' %} class="{% if request.path == '/members/' %}active{% endif %}">Mitglieder</a>
|
||||
</nav>
|
||||
</div>
|
||||
3
member/tests.py
Normal file
3
member/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
7
member/urls.py
Normal file
7
member/urls.py
Normal file
@ -0,0 +1,7 @@
|
||||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path('members/', views.members, name='members'),
|
||||
path('members/details/<int:id>', views.details, name="details"),
|
||||
]
|
||||
19
member/views.py
Normal file
19
member/views.py
Normal file
@ -0,0 +1,19 @@
|
||||
from django.http import HttpResponse
|
||||
from django.template import loader
|
||||
from .models import Person
|
||||
|
||||
def members(request):
|
||||
mymembers=Person.objects.all().values()
|
||||
template=loader.get_template("memberlist.html")
|
||||
context = {
|
||||
'mymembers': mymembers
|
||||
}
|
||||
return HttpResponse(template.render(context, request))
|
||||
|
||||
def details(request, id):
|
||||
mymember = Person.objects.get(id=id)
|
||||
template = loader.get_template("details.html")
|
||||
context = {
|
||||
'mymember': mymember
|
||||
}
|
||||
return HttpResponse(template.render(context, request))
|
||||
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@ -0,0 +1,6 @@
|
||||
asgiref==3.8.1
|
||||
Django==5.2.1
|
||||
mysql-connector-python==9.3.0
|
||||
PyMySQL==1.1.1
|
||||
python-decouple==3.8
|
||||
sqlparse==0.5.3
|
||||
Loading…
x
Reference in New Issue
Block a user