diff --git a/crewpulse/settings.py b/crewpulse/settings.py
index e2424b7..fb39cad 100644
--- a/crewpulse/settings.py
+++ b/crewpulse/settings.py
@@ -11,6 +11,10 @@ https://docs.djangoproject.com/en/5.1/ref/settings/
"""
from pathlib import Path
+from import_export.formats.base_formats import CSV
+
+IMPORT_FORMATS = [CSV]
+EXPORT_FORMATS = [CSV]
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
@@ -35,6 +39,7 @@ INSTALLED_APPS = [
#ADD_ONs
'jazzmin',
'phonenumber_field',
+ 'import_export',
#Defaul
'django.contrib.admin',
@@ -143,3 +148,36 @@ JAZZMIN_SETTINGS = {
"site_logo": 'main/img/crewpulse_logo.png',
"show_ui_builder": True, # Zeigt den UI-Builder im Admin-Bereich
}
+
+JAZZMIN_UI_TWEAKS = {
+ "navbar_small_text": False,
+ "footer_small_text": False,
+ "body_small_text": True,
+ "brand_small_text": False,
+ "brand_colour": False,
+ "accent": "accent-warning",
+ "navbar": "navbar-dark",
+ "no_navbar_border": False,
+ "navbar_fixed": False,
+ "layout_boxed": False,
+ "footer_fixed": False,
+ "sidebar_fixed": False,
+ "sidebar": "sidebar-dark-olive",
+ "sidebar_nav_small_text": False,
+ "sidebar_disable_expand": False,
+ "sidebar_nav_child_indent": False,
+ "sidebar_nav_compact_style": False,
+ "sidebar_nav_legacy_style": False,
+ "sidebar_nav_flat_style": False,
+ "theme": "solar",
+ "dark_mode_theme": "solar",
+ "button_classes": {
+ "primary": "btn-primary",
+ "secondary": "btn-secondary",
+ "info": "btn-info",
+ "warning": "btn-warning",
+ "danger": "btn-danger",
+ "success": "btn-success"
+ },
+ "actions_sticky_top": False
+}
diff --git a/main/admin.py b/main/admin.py
index 03485a1..309b13b 100644
--- a/main/admin.py
+++ b/main/admin.py
@@ -1,39 +1,95 @@
+from django import forms
from django.contrib import admin
import main.models as MainDB
from datetime import date, datetime
+from django.utils.html import format_html
+from import_export import resources
+from import_export.admin import ExportMixin, ImportMixin, ImportExportModelAdmin
+
+
+class contributorsResource(resources.ModelResource):
+
+ class Meta:
+ model = MainDB.contributors
# Register your models here.
@admin.register(MainDB.contributors)
-class MainDB_contributorsAdmin(admin.ModelAdmin):
+# class DeinModellAdmin(ImportExportModelAdmin):
+# pass # Damit hast du Import/Export-Funktionalität im Django Admin
+class MainDB_contributorsAdmin(ExportMixin, ImportMixin, admin.ModelAdmin):
- list_display = ('full_name', 'da_approval', 'teamleader', 'second_teamleader', 'gender', 'birthday_year', 'get_congregation', 'pioneer', 'get_deparment', 'mobilnumber', 'email')
+ resource_class = contributorsResource
+
+ list_display = ('full_name', 'actual', 'da_approval', 'leader', 'tage_anzeige', 'gender', 'birthday_year', 'get_congregation', 'pioneer', 'get_deparment', 'mobilnumber', 'email')
list_filter = ('gender', 'roles', 'deparment', 'da_approval')
search_fields = ('name', 'firstname', 'congregation__title', 'deparment__title', 'mobilnumber', 'email')
-
- def get_congregation(self, obj):
- # Gebe eine durch Komma getrennte Liste der Namen der contributors zurück
- return ", ".join([str(congregation) for congregation in obj.congregation.all()])
- get_congregation.short_description = 'Versammlung' # Optional: Benennung der Spalte
+ def get_congregation(self, obj):
+ return obj.congregation
+ get_congregation.short_description = 'Versammlung'
+ get_congregation.admin_order_field = 'congregation__title'
+
def get_deparment(self, obj):
# Gebe eine durch Komma getrennte Liste der Namen der contributors zurück
return ", ".join([str(deparment) for deparment in obj.deparment.all()])
get_deparment.short_description = 'Abteilung' # Optional: Benennung der Spalte
- # Methode, um den vollständigen Namen zu kombinieren
- def full_name(self, obj):
- return f"{obj.name}, {obj.firstname}"
- full_name.short_description = 'Name' # Spaltenüberschrift im Admin ändern
-
# Methode, um nur das Jahr des Birthdays anzuzeigen
def birthday_year(self, obj):
- year = obj.birthday.year if obj.birthday else None
+ year = obj.birthday.year if obj.birthday != None else 0
return int(datetime.strftime(date.today(), "%Y")) - year
birthday_year.short_description = 'Alter'
+ # Teamleiter ansicht
+ def leader(self, obj):
+ if obj.teamleader:
+ color = "green"
+ elif not obj.teamleader and obj.second_teamleader:
+ color = "gold"
+ else:
+ color = "gray"
+
+ status = f'●'
+ return format_html(status)
+
+ # Verfügbarkeit anzeigen
+ def tage_anzeige(self, obj):
+ """ Kompakte Anzeige mit 2er-Gruppen in farbigen Punkten """
+ tage_pairs = [
+ ("MiV", "MiN"),
+ ("DoV", "DoN"),
+ ("FrV", "FrN"),
+ ("SaV", "SaN"),
+ ("SoV", "SoN"),
+ ("Abbau", None) # Extra2 hat keinen Partner, wird einzeln bewertet
+ ]
+
+ symbols = []
+ for tag1, tag2 in tage_pairs:
+ active1 = obj.tage & obj.TAGE[tag1]
+ active2 = obj.TAGE.get(tag2, 0) and obj.tage & obj.TAGE[tag2] if tag2 else None # Sicherstellen, dass None nicht bewertet wird
+
+ if tag2 is None: # Falls nur ein Eintrag in der Gruppe existiert
+ color = "green" if active1 else "red" # Entweder Grün oder Rot
+ else:
+ if active1 and active2:
+ color = "green" # 🟢 beide aktiv
+ elif active1 or active2:
+ color = "gold" # 🟡 nur einer aktiv
+ else:
+ color = "red" # 🔴 beide inaktiv
+
+ symbols.append(f'●')
+
+ return format_html(" ".join(symbols))
+
admin.site.register(MainDB.department)
-admin.site.register(MainDB.congregation)
+
+@admin.register(MainDB.congregation)
+class MainDB_congregationAdmin(admin.ModelAdmin):
+ list_display = ('id', 'title')
+
admin.site.register(MainDB.role)
\ No newline at end of file
diff --git a/main/models.py b/main/models.py
index d58d962..395f47b 100644
--- a/main/models.py
+++ b/main/models.py
@@ -1,6 +1,6 @@
from django.db import models
from phonenumber_field.modelfields import PhoneNumberField
-
+from django.contrib import admin
# Create your models here.
class department(models.Model):
@@ -48,41 +48,57 @@ class role(models.Model):
def get_absolute_url(self):
return reverse("role_detail", kwargs={"pk": self.pk})
-
class contributors(models.Model):
- DAY_OPTIONS = [
- ('MI', 'Mittwoch'),
- ('DO', 'Donnerstag'),
- ('FRV', 'Freitag vormittag'),
- ('FRN', 'Freitag nachmittag'),
- ('SAV', 'Samstag vormittag'),
- ('SAN', 'Samstag nachmittag'),
- ('SOV', 'Sonntag vormittag'),
- ('SON', 'Sonntag nachmittag'),
- ('SOA', 'Sonntag abbau'),
- ('MO', 'Montag'),
- ]
+ TAGE = {
+ "MiV": 1,
+ "MiN": 2,
+ "DoV": 4,
+ "DoN": 8,
+ "FrV": 16,
+ "FrN": 32,
+ "SaV": 64,
+ "SaN": 128,
+ "SoV": 256,
+ "SoN": 512,
+ "Abbau": 1024,
+ }
name = models.CharField(("name"), max_length=50)
firstname = models.CharField(("vorname"), max_length=50)
- birthday = models.DateField(("geburtstag"), auto_now=False, auto_now_add=False)
- congregation = models.ManyToManyField("main.congregation", verbose_name=("congregation"), null=True)
- roles = models.ManyToManyField("main.role", verbose_name=("role"), null=True)
- pioneer = models.BooleanField(("pionier"))
+ birthday = models.DateField(("geburtstag"), auto_now=False, auto_now_add=False, blank=True, null=True)
+ congregation = models.ForeignKey("main.congregation", verbose_name=("Versammlung"), on_delete=models.SET_NULL, null=True, blank=True)
+ roles = models.ManyToManyField("main.role", verbose_name=("role"), null=True, blank=True)
+ pioneer = models.BooleanField(("pionier"), default=False)
gender = models.CharField("Geschlecht",max_length=20, choices=[("M", "Männlich"), ("W", "Weiblich")])
da_approval = models.BooleanField(("Freigabe"), default=False)
- deparment = models.ManyToManyField("main.department", verbose_name=("department"), null=True, blank=True)
- availability = models.CharField(("Verfügbarkeit"), max_length=50, choices=DAY_OPTIONS, blank=True)
+ deparment = models.ManyToManyField("main.department", verbose_name=("Abteilung"), null=True, blank=True)
+ tage = models.IntegerField(default=0) # Hier speichern wir die Bitmaske
teamleader = models.BooleanField(("Leiter"), default=False)
- second_teamleader = models.BooleanField(("V-Leiter"), default=False)
+ second_teamleader = models.BooleanField(("Gruppenleiter"), default=False)
mobilnumber = PhoneNumberField(("Mobilnummer"), null=True, blank=True)
email = models.EmailField(("E-Mail"), max_length=254, null=True, blank=True)
+ jwpub = models.EmailField(("JWPUB"), max_length=254, null=True, blank=True)
+ notes = models.CharField(("Bemerkung"), max_length=255, null=True, blank=True)
+ actual = models.BooleanField(("Aktuell"), default=False)
class Meta:
verbose_name = ("Helfer")
verbose_name_plural = ("Helfer")
+ @property
+ @admin.display(
+ ordering="name",
+ description="Name, Vorname",
+ boolean=False,
+ )
+ def full_name(self):
+ return self.name + ", " + self.firstname
+
+ def get_tage_list(self):
+ """ Gibt eine Liste der aktiven Tage zurück """
+ return [name for name, bit in self.TAGE.items() if self.tage & bit]
+
def __str__(self):
return f'{self.name}, {self.firstname}'