Merge branch 'master' of https://git.fet.at/bofh/fet2020
This commit is contained in:
@@ -28,6 +28,9 @@ def authentication(username, password):
|
|||||||
except LDAPBindError as e:
|
except LDAPBindError as e:
|
||||||
logger.info('Username does not exist. Error: {}'.format(e))
|
logger.info('Username does not exist. Error: {}'.format(e))
|
||||||
username = None
|
username = None
|
||||||
|
except Exception as e:
|
||||||
|
logger.info('Connection to server lost. Error: {}'.format(e))
|
||||||
|
username = None
|
||||||
|
|
||||||
if not has_user:
|
if not has_user:
|
||||||
username = None
|
username = None
|
||||||
|
|||||||
6
fet2020/authentications/forms.py
Normal file
6
fet2020/authentications/forms.py
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class LoginForm(forms.Form):
|
||||||
|
username = forms.CharField()
|
||||||
|
password = forms.CharField(label='Passwort', widget=forms.PasswordInput())
|
||||||
@@ -3,9 +3,11 @@ from django.contrib.auth import login, logout
|
|||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
from documents.etherpadlib import del_ep_cookie
|
||||||
|
|
||||||
from .authentications import authentication
|
from .authentications import authentication
|
||||||
from .decorators import unauthenticated_user, authenticated_user
|
from .decorators import unauthenticated_user, authenticated_user
|
||||||
from documents.etherpadlib import del_ep_cookie
|
from .forms import LoginForm
|
||||||
|
|
||||||
|
|
||||||
@unauthenticated_user
|
@unauthenticated_user
|
||||||
@@ -27,7 +29,11 @@ def loginPage(request):
|
|||||||
else:
|
else:
|
||||||
messages.info(request, 'username or password is incorrect')
|
messages.info(request, 'username or password is incorrect')
|
||||||
|
|
||||||
context = {}
|
form = LoginForm()
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"form": form,
|
||||||
|
}
|
||||||
return render(request, 'authentications/login.html', context)
|
return render(request, 'authentications/login.html', context)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
0
fet2020/blackboard/__init__.py
Normal file
0
fet2020/blackboard/__init__.py
Normal file
21
fet2020/blackboard/admin.py
Normal file
21
fet2020/blackboard/admin.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import JobPosting
|
||||||
|
|
||||||
|
|
||||||
|
class JobPostingAdmin(admin.ModelAdmin):
|
||||||
|
model = JobPosting
|
||||||
|
fieldsets = (
|
||||||
|
(None, {
|
||||||
|
'fields': (
|
||||||
|
'companyName',
|
||||||
|
'jobName',
|
||||||
|
'salary',
|
||||||
|
'pdfLocation',
|
||||||
|
'publishDate',
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(JobPosting, JobPostingAdmin)
|
||||||
5
fet2020/blackboard/apps.py
Normal file
5
fet2020/blackboard/apps.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class BlackboardConfig(AppConfig):
|
||||||
|
name = 'blackboard'
|
||||||
76
fet2020/blackboard/models.py
Normal file
76
fet2020/blackboard/models.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
from django.core.validators import ValidationError
|
||||||
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
import locale
|
||||||
|
import os
|
||||||
|
from os.path import splitext, basename
|
||||||
|
|
||||||
|
import ghostscript
|
||||||
|
|
||||||
|
import logging
|
||||||
|
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/')
|
||||||
|
pdf_thumb_location = models.CharField(max_length=128)
|
||||||
|
publishDate = models.DateField('date published', default=timezone.now)
|
||||||
|
|
||||||
|
# Managers
|
||||||
|
all_jobPosting = models.Manager()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Stellenausschreibung"
|
||||||
|
verbose_name_plural = "Stellenausschreibungen"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.publishDate) + "_" + self.companyName + "_" + self.jobName
|
||||||
|
|
||||||
|
def pdf2jpeg(self, pdf_input_path, jpeg_output_path):
|
||||||
|
args = [
|
||||||
|
"pef2jpeg", # actual value doesn't matter
|
||||||
|
"-dNOPAUSE",
|
||||||
|
"-sDEVICE=jpeg",
|
||||||
|
"-dDEVICEWIDTHPOINTS=600",
|
||||||
|
"-dDEVICEHEIGHTPOINTS=800",
|
||||||
|
"-sOutputFile=" + jpeg_output_path,
|
||||||
|
pdf_input_path
|
||||||
|
]
|
||||||
|
|
||||||
|
encoding = locale.getpreferredencoding()
|
||||||
|
args = [a.encode(encoding) for a in args]
|
||||||
|
|
||||||
|
ghostscript.Ghostscript(*args)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
if not os.path.exists(settings.MEDIA_ROOT + "uploads/blackboard/thumb/"):
|
||||||
|
os.makedirs(settings.MEDIA_ROOT + "uploads/blackboard/thumb/")
|
||||||
|
|
||||||
|
pdf_thumb_location_full = settings.MEDIA_ROOT \
|
||||||
|
+ "uploads/blackboard/thumb/" \
|
||||||
|
+ splitext(basename(self.pdfLocation.name))[0] \
|
||||||
|
+ ".jpg"
|
||||||
|
|
||||||
|
self.pdf_thumb_location = "/files/uploads/blackboard/thumb/" \
|
||||||
|
+ splitext(basename(self.pdfLocation.name))[0] \
|
||||||
|
+ ".jpg"
|
||||||
|
|
||||||
|
self.pdf2jpeg(self.pdfLocation.path, pdf_thumb_location_full)
|
||||||
|
logger.info("SavenThumbAs: " + self.pdf_thumb_location)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
count = 0
|
||||||
|
for i in self.pdfLocation.name:
|
||||||
|
if i == '.':
|
||||||
|
count = count + 1
|
||||||
|
if count > 1: # if more than one dot in filename
|
||||||
|
raise ValidationError(_('Keine Dateien mit >1 Punkten im Namen erlaubt.'))
|
||||||
3
fet2020/blackboard/tests.py
Normal file
3
fet2020/blackboard/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
8
fet2020/blackboard/urls.py
Normal file
8
fet2020/blackboard/urls.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.index, name='blackboard'),
|
||||||
|
]
|
||||||
14
fet2020/blackboard/views.py
Normal file
14
fet2020/blackboard/views.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from django.shortcuts import render
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from .models import JobPosting
|
||||||
|
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
jobPost_cutoff = timezone.now().date() - timedelta(30) # 30days from now
|
||||||
|
context = {
|
||||||
|
"jobPostings": JobPosting.all_jobPosting.filter(publishDate__gt=jobPost_cutoff),
|
||||||
|
}
|
||||||
|
return render(request, 'blackboard/index.html', context)
|
||||||
@@ -17,7 +17,7 @@ env = environ.Env(
|
|||||||
DEBUG=(bool, True),
|
DEBUG=(bool, True),
|
||||||
MYSQL_HOST=(str, "mysql"),
|
MYSQL_HOST=(str, "mysql"),
|
||||||
MYSQL_PORT=(int, 3308),
|
MYSQL_PORT=(int, 3308),
|
||||||
MYSQL_DATABASE=(str,"fet2020db"),
|
MYSQL_DATABASE=(str, "fet2020db"),
|
||||||
MYSQL_USER=(str),
|
MYSQL_USER=(str),
|
||||||
MYSQL_PASSWORD=(str)
|
MYSQL_PASSWORD=(str)
|
||||||
)
|
)
|
||||||
@@ -50,7 +50,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = env('DEBUG')
|
DEBUG = env('DEBUG')
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
SECRET_KEY = 'r37-i7l)vrduzz2-gira+z#u!p!di9#f+%s*5-bb($hg)55@ns'
|
SECRET_KEY = 'r37-i7l)vrduzz2-gira+z#u!p!di9#f+%s*5-bb($hg)55@ns'
|
||||||
else:
|
else:
|
||||||
SECRET_KEY = env('SECRET_KEY')
|
SECRET_KEY = env('SECRET_KEY')
|
||||||
@@ -81,6 +81,8 @@ INSTALLED_APPS = [
|
|||||||
'posts.apps.PostsConfig',
|
'posts.apps.PostsConfig',
|
||||||
'members.apps.MembersConfig',
|
'members.apps.MembersConfig',
|
||||||
'documents.apps.DocumentsConfig',
|
'documents.apps.DocumentsConfig',
|
||||||
|
'blackboard.apps.BlackboardConfig',
|
||||||
|
'tasks.apps.TasksConfig',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
@@ -121,21 +123,21 @@ WSGI_APPLICATION = 'fet2020.wsgi.application'
|
|||||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else:
|
else:
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.mysql',
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
'NAME': env('MYSQL_DATABASE'),
|
'NAME': env('MYSQL_DATABASE'),
|
||||||
'USER': env('MYSQL_USER'),
|
'USER': env('MYSQL_USER'),
|
||||||
'PASSWORD':env('MYSQL_PASSWORD'),
|
'PASSWORD': env('MYSQL_PASSWORD'),
|
||||||
'HOST': env('MYSQL_HOST'),
|
'HOST': env('MYSQL_HOST'),
|
||||||
'PORT':env('MYSQL_PORT')
|
'PORT': env('MYSQL_PORT')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -38,4 +38,6 @@ urlpatterns = [
|
|||||||
path('ckeditor/', include('ckeditor_uploader.urls')),
|
path('ckeditor/', include('ckeditor_uploader.urls')),
|
||||||
path('api/', include(router.urls)),
|
path('api/', include(router.urls)),
|
||||||
path('members/', include('members.urls'), name='members'),
|
path('members/', include('members.urls'), name='members'),
|
||||||
|
path('blackboard/', include('blackboard.urls'), name='blackboard'),
|
||||||
|
path('tasks/', include('tasks.urls'), name='tasks'),
|
||||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
|
|||||||
@@ -26,11 +26,19 @@ def index(request):
|
|||||||
# remove the pinned post
|
# remove the pinned post
|
||||||
posts.remove(featured_post)
|
posts.remove(featured_post)
|
||||||
|
|
||||||
|
featured_event = Event.only_events.get_future_events().first()
|
||||||
|
# if there is no futurity event
|
||||||
|
if not featured_event:
|
||||||
|
featured_event = Event.only_events.get_past_events().first()
|
||||||
|
|
||||||
|
featured_meeting = FetMeeting.objects.get_meetings()
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'posts': posts,
|
'posts': posts,
|
||||||
'events': Event.all_events.get_five_events(),
|
'events': Event.all_events.get_five_events(),
|
||||||
'featured_post': featured_post,
|
'featured_post': featured_post,
|
||||||
'featured_meeting': FetMeeting.objects.get_meetings(),
|
'featured_event': featured_event,
|
||||||
|
'featured_meeting': featured_meeting,
|
||||||
'tags_list': ", ".join(t)
|
'tags_list': ", ".join(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,12 @@ class JobGroup(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.slug:
|
||||||
|
self.slug = self.shortterm
|
||||||
|
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Job(models.Model):
|
class Job(models.Model):
|
||||||
name = models.CharField(max_length=128)
|
name = models.CharField(max_length=128)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from django.utils import timezone
|
|||||||
|
|
||||||
class PostManager(models.Manager):
|
class PostManager(models.Manager):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super().get_queryset()
|
return super().get_queryset().order_by('-public_date')
|
||||||
|
|
||||||
def get_visible_articles(self):
|
def get_visible_articles(self):
|
||||||
return self.get_queryset().filter(is_hidden=False)
|
return self.get_queryset().filter(is_hidden=False)
|
||||||
@@ -34,7 +34,7 @@ class NewsManager(models.Manager):
|
|||||||
Provide a query set only for "News"
|
Provide a query set only for "News"
|
||||||
"""
|
"""
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super().get_queryset().filter(post_type='N')
|
return super().get_queryset().filter(post_type='N').order_by('-public_date')
|
||||||
|
|
||||||
def get_visible_articles(self):
|
def get_visible_articles(self):
|
||||||
return self.get_queryset().filter(is_hidden=False)
|
return self.get_queryset().filter(is_hidden=False)
|
||||||
@@ -48,7 +48,8 @@ class AllEventManager(models.Manager):
|
|||||||
return super().get_queryset().filter(Q(post_type='E') | Q(post_type='F'))
|
return super().get_queryset().filter(Q(post_type='E') | Q(post_type='F'))
|
||||||
|
|
||||||
def get_five_events(self):
|
def get_five_events(self):
|
||||||
return self.get_queryset().order_by('-event_start')[:5]
|
date_today = timezone.now()
|
||||||
|
return self.get_queryset().filter(event_start__gt=date_today).order_by('event_start')[:5]
|
||||||
|
|
||||||
|
|
||||||
class EventManager(models.Manager):
|
class EventManager(models.Manager):
|
||||||
@@ -59,6 +60,14 @@ class EventManager(models.Manager):
|
|||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super().get_queryset().filter(post_type='E')
|
return super().get_queryset().filter(post_type='E')
|
||||||
|
|
||||||
|
def get_future_events(self):
|
||||||
|
date_today = timezone.now()
|
||||||
|
return self.get_queryset().filter(event_start__gt=date_today).order_by('event_start')
|
||||||
|
|
||||||
|
def get_past_events(self):
|
||||||
|
date_today = timezone.now()
|
||||||
|
return self.get_queryset().filter(event_start__lt=date_today).order_by('-event_start')
|
||||||
|
|
||||||
|
|
||||||
class FetMeetingManager(models.Manager):
|
class FetMeetingManager(models.Manager):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -219,14 +219,6 @@ class Event(Post):
|
|||||||
verbose_name = "Event"
|
verbose_name = "Event"
|
||||||
verbose_name_plural = "Events"
|
verbose_name_plural = "Events"
|
||||||
|
|
||||||
@property
|
|
||||||
def event_start_month(self):
|
|
||||||
return self.event_start.strftime("%b")
|
|
||||||
|
|
||||||
@property
|
|
||||||
def event_start_day(self):
|
|
||||||
return self.event_start.strftime("%d")
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.event_end is None or self.event_start is None:
|
if self.event_end is None or self.event_start is None:
|
||||||
raise ValidationError(_('Das Datum des Events fehlt.'))
|
raise ValidationError(_('Das Datum des Events fehlt.'))
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
django==3.1.*
|
django==3.1.*
|
||||||
django-taggit==1.3.0
|
django-ckeditor==6.0.0
|
||||||
django-ckeditor==5.9.0
|
django-environ==0.4.5
|
||||||
Pillow==7.2.0
|
django-filter==2.3.0
|
||||||
djangorestframework==3.11.0
|
|
||||||
django-static-jquery-ui==1.12.1.1
|
django-static-jquery-ui==1.12.1.1
|
||||||
|
django-taggit==1.3.0
|
||||||
|
djangorestframework==3.12.1
|
||||||
docutils==0.16
|
docutils==0.16
|
||||||
easy-thumbnails==2.7.0
|
easy-thumbnails==2.7.0
|
||||||
etherpad-lite==0.5
|
etherpad-lite==0.5
|
||||||
@@ -11,3 +12,7 @@ django-filter
|
|||||||
ldap3
|
ldap3
|
||||||
django-environ
|
django-environ
|
||||||
mysql-python
|
mysql-python
|
||||||
|
ldap3==2.8.1
|
||||||
|
#mysqlclient==2.0.1
|
||||||
|
Pillow==7.2.0
|
||||||
|
ghostscript==0.6
|
||||||
|
|||||||
0
fet2020/tasks/__init__.py
Normal file
0
fet2020/tasks/__init__.py
Normal file
33
fet2020/tasks/admin.py
Normal file
33
fet2020/tasks/admin.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import Task, TaskList
|
||||||
|
|
||||||
|
|
||||||
|
class TaskAdmin(admin.ModelAdmin):
|
||||||
|
model = Task
|
||||||
|
fieldsets = (
|
||||||
|
(None, {
|
||||||
|
'fields': (
|
||||||
|
'title',
|
||||||
|
'task_list',
|
||||||
|
'due_date',
|
||||||
|
'completed',
|
||||||
|
'completed_date',
|
||||||
|
'assigned_to',
|
||||||
|
'note',
|
||||||
|
'priority',
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
list_display = ('title', 'task_list', 'completed', 'priority', 'due_date')
|
||||||
|
list_filter = ('task_list', )
|
||||||
|
ordering = ('priority', )
|
||||||
|
search_fields = ('title', )
|
||||||
|
|
||||||
|
def save_model(self, request, obj, form, change):
|
||||||
|
obj.created_by = request.user
|
||||||
|
super().save_model(request, obj, form, change)
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(TaskList)
|
||||||
|
admin.site.register(Task, TaskAdmin)
|
||||||
5
fet2020/tasks/apps.py
Normal file
5
fet2020/tasks/apps.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class TasksConfig(AppConfig):
|
||||||
|
name = 'tasks'
|
||||||
15
fet2020/tasks/forms.py
Normal file
15
fet2020/tasks/forms.py
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
from django import forms
|
||||||
|
|
||||||
|
from .models import Task
|
||||||
|
|
||||||
|
|
||||||
|
class TaskForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Task
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
'title',
|
||||||
|
'task_list',
|
||||||
|
'due_date',
|
||||||
|
'assigned_to',
|
||||||
|
]
|
||||||
75
fet2020/tasks/models.py
Normal file
75
fet2020/tasks/models.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
from django.conf import settings
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models import Q
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
|
class TaskQuerySet(models.QuerySet):
|
||||||
|
def get_ordered(self):
|
||||||
|
return self.order_by('task_list')
|
||||||
|
|
||||||
|
|
||||||
|
class TaskManager(models.Manager):
|
||||||
|
def get_tasks(self, user, completed):
|
||||||
|
if completed:
|
||||||
|
return self.get_queryset().get_ordered().filter(Q(assigned_to=user))
|
||||||
|
else:
|
||||||
|
return self.get_queryset().get_ordered().filter(
|
||||||
|
Q(assigned_to=user)
|
||||||
|
& Q(completed=completed)
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return TaskQuerySet(self.model, using=self._db)
|
||||||
|
|
||||||
|
|
||||||
|
class TaskList(models.Model):
|
||||||
|
name = models.CharField(max_length=60)
|
||||||
|
slug = models.SlugField(unique=True, null=True, blank=True)
|
||||||
|
|
||||||
|
objects = models.Manager()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
created_date = models.DateTimeField(auto_now_add=True)
|
||||||
|
due_date = models.DateField(blank=True, null=True)
|
||||||
|
|
||||||
|
completed = models.BooleanField(default=False)
|
||||||
|
completed_date = models.DateField(blank=True, null=True)
|
||||||
|
|
||||||
|
created_by = models.ForeignKey(
|
||||||
|
settings.AUTH_USER_MODEL,
|
||||||
|
related_name="todo_created_by",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
assigned_to = models.ForeignKey(
|
||||||
|
settings.AUTH_USER_MODEL,
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
related_name="todo_assigned_to",
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
)
|
||||||
|
|
||||||
|
note = models.TextField(blank=True, null=True)
|
||||||
|
priority = models.PositiveIntegerField(blank=True, null=True)
|
||||||
|
|
||||||
|
objects = models.Manager()
|
||||||
|
taskmanager = TaskManager()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if self.completed and not self.completed_date:
|
||||||
|
self.completed_date = timezone.now().date()
|
||||||
|
|
||||||
|
if not self.completed and self.completed_date:
|
||||||
|
self.completed_date = None
|
||||||
|
|
||||||
|
super().save(*args, **kwargs)
|
||||||
3
fet2020/tasks/tests.py
Normal file
3
fet2020/tasks/tests.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
8
fet2020/tasks/urls.py
Normal file
8
fet2020/tasks/urls.py
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('', views.index, name='tasks'),
|
||||||
|
]
|
||||||
56
fet2020/tasks/views.py
Normal file
56
fet2020/tasks/views.py
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.shortcuts import render
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
from .forms import TaskForm
|
||||||
|
from .models import Task
|
||||||
|
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
current_user = request.user.id
|
||||||
|
current_action = False
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if 'btn_input' in request.POST:
|
||||||
|
form = TaskForm(request.POST)
|
||||||
|
|
||||||
|
if form.is_valid():
|
||||||
|
task = form.save(commit=False)
|
||||||
|
task.created_by = request.user
|
||||||
|
task.save()
|
||||||
|
|
||||||
|
elif 'btn_checkbox' in request.POST:
|
||||||
|
for task_id in request.POST.getlist('checkbox'):
|
||||||
|
task = Task.objects.get(id=task_id)
|
||||||
|
|
||||||
|
if not task.completed:
|
||||||
|
task.completed = True
|
||||||
|
task.completed_date = timezone.now().date()
|
||||||
|
task.save()
|
||||||
|
|
||||||
|
elif 'btn_user' in request.POST:
|
||||||
|
if request.POST['action'] == 'show_incompleted':
|
||||||
|
current_action = False
|
||||||
|
else:
|
||||||
|
current_action = True
|
||||||
|
|
||||||
|
if request.POST['user'] == 'all':
|
||||||
|
current_user = None
|
||||||
|
else:
|
||||||
|
current_user = int(request.POST['user'])
|
||||||
|
|
||||||
|
form = TaskForm()
|
||||||
|
tasks = deque(Task.taskmanager.get_tasks(user=current_user, completed=current_action))
|
||||||
|
users = User.objects.all()
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"form": form,
|
||||||
|
"tasks": tasks,
|
||||||
|
"users": users,
|
||||||
|
"current_user": current_user,
|
||||||
|
"current_action": current_action,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(request, 'tasks/index.html', context)
|
||||||
@@ -1,123 +1,23 @@
|
|||||||
<!DOCTYPE html>
|
{% extends 'layout.html' %}
|
||||||
<html>
|
{% block content %}
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Login</title>
|
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
|
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
|
||||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" integrity="sha384-gfdkjb5BdAXd+lj+gudLWI+BXq4IuLW5IT+brZEZsLFm++aCMlF1V92rMkPaX4PP" crossorigin="anonymous">
|
|
||||||
|
|
||||||
|
<div class="grid-container">
|
||||||
|
<div class="grid-x">
|
||||||
|
|
||||||
<style>
|
<form action="" method="post">
|
||||||
body,
|
<h3>Anmeldung für FET-Mitarbeiter</h3>
|
||||||
html {
|
{% csrf_token %}
|
||||||
margin: 0;
|
{{ form }}
|
||||||
padding: 0;
|
<input type="submit" class="button" name="btn_input" value="Anmelden">
|
||||||
height: 100%;
|
|
||||||
background: #7abecc !important;
|
|
||||||
}
|
|
||||||
.user_card {
|
|
||||||
width: 350px;
|
|
||||||
margin-top: auto;
|
|
||||||
margin-bottom: auto;
|
|
||||||
background: #74cfbf;
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 10px;
|
|
||||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
|
||||||
-webkit-box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
|
||||||
-moz-box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
}
|
{% for message in messages %}
|
||||||
|
<p id="messages">{{message}}</p>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
.form_container {
|
</form>
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#form-title{
|
</div>
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.login_btn {
|
</div>
|
||||||
width: 100%;
|
|
||||||
background: #33ccff !important;
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
.login_btn:focus {
|
|
||||||
box-shadow: none !important;
|
|
||||||
outline: 0px !important;
|
|
||||||
}
|
|
||||||
.login_container {
|
|
||||||
padding: 0 2rem;
|
|
||||||
}
|
|
||||||
.input-group-text {
|
|
||||||
background: #f7ba5b !important;
|
|
||||||
color: white !important;
|
|
||||||
border: 0 !important;
|
|
||||||
border-radius: 0.25rem 0 0 0.25rem !important;
|
|
||||||
}
|
|
||||||
.input_user,
|
|
||||||
.input_pass:focus {
|
|
||||||
box-shadow: none !important;
|
|
||||||
outline: 0px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#messages{
|
{% endblock %}
|
||||||
background-color: grey;
|
|
||||||
color: #fff;
|
|
||||||
padding: 10px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container h-100">
|
|
||||||
<div class="d-flex justify-content-center h-100">
|
|
||||||
<div class="user_card">
|
|
||||||
<div class="d-flex justify-content-center">
|
|
||||||
|
|
||||||
|
|
||||||
<h3 id="form-title">LOGIN</h3>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex justify-content-center form_container">
|
|
||||||
<form method="POST" action="">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="input-group mb-3">
|
|
||||||
<div class="input-group-append">
|
|
||||||
<span class="input-group-text"><i class="fas fa-user"></i></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="text" name="username" placeholder="Username..." class="form-control">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group mb-2">
|
|
||||||
<div class="input-group-append">
|
|
||||||
<span class="input-group-text"><i class="fas fa-key"></i></span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="password" name="password" placeholder="Password..." class="form-control" >
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="d-flex justify-content-center mt-3 login_container">
|
|
||||||
<input class="btn login_btn" type="submit" value="Login">
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% for message in messages %}
|
|
||||||
<p id="messages">{{message}}</p>
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
|
|||||||
16
fet2020/templates/blackboard/index.html
Normal file
16
fet2020/templates/blackboard/index.html
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{% extends 'layout.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="grid-container">
|
||||||
|
<h1>Blackboard</h1>
|
||||||
|
|
||||||
|
<!-- Tab panes -->
|
||||||
|
<div class="tabs-content">
|
||||||
|
{% for job in jobPostings %}
|
||||||
|
{% include 'blackboard/partials/_jobPosting.html' %}
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
18
fet2020/templates/blackboard/partials/_jobPosting.html
Normal file
18
fet2020/templates/blackboard/partials/_jobPosting.html
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<div class="grid-x">
|
||||||
|
<div class="medium-3 large-2 small-6 cell">
|
||||||
|
<h2>{{job.companyName}}</h2>
|
||||||
|
<p>{{job.jobName}}<br>
|
||||||
|
Mindestgehalt: {{job.salary}}€</p>
|
||||||
|
{# only thumb and name of member #}
|
||||||
|
<a class="thumbnail member-thumb" href="{{job.pdfLocation.url}}" style="width:200px;height:300px">
|
||||||
|
<img src="{{job.pdf_thumb_location}}" alt="" />
|
||||||
|
<div class="thumb-layer">
|
||||||
|
<div>
|
||||||
|
<h1>{{job.companyName}}</h1>
|
||||||
|
<p>{{job.jobName}} <br>
|
||||||
|
Mindestgehalt: {{job.salary}}€</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<div class="medium-4 responsive-side-box cell">
|
<div class="medium-4 responsive-side-box cell">
|
||||||
<a href="{% url 'posts.index' %}"><h1>Neuigkeiten</h1></a>
|
<a href="{% url 'posts.index' %}"><h1>Neuigkeiten</h1></a>
|
||||||
<div class="article-row-section">
|
<div class="article-row-section">
|
||||||
{% with post=featured_post %}
|
{% with post=featured_event %}
|
||||||
{% include 'posts/partials/_article_row.html' %}
|
{% include 'posts/partials/_article_row.html' %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% for post in featured_meeting %}
|
{% for post in featured_meeting %}
|
||||||
|
|||||||
@@ -23,12 +23,14 @@
|
|||||||
{% if request.user.is_authenticated %}
|
{% if request.user.is_authenticated %}
|
||||||
Hallo {{request.user.username}}
|
Hallo {{request.user.username}}
|
||||||
<li class=""><a href="/admin">Admin</a></li>
|
<li class=""><a href="/admin">Admin</a></li>
|
||||||
|
<li class=""><a href="{%url 'tasks'%}">Tasks</a> </li>
|
||||||
<li class=""><a href="{%url 'logout'%}">Logout</a> </li>
|
<li class=""><a href="{%url 'logout'%}">Logout</a> </li>
|
||||||
{% else %}
|
{% else %}
|
||||||
<li class=""><a href="{%url 'login'%}">Login</a> </li>
|
<li class=""><a href="{%url 'login'%}">Login</a> </li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li class=""><a href="{%url 'home'%}">Aktuelles</a> </li>
|
<li class=""><a href="{%url 'home'%}">Aktuelles</a> </li>
|
||||||
<li class=""><a href="/fotos">Fotos</a> </li>
|
<li class=""><a href="/fotos">Fotos</a> </li>
|
||||||
|
<li class=""><a href="/blackboard">Blackboard</a> </li>
|
||||||
<li class=""><a href="{%url 'members'%}">Mitarbeiter</a> </li>
|
<li class=""><a href="{%url 'members'%}">Mitarbeiter</a> </li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,18 +6,19 @@
|
|||||||
<h1>Mitglieder / Tätigkeiten</h1>
|
<h1>Mitglieder / Tätigkeiten</h1>
|
||||||
|
|
||||||
<!-- Nav tabs -->
|
<!-- Nav tabs -->
|
||||||
<ul class="nav nav-tabs nav-fill">
|
<div class="tabs.simple">
|
||||||
<a class="active" id="members" data-toggle="tab" href="/members">Mitglieder</a>
|
<a href="/members">Mitglieder</a>
|
||||||
<a id="members" data-toggle="tab" href="/members/A">Aktive Mitglieder</a>
|
<a href="/members/A">Aktive Mitglieder</a>
|
||||||
<a id="members" data-toggle="tab" href="/members/P">Inaktive Mitglieder</a>
|
<a href="/members/P">Inaktive Mitglieder</a>
|
||||||
|
|
||||||
{% for job in job_group %}
|
{% for job in job_group %}
|
||||||
<a id="jobs-{{job.slug}}" data-toggle="tab" href="/members/jobs/{{job.slug}}">{{job.name}}</a>
|
<a href="/members/jobs/{{job.slug}}">{{job.name}}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Tab panes -->
|
<!-- Tab panes -->
|
||||||
<div class="tab-content" id="nav-tabContent">
|
<div class="tabs-content">
|
||||||
|
|
||||||
{% for mem in member %}
|
{% for mem in member %}
|
||||||
{% with member=mem %}
|
{% with member=mem %}
|
||||||
{% include 'members/partials/_member_details.html' %}
|
{% include 'members/partials/_member_details.html' %}
|
||||||
@@ -25,27 +26,21 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if members is not None %}
|
{% if members is not None %}
|
||||||
<div id="members" class="tab-pane">
|
{% include 'members/members_list.html' %}
|
||||||
{% include 'members/members_list.html' %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% for job in job_list %}
|
{% for job in job_list %}
|
||||||
<div id="jobs" class="tab-pane">
|
<div class="tabs-content" id="{{job.3}}">
|
||||||
<div class="grid-container">
|
|
||||||
|
|
||||||
<div id="{{job.3}}">
|
<h2>{{job.0}}<a class="headerlink" href="#{{job.3}}" title="Permalink to {{job.0}}"> #</a></h2>
|
||||||
|
|
||||||
<h2>{{job.0}}<a class="headerlink" href="#{{job.3}}" title="Permalink to {{job.0}}"> #</a></h2>
|
{% with active_members=job.1 inactive_members=job.2 %}
|
||||||
|
{% include 'members/jobs_list.html' %}
|
||||||
{% with active_members=job.1 inactive_members=job.2 %}
|
{% endwith %}
|
||||||
{% include 'members/jobs_list.html' %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
{% if post.subtitle is not None %}
|
{% if post.subtitle is not None %}
|
||||||
<p class="article-row-content-description">{{post.subtitle}}</p>
|
<p class="article-row-content-description">{{post.subtitle}}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<p class="article-row-content-author">{{post.author}}</p>
|
<!--<p class="article-row-content-author">{{post.author}}</p>-->
|
||||||
<time class="article-row-content-time" datetime="2008-02-14 20:00">{{post.public_date}}</time>
|
<time class="article-row-content-time">{{post.event_start|date}}</time>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
78
fet2020/templates/tasks/index.html
Normal file
78
fet2020/templates/tasks/index.html
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
{% extends 'layout.html' %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="grid-container">
|
||||||
|
<h1>Tasks</h1>
|
||||||
|
|
||||||
|
<div class="grid-x">
|
||||||
|
<form action="" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<label>User
|
||||||
|
<select id="id_user" name="user">
|
||||||
|
<option value="all">
|
||||||
|
Alle
|
||||||
|
</option>
|
||||||
|
{% for user in users %}
|
||||||
|
<option value="{{ user.id }}" {% if current_user == user.id %} selected {% endif %}>
|
||||||
|
{{ user.username }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>Aktion
|
||||||
|
<select id="id_action" name="action">
|
||||||
|
<option value="show_incompleted" {% if not current_action %} selected {% endif %}>unvollständige Tasks</option>
|
||||||
|
<option value="show_all" {% if current_action %} selected {% endif %}>alle Tasks</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<input type="submit" class="button" name="btn_user" value="Anzeigen">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-x">
|
||||||
|
|
||||||
|
<form action="" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{% regroup tasks by task_list as section_list %}
|
||||||
|
{% for group in section_list %}
|
||||||
|
|
||||||
|
<div class="cell">
|
||||||
|
<ul class="no-bullet">
|
||||||
|
|
||||||
|
<h3>{{ group.grouper }}</h3>
|
||||||
|
|
||||||
|
{% for task in group.list %}
|
||||||
|
<input type="checkbox" name="checkbox" value="{{ task.id }}" {% if task.completed %} checked {% endif %}>
|
||||||
|
|
||||||
|
{{ task.title }}</a>
|
||||||
|
|
||||||
|
{{ task.due_date|date:"d.m.Y" }}
|
||||||
|
|
||||||
|
<br>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<input type="submit" class="button" name="btn_checkbox" value="Task abschließen">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid-x">
|
||||||
|
|
||||||
|
<form action="" method="post">
|
||||||
|
<h2>neue Tasks hinzufügen</h2>
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form }}
|
||||||
|
<input type="submit" class="button" name="btn_input" value="Hinzufügen">
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user