main
Jannes Hikmat 1 year ago
parent 7893cbcb6f
commit 3387056120

@ -18,6 +18,6 @@ class LocationAdmin(admin.ModelAdmin):
pass pass
@admin.register(Day) @admin.register(NewsArticle)
class DayAdmin(admin.ModelAdmin): class NewsArticleAdmin(admin.ModelAdmin):
pass pass

@ -9,9 +9,7 @@ from datetime import date
class MenuForm(forms.ModelForm): class MenuForm(forms.ModelForm):
class Meta: class Meta:
model = Menu model = Menu
fields = ['art', 'name', 'price', 'allergens', 'types', 'day'] fields = ['location', 'art', 'name', 'price', 'allergens', 'types', 'day']
widgets = { widgets = {
'types': forms.CheckboxSelectMultiple(choices=TYPES_CHOICES), 'day': forms.DateInput(attrs={'type': 'date'}),
'allergens': forms.CheckboxSelectMultiple(choices=ALLERGENS_CHOICES),
'day': forms.DateInput(attrs={'type': 'date'}, format="%d.%m.%Y"),
} }

@ -20,46 +20,39 @@ example_json = [
def load_data(locations, checked_types, status): def load_data(locations, checked_types, status):
current_week = datetime.date.today().isocalendar().week current_week = datetime.date.today().isocalendar().week
days = Day.objects.filter(date__week=current_week)
for location in locations: for location in locations:
menus = Menu.objects.filter(days__in=days, locations=location) menus = Menu.objects.filter(day__week=current_week, location=location)
if not menus.exists(): if not menus.exists():
fetched_data = fetch(location.mensa_id) fetched_data = fetch(location.mensa_id)
store(fetched_data, location) store(fetched_data, location)
data = formatted_menu_data(locations, checked_types, status) data = formatted_menu_data(locations, checked_types, status)
return data return data
def formatted_menu_data(locations, checked_types, status): def formatted_menu_data(locations, checked_types, status):
current_week = datetime.date.today().isocalendar().week current_week = datetime.date.today().isocalendar().week
days = Day.objects.filter(date__week=current_week) today = datetime.date.today()
days = [today + datetime.timedelta(days=i)
for i in range(0 - today.weekday(), 5 - today.weekday())]
formatted_data = {} formatted_data = {}
for day in days:
formatted_data[str(day.date)] = []
for day in days: for day in days:
if day not in formatted_data:
formatted_data[day] = []
for location in locations: for location in locations:
menus = Menu.objects.filter(days=day, locations=location) menus = Menu.objects.filter(location=location, day=day)
for menu in menus: for menu in menus:
# AND or OR filter ??? # AND or OR filter ???
if set(checked_types).issubset(set(menu.types)): # <-- AND if set(checked_types).issubset(set(menu.types)): # <-- AND
formatted_data[day].append({
formatted_data[str(day.date)].append({
'art': menu.art, 'art': menu.art,
'name': menu.name, 'name': menu.name,
'price': menu.price if status is None else menu.get_price[status], 'price': menu.price if status is None
else menu.get_price[status],
'allergens': menu.get_allergens, 'allergens': menu.get_allergens,
'types': menu.get_types, 'types': menu.get_types,
'likes': menu.likes, 'likes': menu.likes,
'location': location.name, 'location': location,
'pk': menu.pk, 'pk': menu.pk,
}) })
return formatted_data return formatted_data
@ -67,32 +60,21 @@ def formatted_menu_data(locations, checked_types, status):
def store(data, location): def store(data, location):
for day_json in data: for day_json in data:
date_string= day_json['day'] date_string = day_json['day']
menus_json = day_json['menus'] menus_json = day_json['menus']
day = parse_date(date_string)
date = parse_date(date_string)
if not Day.objects.filter(date=date).exists():
Day(date=date).save()
day = Day.objects.get(date=date)
for menu_json in menus_json: for menu_json in menus_json:
art = menu_json['art'] art = menu_json['art']
name = menu_json['name'] name = menu_json['name']
price = menu_json['price'] or "0,00€ / 0,00€ / 0,00€" price = menu_json['price'] or "0,00 € / 0,00 € / 0,00 €"
allergens = menu_json['allergens'] allergens = menu_json['allergens']
types = menu_json['types'] types = menu_json['types']
menu_exists = Menu.objects.filter(art=art, name=name, price=price,
menu_exists = Menu.objects.filter(art=art, name=name, price=price, allergens=allergens, types=types).exists() allergens=allergens, types=types,
day=day).exists()
if not menu_exists: if not menu_exists:
Menu(art=art, name=name, price=price, allergens=allergens, types=types).save() Menu(art=art, name=name, price=price, allergens=allergens,
types=types, day=day, location=location).save()
menu = Menu.objects.get(art=art, name=name, price=price, allergens=allergens, types=types)
menu.locations.add(location)
menu.days.add(day)
menu.save()
def fetch(mensa_id: int): def fetch(mensa_id: int):
@ -100,43 +82,3 @@ def fetch(mensa_id: int):
if res.status_code != 200: if res.status_code != 200:
raise Exception('request failed') raise Exception('request failed')
return res.json() return res.json()
def models_from_json(mensa_id: int):
for day_json in fetch_menus(mensa_id):
day_date = datetime.datetime.strptime(day_json['day'], '%Y-%m-%d').date()
if not Day.objects.filter(mensa_id=mensa_id, date__contains=day_date):
# create new day model ONLY if it does not yet exist for the given
# day_date(s) and mensa_id.
Day(mensa_id=mensa_id, date=day_date).save()
day = Day.objects.get(mensa_id=mensa_id, date=day_date)
for menu_json in day_json['menus']:
# if meal already exists, simply add day to related menus.
# otherwise, build new meal model from json with the specified menu
# as the first related menu.
if not Menu.objects.filter(name=menu_json['name']):
Menu.from_json(menu_json, day).save()
day.menus.add(Menu.objects.get(name=menu_json['name']))
def days_as_json(mensa_ids: list, types_filter: list, price_filter: str):
cw = datetime.date.today().isocalendar().week
dates = list(dict.fromkeys([day.date for day in Day.objects.filter(date__week=cw)]))
days_list = []
for date in dates:
day_json = { 'day': date, 'menus': [] }
for mensa_id in mensa_ids:
try:
day = Day.objects.get(mensa_id=mensa_id, date=date)
for menu in Menu.objects.filter(day=day, related_types__in=Type.objects.filter(type__in=types_filter) if types_filter else Type.objects.all()).distinct():
day_json['menus'].append({ 'menu': menu, 'mensa_id': mensa_id })
except ObjectDoesNotExist:
pass
days_list.append(day_json)
return days_list

@ -0,0 +1,25 @@
# Generated by Django 4.1.4 on 2023-01-06 00:06
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mensaviewer', '0015_comment_timestamp'),
]
operations = [
migrations.RemoveField(
model_name='menu',
name='days',
),
migrations.DeleteModel(
name='Day',
),
migrations.AddField(
model_name='menu',
name='days',
field=models.JSONField(blank=True, null=True),
),
]

@ -0,0 +1,37 @@
# Generated by Django 4.1.4 on 2023-01-06 22:11
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mensaviewer', '0016_remove_menu_days_delete_day_menu_days'),
]
operations = [
migrations.RemoveField(
model_name='menu',
name='days',
),
migrations.RemoveField(
model_name='menu',
name='locations',
),
migrations.AddField(
model_name='location',
name='city',
field=models.CharField(blank=True, max_length=144, null=True),
),
migrations.AddField(
model_name='menu',
name='day',
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name='menu',
name='location',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='mensaviewer.location'),
),
]

@ -0,0 +1,42 @@
# Generated by Django 4.1.4 on 2023-01-06 22:32
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('mensaviewer', '0017_remove_menu_days_remove_menu_locations_location_city_and_more'),
]
operations = [
migrations.RenameField(
model_name='comment',
old_name='username',
new_name='author',
),
migrations.RenameField(
model_name='comment',
old_name='related_menu',
new_name='menu',
),
migrations.RemoveField(
model_name='comment',
name='comment',
),
migrations.AddField(
model_name='comment',
name='text',
field=models.CharField(blank=True, max_length=512, null=True),
),
migrations.CreateModel(
name='NewsArticle',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('author', models.CharField(default='anon', max_length=144)),
('text', models.CharField(blank=True, max_length=8192, null=True)),
('location', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='mensaviewer.location')),
],
),
]

@ -0,0 +1,23 @@
# Generated by Django 4.1.4 on 2023-01-06 22:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mensaviewer', '0018_rename_username_comment_author_and_more'),
]
operations = [
migrations.AlterField(
model_name='comment',
name='text',
field=models.TextField(blank=True, null=True),
),
migrations.AlterField(
model_name='newsarticle',
name='text',
field=models.TextField(blank=True, null=True),
),
]

@ -0,0 +1,19 @@
# Generated by Django 4.1.4 on 2023-01-08 01:01
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('mensaviewer', '0019_alter_comment_text_alter_newsarticle_text'),
]
operations = [
migrations.AddField(
model_name='newsarticle',
name='timestamp',
field=models.DateTimeField(blank=True, default=django.utils.timezone.now),
),
]

@ -0,0 +1,18 @@
# Generated by Django 4.1.4 on 2023-01-08 02:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mensaviewer', '0020_newsarticle_timestamp'),
]
operations = [
migrations.AddField(
model_name='newsarticle',
name='title',
field=models.CharField(blank=True, max_length=144),
),
]

@ -0,0 +1,38 @@
# Generated by Django 4.1.4 on 2023-01-08 03:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mensaviewer', '0021_newsarticle_title'),
]
operations = [
migrations.AlterField(
model_name='menu',
name='allergens',
field=models.JSONField(blank=True, default=[]),
),
migrations.AlterField(
model_name='menu',
name='art',
field=models.CharField(choices=[('M', 'Mensa'), ('C', 'Cafeteria')], default='M', max_length=1),
),
migrations.AlterField(
model_name='menu',
name='name',
field=models.CharField(blank=True, default='', max_length=144),
),
migrations.AlterField(
model_name='menu',
name='price',
field=models.CharField(blank=True, default='', max_length=144),
),
migrations.AlterField(
model_name='menu',
name='types',
field=models.JSONField(blank=True, default=[]),
),
]

@ -0,0 +1,38 @@
# Generated by Django 4.1.4 on 2023-01-08 18:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('mensaviewer', '0022_alter_menu_allergens_alter_menu_art_alter_menu_name_and_more'),
]
operations = [
migrations.AlterField(
model_name='menu',
name='allergens',
field=models.JSONField(blank=True, default=list),
),
migrations.AlterField(
model_name='menu',
name='art',
field=models.CharField(choices=[('Mensa', 'Mensa'), ('Cafeteria', 'Cafeteria')], default='M', max_length=24),
),
migrations.AlterField(
model_name='menu',
name='name',
field=models.CharField(blank=True, default='---', max_length=144),
),
migrations.AlterField(
model_name='menu',
name='price',
field=models.CharField(blank=True, default='---', max_length=144),
),
migrations.AlterField(
model_name='menu',
name='types',
field=models.JSONField(blank=True, default=list),
),
]

@ -10,7 +10,7 @@ class Location(models.Model):
mensa_id = models.CharField(max_length=2, null=True, blank=True) mensa_id = models.CharField(max_length=2, null=True, blank=True)
def __str__(self): def __str__(self):
return f"{self.city} - {self.name} [{self.mensa_id}]" return f"{self.city} - {self.name}"
@property @property
def menus(self): def menus(self):
@ -33,7 +33,7 @@ class Menu(models.Model):
blank=True) blank=True)
def __str__(self): def __str__(self):
return f"{self.name}" return f"{self.name} - {self.location} | {self.day}"
@property @property
def get_price(self): def get_price(self):
@ -69,6 +69,9 @@ class Comment(models.Model):
author = models.CharField(max_length=144, default='anon') author = models.CharField(max_length=144, default='anon')
text = models.TextField(null=True, blank=True) text = models.TextField(null=True, blank=True)
def __str__(self):
return f"{self.menu} - {self.author} | {self.timestamp}"
class NewsArticle(models.Model): class NewsArticle(models.Model):
location = models.ForeignKey(Location, on_delete=models.CASCADE) location = models.ForeignKey(Location, on_delete=models.CASCADE)
@ -76,3 +79,6 @@ class NewsArticle(models.Model):
author = models.CharField(max_length=144, default='anon') author = models.CharField(max_length=144, default='anon')
title = models.CharField(max_length=144, blank=True) title = models.CharField(max_length=144, blank=True)
text = models.TextField(null=True, blank=True) text = models.TextField(null=True, blank=True)
def __str__(self):
return f"{self.title} | {self.title} - {self.author} | {self.timestamp}"

@ -5,19 +5,28 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="stylesheet" href="{% static 'base.css' %}"> <link rel="stylesheet" href="{% static 'base.css' %}">
<title>Mensaviewer | {% block title %}Home{%endblock%}</title> <title>Mensaviewer | {% block title %}Home{% endblock %}</title>
</head> </head>
<body> <body>
<header> <header>
<h1>Header Title</h1> {% block header %}<h1>Header Title</h1>{% endblock %}
</header> </header>
<nav>
<a href="{% url 'mensaviewer:home' %}">Home</a>
<a href="/admin">Admin</a>
</nav>
<main> <main>
{% block content %} <nav>
{% endblock %} <a href="{% url 'mensaviewer:flensburg' %}">Flensburg</a>
<a href="{% url 'mensaviewer:custom' %}">Custom</a>
<hr>
<a href="{% url 'mensaviewer:location_list' %}">Locations</a>
<a href="{% url 'mensaviewer:news_list' %}">News Articles</a>
<a href="{% url 'mensaviewer:menu_list' %}">Menus</a>
<a href="{% url 'mensaviewer:comment_list' %}">Comments</a>
<hr>
<a href="/admin">Admin</a>
</nav>
<div class="content">
{% block content %}
{% endblock %}
</div>
</main> </main>
</body> </body>
</html> </html>

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block content %}
<form class="model-form" method="post">
{% csrf_token %}
<p>Are you sure you want to delete "{{ object }}"?</p>
{{ form }}
<input type="submit" value="Confirm">
</form>
{% endblock %}

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block content %}
<div class="detail-container">
<h1>By {{ comment.author }}</h1>
<h3>Timestamp: {{ comment.timestamp }}</h3>
<h3>Menu: <a href="{% url 'mensaviewer:menu_detail' comment.menu.pk %}">{{ comment.menu.name }}</a></h3>
<h3>Text: {{ comment.text }}</h3>
</div>
{% endblock %}

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% block content %}
<form class="model-form" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Save">
</form>
{% endblock %}

@ -0,0 +1,31 @@
{% extends 'base.html' %}
{% block content %}
<h1>Comments</h1>
<table class="comment-list">
<tr>
<td colspan="99" class="centered-td">
<a class="btn" href="{% url 'mensaviewer:comment_create' %}">Add Comment</a>
</td>
</tr>
<tr>
<th>Author</th>
<th>Timestamp</th>
<th>Menu</th>
<th></th>
<th></th>
</tr>
{% for comment in object_list %}
<tr>
<td>{{ comment.author }}</td>
<td>{{ comment.timestamp }}</td>
<td><a href="{% url 'mensaviewer:menu_detail' comment.menu.pk %}">{{ comment.menu }}</a></td>
<td class="fit-width"><a href="{% url 'mensaviewer:comment_update' comment.pk %}">Edit</a></td>
<td class="fit-width"><a href="{% url 'mensaviewer:comment_delete' comment.pk %}">Delete</a></td>
</tr>
{% endfor %}
</table>
{% endblock %}

@ -0,0 +1,60 @@
{% extends 'base.html' %}
{% block title %}Custom Locations{% endblock %}
{% block header %}<h1>Show menus for custom location selection</h1>{% endblock %}
{% block content %}
<form class="filter-form" action="{% url 'mensaviewer:custom' %}" method="get">
<input type="hidden" name="filterform" value="filter">
<div class="type-filter">
{% for name, id in types %}
<label>
{{ name }}
<input type="checkbox" name="type" value={{ id }} {% if id in checked_types %} checked{% endif %}>
</label>
{% endfor %}
</div>
<div class="status-filter">
{% for status_value in status_values %}
<label>
{{ status_value }}
<input type="radio" name="status" value="{{ status_value }}" {% if status == status_value %} checked{% endif %}>
</label>
{% endfor %}
</div>
<div class="location-filter">
{% for location in locations %}
<label>
{{ location.name }}
<input type="checkbox" name="location" value="{{ location.mensa_id }}" {% if location.mensa_id in selected_locations %} checked{% endif %}>
</label>
{% endfor %}
</div>
<button type="submit">Filter</button>
</form>
<div class="container">
{% for day, menus in menu_data.items %}
<div class="column">
<h2>{{ day }}</h2>
{% for menu in menus %}
<div class="menu {% if menu.mensa_id == 1 %}a-mensa{% elif menu.mensa_id == 6 %}b-mensa{% endif %}">
<a href="{% url 'mensaviewer:menu_detail' menu.pk %}">{{ menu.name }}</a>
<p>{{ menu.art }}</p>
<p>{{ menu.price }}</p>
<p>{{ menu.types }}</p>
<p>{{ menu.allergens }}</p>
<p>{{ menu.likes }}</p>
<a href="{% url 'mensaviewer:like' menu.pk %}?next={{ request.path }}">Like</a>
<a href="{% url 'mensaviewer:dislike' menu.pk %}?next={{ request.path }}">Dislike</a>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
{% endblock %}

@ -1,17 +1,18 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %}Flensburg{% endblock %}
{% block header %}<h1>Mensen in Flensburg</h1>{% endblock %}
{% block content %} {% block content %}
<form action="{% url 'mensaviewer:home' %}" method="get"> <form class="filter-form" action="{% url 'mensaviewer:flensburg' %}" method="get">
<input type="hidden" name="filterform" value="filter"> <input type="hidden" name="filterform" value="filter">
<div class="type-filters"> <div class="type-filter">
{% for name, id in types %} {% for name, id in types %}
<label> <label>
{{ name }} {{ name }}
<input type="checkbox" <input type="checkbox" name="type" value={{ id }} {% if id in checked_types %} checked{% endif %}>
name="type"
value={{ id }}
{% if id in checked_types %} checked{% endif %}>
</label> </label>
{% endfor %} {% endfor %}
</div> </div>
@ -19,25 +20,10 @@
{% for status_value in status_values %} {% for status_value in status_values %}
<label> <label>
{{ status_value }} {{ status_value }}
<input type="radio" <input type="radio" name="status" value="{{ status_value }}" {% if status == status_value %} checked{% endif %}>
name="status"
value="{{ status_value }}"
{% if status == status_value %} checked{% endif %}>
</label> </label>
{% endfor %} {% endfor %}
</div> </div>
<div class="location-filter">
{% for location in locations %}
<label>
{{ location.name }}
<input type="checkbox"
name="location"
value="{{ location.mensa_id }}"
{% if location.mensa_id in selected_locations %} checked{% endif %}>
</label>
{% endfor %}
</div>
<a href="{% url 'mensaviewer:location_create' %}">New Location</a>
<button type="submit">Filter</button> <button type="submit">Filter</button>
</form> </form>
@ -47,23 +33,21 @@
<h2>{{ day }}</h2> <h2>{{ day }}</h2>
{% for menu in menus %} {% for menu in menus %}
<div class="menu {% if menu.mensa_id == 1 %}a-mensa{% elif menu.mensa_id == 6 %}b-mensa{% endif %}"> <div class="menu {% if menu.location.mensa_id == '1' %}a-mensa{% elif menu.location.mensa_id == '6' %}b-mensa{% endif %}">
<a href="{% url 'mensaviewer:menu_detail' menu.pk %}">{{ menu.name }}</a> <p>{{ menu.art }} - {{ menu.location.name }}</p>
<p>{{ menu.art }}</p> <hr>
<h4><a href="{% url 'mensaviewer:menu_detail' menu.pk %}">{{ menu.name }}</a></h4>
<p>{{ menu.price }}</p> <p>{{ menu.price }}</p>
<p>{{ menu.types }}</p> <p>{{ menu.types }}</p>
<p>{{ menu.allergens }}</p> <p>{{ menu.allergens }}</p>
<p>{{ menu.likes }}</p>
<a href="{% url 'mensaviewer:like' menu.pk %}?next={{ request.path }}">Like</a> <a href="{% url 'mensaviewer:like' menu.pk %}?next={{ request.path }}">Like</a>
<span>{{ menu.likes }}</span>
<a href="{% url 'mensaviewer:dislike' menu.pk %}?next={{ request.path }}">Dislike</a> <a href="{% url 'mensaviewer:dislike' menu.pk %}?next={{ request.path }}">Dislike</a>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% endblock %} {% endblock %}

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block content %}
<form class="model-form" method="post">
{% csrf_token %}
<p>Are you sure you want to delete "{{ object }}"?</p>
{{ form }}
<input type="submit" value="Confirm">
</form>
{% endblock %}

@ -0,0 +1,29 @@
{% extends 'base.html' %}
{% block content %}
<div class="detail-container">
<h1>{{ location.name }}</h1>
<h3>City: {{ location.city }}</h3>
<h3>ID: {{ location.mensa_id }}</h3>
<a href="{% url 'mensaviewer:news_add' location.pk %}">Add News</a>
<a href="{% url 'mensaviewer:menu_add' location.pk %}">Add Menu</a>
<div class="related-items">
<h2>News</h2>
{% for news_article in location.news_articles %}
<div class="news-article">
<h3><a href="{% url 'mensaviewer:news_detail' news_article.pk %}">{{ news_article.title }}</a></h3>
</div>
{% endfor %}
</div>
<div class="related-items">
<h2>Menus</h2>
{% for menu in location.menus %}
<div class="menu">
<h3><a href="{% url 'mensaviewer:menu_detail' menu.pk %}">{{ menu.name }}</a></h3>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% block content %}
<form class="model-form" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Save">
</form>
{% endblock %}

@ -0,0 +1,31 @@
{% extends 'base.html' %}
{% block content %}
<h1>Locations</h1>
<table class="location-list">
<tr>
<td colspan="99" class="centered-td">
<a class="btn" href="{% url 'mensaviewer:location_create' %}">New Location</a>
</td>
</tr>
<tr>
<th>Name</th>
<th>City</th>
<th>ID</th>
<th></th>
<th></th>
</tr>
{% for location in object_list %}
<tr>
<td><a href="{% url 'mensaviewer:location_detail' location.pk %}">{{ location.name }}</a></td>
<td>{{ location.city }}</td>
<td class="fit-width">{{ location.mensa_id }}</td>
<td class="fit-width"><a href="{% url 'mensaviewer:location_update' location.pk %}">Edit</a></td>
<td class="fit-width"><a href="{% url 'mensaviewer:location_delete' location.pk %}">Delete</a></td>
</tr>
{% endfor %}
</table>
{% endblock %}

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block content %}
<form class="model-form" method="post">
{% csrf_token %}
<p>Are you sure you want to delete "{{ object }}"?</p>
{{ form }}
<input type="submit" value="Confirm">
</form>
{% endblock %}

@ -0,0 +1,28 @@
{% extends 'base.html' %}
{% block content %}
<div class="detail-container">
<h1>{{ menu.name }}</h1>
<p>Art: {{ menu.art }}</p>
<p>Price for Students: {{ menu.get_price.Student }}</p>
<p>Price for Employees: {{ menu.get_price.Employee }}</p>
<p>Price for Guests: {{ menu.get_price.Guest }}</p>
<p>Allergens: {{ menu.get_allergens }}</p>
<p>Types: {{ menu.get_types }}</p>
<p>Rating: {{ menu.likes }}</p>
<p>Location and Date: {{ menu.location.city }} {{ menu.location.name }} | {{ menu.day }}</p>
<a href="{% url 'mensaviewer:like' menu.id %}?next={{ request.path }}">Like</a>
<a href="{% url 'mensaviewer:dislike' menu.id %}?next={{ request.path }}">Dislike</a>
<a href="{% url 'mensaviewer:comment_add' menu.id %}">Comment</a>
<div class="related-items">
{% for comment in menu.comments %}
<div class="comment">
<h3>{{ comment.author }}</h3>
<p>{{ comment.text }}</p>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% block content %}
<form class="model-form" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Save">
</form>
{% endblock %}

@ -0,0 +1,31 @@
{% extends 'base.html' %}
{% block content %}
<h1>Menus</h1>
<table class="menu-list">
<tr>
<td colspan="99" class="centered-td">
<a class="btn" href="{% url 'mensaviewer:menu_create' %}">Add Menu</a>
</td>
</tr>
<tr>
<th>Name</th>
<th>Location</th>
<th>Day</th>
<th>Edit</th>
<th>Delete</th>
</tr>
{% for menu in object_list %}
<tr>
<td><a href="{% url 'mensaviewer:menu_detail' menu.pk %}">{{ menu.name }}</a></td>
<td><a href="{% url 'mensaviewer:location_detail' menu.location.pk %}">{{ menu.location.name }}</a></td>
<td>{{ menu.day }}</td>
<td class="fit-width"><a href="{% url 'mensaviewer:menu_update' menu.pk %}">Edit</a></td>
<td class="fit-width"><a href="{% url 'mensaviewer:menu_delete' menu.pk %}">Delete</a></td>
</tr>
{% endfor %}
</table>
{% endblock %}

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block content %}
<form class="model-form" method="post">
{% csrf_token %}
<p>Are you sure you want to delete "{{ object }}"?</p>
{{ form }}
<input type="submit" value="Confirm">
</form>
{% endblock %}

@ -0,0 +1,12 @@
{% extends 'base.html' %}
{% block content %}
<div class="detail-container">
<h1>By {{ newsarticle.author }}</h1>
<h3>Timestamp: {{ newsarticle.timestamp }}</h3>
<h3>Location: <a href="{% url 'mensaviewer:location_detail' newsarticle.location.pk %}">{{ newsarticle.location.name }}</a></h3>
<h3>Text: {{ newsarticle.text }}</h3>
</div>
{% endblock %}

@ -0,0 +1,11 @@
{% extends 'base.html' %}
{% block content %}
<form class="model-form" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Save">
</form>
{% endblock %}

@ -0,0 +1,33 @@
{% extends 'base.html' %}
{% block content %}
<h1>News Articles</h1>
<table class="news-list">
<tr>
<td colspan="99" class="centered-td">
<a class="btn" href="{% url 'mensaviewer:news_create' %}">Add News Article</a>
</td>
</tr>
<tr>
<th>Title</th>
<th>Author</th>
<th>Timestamp</th>
<th>Menu</th>
<th></th>
<th></th>
</tr>
{% for newsarticle in object_list %}
<tr>
<td><a href="{% url 'mensaviewer:news_detail' newsarticle.pk %}">{{ newsarticle.title }}</a></td>
<td>{{ newsarticle.author }}</td>
<td>{{ newsarticle.timestamp }}</td>
<td><a href="{% url 'mensaviewer:location_detail' newsarticle.location.pk %}">{{ newsarticle.location.city }} - {{ newsarticle.location.name }}</a></td>
<td class="fit-width"><a href="{% url 'mensaviewer:news_update' newsarticle.pk %}">Edit</a></td>
<td class="fit-width"><a href="{% url 'mensaviewer:news_delete' newsarticle.pk %}">Delete</a></td>
</tr>
{% endfor %}
</table>
{% endblock %}

@ -3,10 +3,46 @@ from . import views
app_name = 'mensaviewer' app_name = 'mensaviewer'
urlpatterns = [ urlpatterns = [
path('', views.home, name='home'), # routes for overview
path('', views.flensburg, name='flensburg'),
path('custom-location', views.custom, name='custom'),
# raw create routes
path('add-location/', views.LocationCreateView.as_view(), name='location_create'),
path('add-news-article/', views.NewsArticleCreateView.as_view(), name='news_create'),
path('add-menu/', views.MenuCreateView.as_view(), name='menu_create'),
path('add-comment/', views.CommentCreateView.as_view(), name='comment_create'),
# relation-dependent create routes
path('location/<int:pk>/add-news-article/', views.NewsArticleCreateView.as_view(), name='news_add'),
path('location/<int:pk>/add-menu/', views.MenuCreateView.as_view(), name='menu_add'),
path('menu/<int:pk>/add-comment/', views.CommentCreateView.as_view(), name='comment_add'),
# list-view routes
path('locations/', views.LocationListView.as_view(), name='location_list'),
path('news-articles/', views.NewsArticleListView.as_view(), name='news_list'),
path('menus/', views.MenuListView.as_view(), name='menu_list'),
path('comments/', views.CommentListView.as_view(), name='comment_list'),
# detail-view routes
path('location/<int:pk>/', views.LocationDetailView.as_view(), name='location_detail'),
path('news-article/<int:pk>/', views.NewsArticleDetailView.as_view(), name='news_detail'),
path('menu/<int:pk>/', views.MenuDetailView.as_view(), name='menu_detail'), path('menu/<int:pk>/', views.MenuDetailView.as_view(), name='menu_detail'),
path('comment/<int:pk>/', views.CommentDetailView.as_view(), name='comment_detail'),
# update-view routes
path('location/<int:pk>/edit/', views.LocationUpdateView.as_view(), name='location_update'),
path('news-artice/<int:pk>/edit/', views.NewsArticleUpdateView.as_view(), name='news_update'),
path('menu/<int:pk>/edit/', views.MenuUpdateView.as_view(), name='menu_update'),
path('comment/<int:pk>/edit/', views.CommentUpdateView.as_view(), name='comment_update'),
# delete-view routes
path('location/<int:pk>/delete/', views.LocationDeleteView.as_view(), name='location_delete'),
path('news-artice/<int:pk>/delete/', views.NewsArticleDeleteView.as_view(), name='news_delete'),
path('menu/<int:pk>/delete/', views.MenuDeleteView.as_view(), name='menu_delete'),
path('comment/<int:pk>/delete/', views.CommentDeleteView.as_view(), name='comment_delete'),
# like / dislike routes
path('menu/<int:pk>/like/', views.like, name='like'), path('menu/<int:pk>/like/', views.like, name='like'),
path('menu/<int:pk>/dislike/', views.dislike, name='dislike'), path('menu/<int:pk>/dislike/', views.dislike, name='dislike'),
path('menu/<int:pk>/comment/', views.CommentCreateView.as_view(), name='comment'),
path('location/create/', views.LocationCreateView.as_view(), name='location_create')
] ]

@ -1,6 +1,7 @@
from django.views.generic import DetailView from django.views.generic import DetailView, ListView
from django.views.generic.edit import CreateView from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.shortcuts import render, redirect, reverse from django.shortcuts import render, redirect, reverse
from django.urls import reverse_lazy
from .models import * from .models import *
from .helpers import * from .helpers import *
@ -8,16 +9,36 @@ from .forms import *
from .data import * from .data import *
def home(request): def flensburg(request):
filter_query = request.COOKIES.get('filter_query', '') filter_query = request.COOKIES.get('filter_query', '')
if len(request.GET.getlist('filterform')) == 0 and filter_query != '': if len(request.GET.getlist('filterform')) == 0 and filter_query != '':
return redirect(reverse('mensaviewer:home') + '?' + filter_query) return redirect(reverse('mensaviewer:flensburg') + '?' + filter_query)
checked_types = request.GET.getlist('type')
status = request.GET.get('status')
selected_locations = Location.objects.filter(mensa_id__in=[1, 6])
context = {
'types': [(TYPES[type], type) for type in TYPES],
'checked_types': checked_types,
'status_values': STATUS_VALUES,
'status': status,
'locations': Location.objects.all(),
'menu_data': load_data(selected_locations, checked_types, status),
}
response = render(request, "flensburg.html", context)
response.set_cookie('filter_query', request.GET.urlencode())
return response
def custom(request):
filter_query = request.COOKIES.get('filter_query', '')
if len(request.GET.getlist('filterform')) == 0 and filter_query != '':
return redirect(reverse('mensaviewer:custom') + '?' + filter_query)
selected_location_ids = request.GET.getlist('location') selected_location_ids = request.GET.getlist('location')
checked_types = request.GET.getlist('type') checked_types = request.GET.getlist('type')
status = request.GET.get('status') status = request.GET.get('status')
selected_locations = Location.objects.filter(mensa_id__in=selected_location_ids) selected_locations = Location.objects.filter(mensa_id__in=selected_location_ids)
context = { context = {
'types': [(TYPES[type], type) for type in TYPES], 'types': [(TYPES[type], type) for type in TYPES],
'checked_types': checked_types, 'checked_types': checked_types,
@ -27,8 +48,7 @@ def home(request):
'selected_locations': selected_location_ids, 'selected_locations': selected_location_ids,
'menu_data': load_data(selected_locations, checked_types, status), 'menu_data': load_data(selected_locations, checked_types, status),
} }
response = render(request, "custom.html", context)
response = render(request, "flensburg.html", context)
response.set_cookie('filter_query', request.GET.urlencode()) response.set_cookie('filter_query', request.GET.urlencode())
return response return response
@ -38,7 +58,7 @@ def like(request, pk):
menu = Menu.objects.get(pk=pk) menu = Menu.objects.get(pk=pk)
menu.likes += 1 menu.likes += 1
menu.save() menu.save()
return redirect(reverse('mensaviewer:home') + '?' + request.GET.get('next')) return redirect(request.GET.get('next'))
def dislike(request, pk): def dislike(request, pk):
@ -46,30 +66,155 @@ def dislike(request, pk):
menu = Menu.objects.get(pk=pk) menu = Menu.objects.get(pk=pk)
menu.likes -= 1 menu.likes -= 1
menu.save() menu.save()
return redirect(reverse('mensaviewer:home') + '?' + request.GET.get('next')) return redirect(request.GET.get('next'))
class MenuDetailView(DetailView):
model = Menu
template_name = "menu_detail.html"
queryset = Menu.objects.all()
class LocationCreateView(CreateView): class LocationCreateView(CreateView):
model = Location model = Location
template_name = "location/edit.html"
fields = '__all__'
success_url = reverse_lazy("mensaviewer:location_list")
class LocationUpdateView(UpdateView):
model = Location
template_name = "location/edit.html"
fields = '__all__' fields = '__all__'
template_name = "create_location.html" success_url = reverse_lazy("mensaviewer:location_list")
success_url = "/"
class LocationDeleteView(DeleteView):
model = Location
template_name = "location/delete.html"
success_url = reverse_lazy("mensaviewer:location_list")
class LocationDetailView(DetailView):
model = Location
template_name = "location/detail.html"
class LocationListView(ListView):
model = Location
template_name = "location/list.html"
class NewsArticleCreateView(CreateView):
model = NewsArticle
template_name = "news/edit.html"
fields = ['location', 'title', 'author', 'text']
success_url = reverse_lazy("mensaviewer:news_list")
def get_initial(self):
if 'pk' in self.kwargs:
return { 'location': get_object_or_404(Location, pk=self.kwargs['pk']) }
else:
return { 'location': None }
class NewsArticleUpdateView(UpdateView):
model = NewsArticle
template_name = "news/edit.html"
fields = ['location', 'title', 'author', 'text']
success_url = reverse_lazy("mensaviewer:news_list")
class NewsArticleDeleteView(DeleteView):
model = NewsArticle
template_name = "news/delete.html"
success_url = reverse_lazy("mensaviewer:news_list")
class NewsArticleDetailView(DetailView):
model = NewsArticle
template_name = "news/detail.html"
class NewsArticleListView(ListView):
model = NewsArticle
template_name = "news/list.html"
class MenuCreateView(CreateView):
model = Menu
form_class = MenuForm
template_name = "menu/edit.html"
success_url = reverse_lazy("mensaviewer:menu_list")
def get_initial(self):
if 'pk' in self.kwargs:
return { 'location': get_object_or_404(Location, pk=self.kwargs['pk']) }
else:
return { 'location': None }
class MenuUpdateView(UpdateView):
model = Menu
form_class = MenuForm
template_name = "menu/edit.html"
success_url = reverse_lazy("mensaviewer:menu_list")
def get_initial(self):
menu = get_object_or_404(Menu, pk=self.kwargs['pk'])
print(menu.types)
return { 'types': menu.types }
class MenuDeleteView(DeleteView):
model = Menu
template_name = "menu/delete.html"
success_url = reverse_lazy("mensaviewer:menu_list")
class MenuDetailView(DetailView):
model = Menu
template_name = "menu/detail.html"
class MenuListView(ListView):
model = Menu
template_name = "menu/list.html"
class CommentCreateView(CreateView): class CommentCreateView(CreateView):
model = Comment model = Comment
fields = ['username', 'comment'] fields = ['menu', 'author', 'text']
template_name = "comment.html" template_name = "comment/edit.html"
success_url = '/menu/{pk}/' success_url = '/menu/{menu_id}/'
def form_valid(self, form): def get_initial(self):
self.menu = get_object_or_404(Menu, pk=self.kwargs['pk']) if 'pk' in self.kwargs:
form.instance.menu = self.menu return { 'menu': get_object_or_404(Menu, pk=self.kwargs['pk']) }
print(self.menu) else:
return super().form_valid(form) return { 'menu': None }
class CommentUpdateView(UpdateView):
model = Comment
fields = ['author', 'text']
template_name = "comment/edit.html"
success_url = '/menu/{menu_id}/'
class CommentDeleteView(DeleteView):
model = Comment
template_name = "comment/delete.html"
success_url = '/menu/{menu_id}/'
class CommentDetailView(DetailView):
model = Comment
template_name = "comment/detail.html"
class CommentListView(ListView):
model = Comment
template_name = "comment/list.html"

@ -1,15 +1,170 @@
* { * {
font-family: monospace; font-family: Tahoma;
box-sizing: border-box; box-sizing: border-box;
outline: 1px solid green;
background-color: rgba(16, 16, 16, 0.1)
} }
body { body {
margin: 0; margin: 0;
background-color: #1D1D28;
color: #EEEDFF;
}
h1, h2, h3 {
margin: 0;
padding: 1em;
}
header {
padding-left: 12vw;
box-shadow: 0px 0px 32px rgba(0, 0, 0, 0.5);
}
main {
display: flex;
background-color: #3E3949;
}
nav {
border-right: 1px solid rgba(0, 0, 0, 0.2);
display: flex;
flex-direction: column;
width: 16vw;
min-width: 16em;
padding-top: 4em;
}
nav a {
text-align: center;
font-size: 18px;
padding: 0.6em;
}
nav a:hover {
transition: 250ms;
background-color: rgba(0, 0, 0, 0.2);
}
nav hr {
width: 16em;
opacity: 0.1;
}
a {
text-decoration: none;
color: #EEEDFF;
}
a:hover {
transition: 250ms;
color: #FFEFCA;
}
a:active {
color: #CEA716;
}
form {
padding: 1em;
margin: 0.5em;
border: 1px solid rgba(0, 0, 0, 0.3);
border-radius: 0.5em;
box-shadow: inset 0px 0px 1em rgba(0, 0, 0, 0.2);
}
table {
width: 100%;
padding: 1em;
margin: 0.5em;
border: 1px solid rgba(0, 0, 0, 0.3);
border-radius: 0.5em;
box-shadow: inset 0px 0px 1em rgba(0, 0, 0, 0.2);
}
th {
padding: 0.5em;
color: #CEA716;
text-align: left;
}
td {
padding: 0.5em;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 0.2em;
max-width: 30vw;
}
.fit-width {
width: 1%;
}
.content {
margin-left: 4vw;
margin-right: 4vw;
flex: 1;
}
.centered-td {
text-align: center;
padding: 1em;
}
.btn {
padding: 0.4em;
background-color: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(0, 0, 0, 0.3);
border-radius: 0.5em;
}
.model-form {
display: flex;
flex-direction: column;
}
.model-form input, .model-form textarea, .model-form select, button {
margin-top: 0.5em;
margin-bottom: 1em;
padding: 0.5em;
background-color: rgba(0, 0, 0, 0.5);
border: 1px solid rgba(0, 0, 0, 1);
border-radius: 0.5em;
color: #EEEDFF;
}
.filter-form {
display: flex;
flex-direction: column;
}
.filter-form .type-filter, .filter-form .status-filter, .filter-form .location-filter {
display: flex;
flex-direction: row;
}
.filter-form label {
background-color: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(0, 0, 0, 0.3);
border-radius: 0.3em;
padding: 0.3em;
margin: 0.5em;
text-align: right;
}
input[type="submit"], button {
cursor: pointer;
}
input[type="submit"]:active, button:active {
transition: 200ms;
background-color: rgba(0, 0, 0, 0.7);
cursor: pointer;
} }
.container { .container {
padding: 0.3em;
margin: 0.3em;
border: 1px solid rgba(0, 0, 0, 0.3);
border-radius: 0.5em;
box-shadow: inset 0px 0px 1em rgba(0, 0, 0, 0.2);
display: flex; display: flex;
flex-flow: row wrap; flex-flow: row wrap;
} }
@ -20,10 +175,31 @@ body {
} }
.menu { .menu {
padding: 1em;
margin: 0.2em;
border-radius: 0.5em;
box-shadow: inset 0px 0px 1em rgba(0, 0, 0, 0.2);
background-color: rgba(0, 0, 0, 0.2);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
.menu * {
margin: 0;
}
.menu hr {
opacity: 0.2;
}
.menu p {
font-size: 12px;
}
.menu a {
display: inline-block;
}
.a-mensa { .a-mensa {
background-color: rgba(16, 64, 16, 0.2); background-color: rgba(16, 64, 16, 0.2);
} }

Loading…
Cancel
Save