This commit is contained in:
2020-10-26 06:16:12 +00:00
18 changed files with 275 additions and 192 deletions

View File

@@ -3,7 +3,12 @@ server {
location /assets {
alias /app/assets;
try_files $uri $uri/ =404;
try_files $uri =404;
}
location /files {
alias /app/files;
try_files $uri =404;
}
location /fotos {

View File

@@ -1,21 +1,14 @@
from django.contrib import admin
from .forms import JobPostingForm
from .models import JobPosting
class JobPostingAdmin(admin.ModelAdmin):
form = JobPostingForm
model = JobPosting
fieldsets = (
(None, {
'fields': (
'companyName',
'jobName',
'salary',
'pdfLocation',
'publishDate',
)
}),
)
list_display = ['companyName', 'jobName', 'salary', 'publishDate']
admin.site.register(JobPosting, JobPostingAdmin)

View File

@@ -0,0 +1,25 @@
from django import forms
from django.utils.translation import gettext_lazy as _
from .models import JobPosting
class JobPostingForm(forms.ModelForm):
class Meta:
model = JobPosting
fields = ['companyName', 'jobName', 'salary', 'pdfLocation', 'publishDate']
labels = {
'companyName': _("Firmenname"),
'jobName': _("Berufsbezeichnung"),
'salary': _("Gehalt"),
'pdfLocation': _("Stellenausschreibung"),
'publishDate': _("Veröffentlichung"),
}
help_texts = {
'pdfLocation': _(
"Verwendbare Formate: PDF"
),
'salary': _("in Euro angeben"),
}

View File

@@ -15,12 +15,12 @@ logger = logging.getLogger('blackboard')
class JobPosting(models.Model):
companyName = models.CharField(max_length=128)
jobName = models.CharField(max_length=128)
salary = models.PositiveSmallIntegerField()
pdfLocation = models.FileField(upload_to='uploads/blackboard/pdf/')
companyName = models.CharField(verbose_name="Firmenname", max_length=128)
jobName = models.CharField(verbose_name="Berufsbezeichnung", max_length=128)
salary = models.PositiveSmallIntegerField(verbose_name="Gehalt", )
pdfLocation = models.FileField(verbose_name="Stellenausschreibung", upload_to='uploads/blackboard/pdf/')
pdf_thumb_location = models.CharField(max_length=128)
publishDate = models.DateField('date published', default=timezone.now)
publishDate = models.DateField(verbose_name="Veröffentlichung", default=timezone.now)
# Managers
all_jobPosting = models.Manager()

View File

@@ -227,7 +227,7 @@ THUMBNAIL_ALIASES = {
},
}
# ETHERPAD CLIENT
if DEBUG:
ETHERPAD_CLIENT = {
'url': "http://etherpad:"+env('ETHERPAD_PORT'),
@@ -241,20 +241,7 @@ else:
'apikey': "/app/etherpad/APIKEY.txt"
}
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
]
}
# DJANGO MAIL
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'buran.htu.tuwien.ac.at'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
# REST FRAMEWORK
REST_FRAMEWORK={
'DEFAULT_PERMISSION_CLASSES_CLASSES':[
'rest_framework.permissions.AllowAny',
@@ -262,6 +249,12 @@ REST_FRAMEWORK={
'DEFAULT_AUTHENTICATION_CLASSES':()
}
# DJANGO MAIL
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'buran.htu.tuwien.ac.at'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
# CRON JOBS
CRONJOBS = [
('0 16 * * *', 'posts.cronjobs.check_to_send_agenda_mail'),

View File

@@ -1,4 +1,6 @@
from django import forms
from django.utils.translation import gettext_lazy as _
from ckeditor_uploader.widgets import CKEditorUploadingWidget
from .models import Member, Job, JobGroup
@@ -8,45 +10,56 @@ class MemberForm(forms.ModelForm):
class Meta:
model = Member
fields = [
'firstname',
'surname',
'nickname',
'mailaccount',
'role',
'description',
'image',
'birthday',
'phone',
'address'
'firstname', 'surname', 'nickname', 'mailaccount', 'role', 'description', 'image',
'birthday', 'phone', 'address',
]
widgets = {
'description': CKEditorUploadingWidget(config_name='default')
}
labels = {
'description': _("Beschreibung zu der Person"),
'image': _("Porträt"),
'birthday': _("Geburtstag"),
'phone': _("Telefonnummer"),
'address': _("Wohnadresse"),
}
help_texts = {
'image': _(
"Mindestgröße: 150*150 px, Verwendbare Formate: ..."
),
'mailaccount': _(
"Die Mailadresse mit '@fet.at' angeben."
),
}
class JobForm(forms.ModelForm):
class Meta:
model = Job
fields = [
'name',
'shortterm',
'slug',
'job_group',
]
fields = ['name', 'shortterm', 'slug', 'job_group',]
labels = {
'shortterm': _("Kürzel der Tätigkeit"),
'job_group': _("Tätigkeitsbereich"),
}
class JobGroupForm(forms.ModelForm):
class Meta:
model = JobGroup
fields = [
'name',
'shortterm',
'slug',
'description',
'is_pinned',
]
fields = ['name', 'shortterm', 'slug', 'description', 'is_pinned',]
widgets = {
'description': CKEditorUploadingWidget(config_name='default')
}
labels = {
'shortterm': _("Kürzel des Tätigkeitsbereichs"),
'description': _("Beschreibung des Tätigkeitsbereichs"),
'is_pinned': _(
"Dieser Tätigkeitsbereich soll im Fachschaftsbereich angeheftet werden, damit es sofort ersichtlich ist."
),
}

View File

@@ -77,16 +77,21 @@ class JobGroupManager(models.Manager):
class Member(models.Model):
firstname = models.CharField(max_length=128)
surname = models.CharField(max_length=128)
nickname = models.CharField(max_length=128)
mailaccount = models.CharField(max_length=128)
firstname = models.CharField("Vorname", max_length=128)
surname = models.CharField("Nachname", max_length=128)
nickname = models.CharField("Spitzname", max_length=128)
mailaccount = models.CharField("Mailadresse", max_length=128)
class MemberRole(models.TextChoices):
ACTIVE = 'A', _('Active')
PENSION = 'P', _('Pension')
role = models.CharField(max_length=1, choices=MemberRole.choices, default=MemberRole.ACTIVE)
role = models.CharField(
"Rolle",
max_length=1,
choices=MemberRole.choices,
default=MemberRole.ACTIVE,
)
description = models.TextField(null=True, blank=True)
image = ThumbnailerImageField(upload_to='uploads/members/image/')
@@ -132,14 +137,14 @@ class JobGroup(models.Model):
description = models.TextField(null=True, blank=True)
is_pinned = models.BooleanField(default=False)
is_pinned = models.BooleanField(verbose_name="ANGEHEFTET", default=False)
# Managers
all_jobgroups = JobGroupManager()
class Meta:
verbose_name = "Tätigkeit-Gruppierung"
verbose_name_plural = "Tätigkeit-Gruppierungen"
verbose_name = "Tätigkeitsbereich"
verbose_name_plural = "Tätigkeitsbereiche"
def __str__(self):
return self.name

View File

@@ -2,7 +2,7 @@ from django.contrib import admin, auth, messages
from django.utils.translation import gettext_lazy as _
from .models import Post, Event, News, FetMeeting
from .forms import MyPostForm, MyEventForm, MyNewsForm, MyFetMeetingForm
from .forms import PostForm, EventForm, NewsForm, FetMeetingForm
from documents.api import createPadifNotExists
import taggit.admin
@@ -57,8 +57,8 @@ def make_fetmeeting(self, request, queryset):
make_fetmeeting.short_description = "In eine Fachschaftssitzung konvertieren"
class MyPostAdmin(admin.ModelAdmin):
form = MyPostForm
class PostAdmin(admin.ModelAdmin):
form = PostForm
model = Post
list_filter = ['is_pinned', 'is_hidden']
list_display = ['title', 'slug', 'public_date', 'is_pinned', 'is_hidden']
@@ -95,8 +95,8 @@ class MyPostAdmin(admin.ModelAdmin):
]
class MyEventAdmin(MyPostAdmin):
form = MyEventForm
class EventAdmin(PostAdmin):
form = EventForm
model = Event
list_filter = ['is_pinned']
list_display = ['title', 'slug', 'event_start', 'public_date', 'is_pinned']
@@ -104,23 +104,19 @@ class MyEventAdmin(MyPostAdmin):
actions = [make_fetmeeting]
admin.site.register(Event, MyEventAdmin)
class MyNewsAdmin(MyPostAdmin):
form = MyNewsForm
class NewsAdmin(PostAdmin):
form = NewsForm
model = News
admin.site.register(News, MyNewsAdmin)
class MyFetMeetingAdmin(MyEventAdmin):
form = MyFetMeetingForm
class FetMeetingAdmin(EventAdmin):
form = FetMeetingForm
model = FetMeeting
list_filter = []
list_display = ['title', 'slug', 'event_start', 'public_date']
actions = []
admin.site.register(FetMeeting, MyFetMeetingAdmin)
admin.site.register(Event, EventAdmin)
admin.site.register(News, NewsAdmin)
admin.site.register(FetMeeting, FetMeetingAdmin)

View File

@@ -7,7 +7,7 @@ from taggit.models import Tag
from .models import Post, Event, News, FetMeeting
class MyPostForm(forms.ModelForm):
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'subtitle', 'tags', 'image', 'body', 'slug', 'author', 'public_date']
@@ -21,28 +21,37 @@ class MyPostForm(forms.ModelForm):
)
class MyNewsForm(MyPostForm):
class NewsForm(PostForm):
class Meta:
model = News
fields = [
'title', 'subtitle', 'tags', 'image', 'body', 'slug', 'author', 'public_date',
'is_pinned', 'is_hidden',
]
labels = {
'title': _('Titel des Posts'),
'image': _('Hintergrundbild des Posts'),
'body': _('Beschreibung des Posts'),
'author': _('Autor'),
'public_date': _('Veröffentlichung'),
'is_pinned': _('Post anheften'),
'is_hidden': _('Post verstecken'),
'title': _("Titel"),
'subtitle': _("Untertitel"),
'image': _("Hintergrundbild"),
'body': _("Text"),
'author': _("Autor"),
'public_date': _("Veröffentlichung"),
'is_pinned': _("Post anheften"),
'is_hidden': _("Post verstecken"),
}
help_texts = {
'tags': _(
"Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen."
),
'image': _(
"Verwendbare Formate: ..."
),
'is_pinned': _(
'Dieser Post soll an die Startseite als erster Post angeheftet werden.'
"Dieser Post soll an die Startseite als erster Post angeheftet werden."
),
'is_hidden': _(
'Dieser Post soll im News Feed nicht auftauchen, z.B. Impressum.'
"Dieser Post soll im News Feed nicht auftauchen, z.B. Impressum."
),
}
@@ -52,27 +61,36 @@ class MyNewsForm(MyPostForm):
super().__init__(*args, **kwargs) # to get the self.fields set
class MyEventForm(MyPostForm):
class EventForm(PostForm):
class Meta:
model = Event
fields = [
'title', 'subtitle', 'tags', 'image', 'body', 'event_start', 'event_end',
'event_place', 'slug', 'author', 'public_date', 'is_pinned',
]
labels = {
'title': _('Titel des Events'),
'image': _('Hintergrundbild des Posts'),
'body': _('Beschreibung des Events'),
'event_start': _('Start des Events'),
'event_end': _('Ende des Events'),
'event_place': _('Ort des Events'),
'author': _('Autor'),
'public_date': _('Veröffentlichung'),
'is_pinned': _('Event anheften'),
'title': _("Titel"),
'subtitle': _("Untertitel"),
'image': _("Hintergrundbild"),
'body': _("Text"),
'event_start': _("Start des Events"),
'event_end': _("Ende des Events"),
'event_place': _("Ort des Events"),
'author': _("Autor"),
'public_date': _("Veröffentlichung"),
'is_pinned': _("Event anheften"),
}
help_texts = {
'tags': _(
"Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen."
),
'image': _(
"Verwendbare Formate: "
),
'is_pinned': _(
'Dieses Event soll an die Startseite als erster Post angeheftet werden.'
"Dieses Event soll an die Startseite als erster Post angeheftet werden."
),
}
@@ -88,20 +106,25 @@ class MyEventForm(MyPostForm):
self.fields['event_place'].required = True
class MyFetMeetingForm(MyEventForm):
class FetMeetingForm(EventForm):
# agenda_html = forms.CharField(widget = forms.TextInput())
class Meta:
model = FetMeeting
fields = ['event_start', 'event_end', 'tags', 'has_agenda', 'has_protocol']
labels = {
'event_start': _('Start der Sitzung'),
'event_end': _('Ende der Sitzung'),
'has_agenda': _('Agenda'),
'has_protocol': _('Protokoll'),
'event_start': _("Start der Sitzung"),
'event_end': _("Ende der Sitzung"),
'has_agenda': _("Agenda"),
'has_protocol': _("Protokoll"),
}
help_texts = {
'has_agenda': _('Agenda zur Sitzung hinzufügen.'),
'has_protocol': _('Protokoll zur Sitzung hinzufügen.'),
'tags': _(
"Die Hashtags ohne '#' eintragen, und mit Komma kann man mehrere Tags anfügen."
),
'has_agenda': _("Agenda zur Sitzung hinzufügen."),
'has_protocol': _("Protokoll zur Sitzung hinzufügen."),
}
def __init__(self, *args, **kwargs):

View File

@@ -43,7 +43,7 @@ class Post(models.Model):
legacy_id = models.IntegerField(null=True)
legacy_rubrik_id = models.IntegerField(null=True)
# Titel des Posts
title = models.CharField(max_length=200)
title = models.CharField(verbose_name="Titel", max_length=200)
subtitle = models.CharField(max_length=500, null=True, blank=True)
# Slug = Text Basierter url bestandteil zb Fetsitzung 22.1.2020 --> fetsitzung_22_1_2020 für Url
@@ -57,7 +57,7 @@ class Post(models.Model):
tags = TaggableManager(blank=True)
# Datum ab dem etwas öffentlich sein soll
public_date = models.DateField('date published', null=True, blank=True, default=timezone.now)
public_date = models.DateField(verbose_name="Veröffentlichung", null=True, blank=True, default=timezone.now)
imported_from = models.CharField(max_length=200, null=True, blank=True)
@@ -75,8 +75,8 @@ class Post(models.Model):
is_hidden = models.BooleanField(verbose_name="UNSICHTBAR", default=False)
# Zusatz Info wenn ein Event gepostet wird
event_start = models.DateTimeField('Event Start', null=True, blank=True)
event_end = models.DateTimeField('Event Ende', null=True, blank=True)
event_start = models.DateTimeField(verbose_name="Event Start", null=True, blank=True)
event_end = models.DateTimeField(verbose_name="Event Ende", null=True, blank=True)
event_place = models.CharField(max_length=200, null=True, blank=True)
# Dokumente v.a. fuer Sitzungen

View File

@@ -21,7 +21,8 @@ class PostSerializer(serializers.HyperlinkedModelSerializer):
'event_start',
'event_end',
'is_hidden',
'agenda_html'
'agenda_html',
'has_agenda',
# 'author',
]
extra_kwargs={

View File

@@ -38,16 +38,7 @@ def tags(request, tag=""):
featured_post = Post.objects.get_visible_articles().filter(slug=tag).first()
members = []
job_names, slug_list = JobMember.jobs.get_job_names(slug=tag)
if job_names:
active_members = JobMember.active_member.get_members_of_job(job_names=job_names)
for idx, item in enumerate(job_names):
if active_members[idx]:
members.append(
(job_names[idx], active_members[idx], tag + "#" + slug_list[idx])
)
job_members = JobMember.active_member.get_all_by_slug(slug=tag)
author_image = None
if featured_post:
@@ -60,7 +51,7 @@ def tags(request, tag=""):
"posts": posts,
"author_image": author_image,
"featured_post": featured_post,
"members": members,
"job_members": job_members,
"tags_list": None,
}

View File

@@ -1,35 +1,29 @@
from django.contrib import admin
from .forms import TaskListForm
from .forms import TaskAdminForm, TaskListForm
from .models import Task, TaskList
class TaskAdmin(admin.ModelAdmin):
form = TaskAdminForm
model = Task
fieldsets = (
(None, {
'fields': (
'title',
'task_list',
'assigned_to',
'due_date',
'completed',
'completed_date',
'assigned_to',
'note',
'priority',
)
}),
)
list_display = (
'title',
'task_list',
'completed',
'priority',
'due_date',
'assigned_to',
)
list_display = ['title', 'task_list', 'assigned_to', 'due_date', 'completed', 'priority']
list_filter = ('task_list', )
ordering = ('priority', )
search_fields = ('title', )
def save_model(self, request, obj, form, change):

View File

@@ -10,6 +10,27 @@ class DateInput(forms.DateInput):
input_type = 'date'
class TaskAdminForm(forms.ModelForm):
class Meta:
model = Task
fields = '__all__'
labels = {
'title': _('Titel'),
'task_list': _('Aufgabenbereich'),
'due_date': _('Fälligkeit'),
'completed': _('Abgeschlossen'),
'completed_date': _('Datum der Fertigstellung'),
'assigned_to': _('Zuweisen an'),
'note': _('Notizen'),
'priority': _('Priorität'),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) # to get the self.fields set
self.fields['assigned_to'].empty_label = "Alle"
class TaskForm(forms.ModelForm):
class Meta:
model = Task
@@ -40,4 +61,13 @@ class TaskForm(forms.ModelForm):
class TaskListForm(forms.ModelForm):
users = forms.ModelMultipleChoiceField(queryset=User.objects.all(), widget=FilteredSelectMultiple("User", is_stacked=False))
users = forms.ModelMultipleChoiceField(
label="Benutzer",
help_text="Es können nur die Benutzer ausgewählt werden, die sich auf der Homepage angemeldet haben.",
queryset=User.objects.all(),
widget=FilteredSelectMultiple("User", is_stacked=False)
)
class Meta:
model = TaskList
fields = '__all__'

View File

@@ -13,18 +13,19 @@ class TaskQuerySet(models.QuerySet):
class TaskManager(models.Manager):
def get_tasks(self, user, completed, task_list, all_tasks):
# None ... assigned to all users
qs_comp = self.get_queryset().get_ordered().filter(assigned_to=user)
qs = self.get_queryset().get_ordered().filter(assigned_to__id=user)
if all_tasks:
qs_comp |= self.get_queryset().get_ordered().filter(Q(assigned_to=None) & Q(task_list__users=user))
qs_tmp = self.get_queryset().get_ordered().filter(Q(assigned_to=None) & Q(task_list__users__id__exact=user))
qs = (qs | qs_tmp).distinct()
if not completed:
qs_comp = qs_comp.filter(completed=completed)
qs = qs.filter(completed=completed)
if task_list:
qs_comp = qs_comp.filter(task_list=task_list)
qs = qs.filter(task_list=task_list)
return qs_comp
return qs
def get_queryset(self):
return TaskQuerySet(self.model, using=self._db)
@@ -38,39 +39,48 @@ class TaskList(models.Model):
objects = models.Manager()
class Meta:
verbose_name = "Auf­ga­ben­be­reich"
verbose_name_plural = "Auf­ga­ben­be­reiche"
def __str__(self):
return self.name
class Task(models.Model):
title = models.CharField(max_length=140)
task_list = models.ForeignKey(TaskList, on_delete=models.CASCADE, null=True)
title = models.CharField(verbose_name="Titel", max_length=140)
task_list = models.ForeignKey(TaskList, verbose_name="Aufgabenbereich", on_delete=models.CASCADE, null=True)
created_date = models.DateTimeField(auto_now_add=True)
due_date = models.DateField(blank=True, null=True)
due_date = models.DateField(verbose_name="Fälligkeit", blank=True, null=True)
completed = models.BooleanField(default=False)
completed = models.BooleanField(verbose_name="Abgeschlossen", default=False)
completed_date = models.DateField(blank=True, null=True)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name="todo_created_by",
User,
related_name="created_by",
on_delete=models.CASCADE,
)
assigned_to = models.ForeignKey(
settings.AUTH_USER_MODEL,
User,
blank=True,
null=True,
related_name="todo_assigned_to",
related_name="assigned_to",
on_delete=models.CASCADE,
verbose_name="Zugewiesen an",
)
note = models.TextField(blank=True, null=True)
priority = models.PositiveIntegerField(blank=True, null=True)
note = models.TextField(verbose_name="Notizen", blank=True, null=True)
priority = models.PositiveIntegerField(verbose_name="Priorität", blank=True, null=True)
objects = models.Manager()
taskmanager = TaskManager()
class Meta:
verbose_name = "Aufgabe"
verbose_name_plural = "Aufgaben"
def __str__(self):
return self.title

View File

@@ -2,7 +2,7 @@
<div class="grid-x">
{% for member in members %}
<div class="cell large-2 medium-3 small-6">
<h2>{{member.surname}}</h2>
<h2>{{member.firstname}} {{member.surname}}</h2>
{% include 'members/partials/_member.html' %}
</div>
{% endfor %}

View File

@@ -34,35 +34,11 @@
</div>
<div class="grid-container">
<div class="grid-x">
<div class="cell medium-8">
<h1>{{ post.title|tags_to_url }}</h1>
<strong>{{ post.subtitle|default_if_none:"&nbsp;"|tags_to_url }}</strong>
<span class="nav fa fa-chevron-right fa-3x"></span>
<br>
<a href="{% url 'posts.show' next %}">Nächster Artikel</a><br>
<div class="cell medium-4">
{% include 'posts/partials/_date_box.html' %}
</div>
Start: {{ post.event_start }}<br>
Ende: {{ post.event_end }}<br>
{% if request.user.is_authenticated %}
{% if post.has_agenda %}
<a href="{{ ep_agenda_link }}">Agenda</a><br>
{% endif %}
{% if post.has_protocol %}
<a href="{{ ep_protocol_link }}">Protokoll</a>
{% endif %}
<br>------<br>
<a href="{% url "admin:posts_news_change" post.id %}">Bearbeiten</a>
{% endif %}
<div class="grid-x grid-padding-x">
<div class="cell medium-8">
<hr>
{% for tag in post.get_tagnames %}
{{ tag|tags_to_url }}
@@ -77,6 +53,25 @@
{{ post.body|safe|add_internal_links|tags_to_url }}
<hr>
</div>
<div class="cell medium-4">
{% if request.user.is_authenticated %}
{% if post.has_agenda %}
<a href="{{ ep_agenda_link }}">Agenda</a><br>
{% endif %}
{% if post.has_protocol %}
<a href="{{ ep_protocol_link }}">Protokoll</a>
{% endif %}
{% endif %}
Start: {{ post.event_start }}<br>
Ende: {{ post.event_end }}<br>
<a href="{% url 'posts.show' next %}">Nächster Artikel </a><span class="nav fa fa-chevron-right fa-1x"></span><br>
{% include 'posts/partials/_date_box.html' %}
</div>
</div>
<div class="grid-x grid-margin-x">

View File

@@ -37,23 +37,32 @@
{% endif %}
<div class="grid-container">
{% for member in members %}
<h2>{{member.0}}<a class="headerlink" href="/members/jobs/{{member.2}}" title="Permalink to {{member.2}}"> #</a></h2>
<!-- show job lists in a job group -->
{% regroup job_members by job.name as all_jobmem_list %}
{% for jobmem in all_jobmem_list %}
<div class="padding-top-1 padding-left-1 padding-bottom-1 padding-right-1" style="background-color: white;">
<h2>{{jobmem.grouper}}<a class="headerlink" href="#{{jobmem.list.0.job.slug}}" title="Permalink to {{jobmem.grouper}}"> #</a></h2>
<b>Aktuelle Mitglieder:</b>
<div class="grid-x">
{% for mem in member.1 %}
{% with member=mem.member %}
{% for jm in jobmem.list %}
{% with member=jm.member %}
<div class="medium-3 large-2 small-6 cell">
<h2>{{member.surname}}</h2>
<h2>{{jm.member.firstname}} {{jm.member.surname}}</h2>
{% include 'members/partials/_member.html' %}
</div>
{% endwith %}
{% endfor %}
</div>
</div>
{% endfor %}
</div>
<div class="grid-container">