Browse Source

initial commit

Bernadette Elena Hammerle 1 year ago
commit
31f9090820

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+.DS_Store

+ 4 - 0
Dockerfile

@@ -0,0 +1,4 @@
+FROM docker.io/python:3.9.5
+RUN pip install flask==1.1.2
+COPY . /src/
+CMD ["python", "/src/app.py"]

+ 58 - 0
app.py

@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+"""
+Created on Sat Apr 22 17:07:13 2022
+
+@author: Hammerle
+"""
+import json
+import requests
+from flask import Flask, render_template
+
+
+# FOLDER = "src/"  # docker
+FOLDER = ""  # local
+
+
+app = Flask(__name__)
+app.config["TEMPLATES_AUTO_RELOAD"] = True
+with open(f"{FOLDER}secret_key", "rb") as secretKeyFile:
+    app.secret_key = secretKeyFile.read()
+
+
+@app.route("/", methods=["GET"])
+def index():
+    return render_template("index.html")
+
+
+@app.route("/karte", methods=["GET"])
+def map_():
+    marker_groups = read_database("map")
+    return render_template("map.html", marker_groups=marker_groups)
+
+
+@app.route("/schritte", methods=["GET"])
+def steps():
+    steps = read_database("steps")
+    return render_template("steps.html", steps=steps)
+
+
+@app.route("/events", methods=["GET"])
+def events():
+    boudicca_events = requests.get("https://api.boudicca.events/event").json()
+    return render_template('events.html', events=boudicca_events)
+
+
+@app.route("/mitgestaltung", methods=["GET"])
+def contribute():
+    return render_template("contribute.html")
+
+
+def read_database(name):
+    with open(f"{FOLDER}database_{name}.json") as file:
+        db_data = json.load(file)
+    return db_data
+
+
+if __name__ == "__main__":
+    app.run()  # localhost
+    # app.run(host="0.0.0.0")  # in network

+ 86 - 0
database_map.json

@@ -0,0 +1,86 @@
+{
+    "Vereine": {
+        "icon": "globe",
+        "color": "blue",
+        "liste":
+        [{
+                "name": "Klimabündnis Oberösterreich",
+                "url": "https://oberoesterreich.klimabuendnis.at/",
+                "long": 48.29643,
+                "lat": 14.29434,
+                "shortdescription": "Wir sind das größte kommunale Klimaschutz-Netzwerk Österreichs. Wir setzen lokale Klimaschutz-Initiativen und schützen gleichzeitig den Amazonas-Regenwald. Und das bereits seit 1990."
+            }, {
+                "name": "kim",
+                "url": "kimz.at",
+                "long": 48.198655456853174,
+                "lat": 13.952166412848625,
+                "shortdescription": "Klimaschutz. Ideen. Möglichkeiten."
+            }, {
+                "name": "Verein Energiewende Linz",
+                "url": "https://www.energiewende-linz.at",
+                "long": 48.30807334038928,
+                "lat": 14.29490555581985,
+                "shortdescription": "Wir sehen uns als Anlaufstelle für alle, die die Umsetzung von konkreten Schritten zur Energiewende unterstützen wollen und damit einen Beitrag zu lokalen und globalen Klimaschutz leisten."
+            }, {
+                "name": "Grünschnabel",
+                "url": "gruenschnabel.at",
+                "long": 48.31066139162836,
+                "lat": 14.276978012854087,
+                "shortdescription": "Grünschnabel ist ein Projekt des Vereins zur Förderung von Lebensqualität."
+            }
+
+        ]
+    },
+    "Kleiderkreisel": {
+        "icon": "key",
+        "color": "red",
+        "liste":
+        [{
+                "name": "Rotkreuz-Markt",
+                "url": "https://www.roteskreuz.at/oberoesterreich/ried/ich-will-helfen/second-hand-shops",
+                "long": 48.30502268766204,
+                "lat": 14.29914939653715,
+                "shortdescription": "Das Rote Kreuz setzt auf Nachhaltigkeit: gebrauchte Kleidung und Gegenstände bekommen in den Second Hand Boutiquen des Roten Kreuzes ein zweites Leben."
+            }
+        ]
+    },
+    "SoLaWi": {
+        "icon": "leaf",
+        "color": "green",
+        "prefix": "fa",
+        "liste":
+        [{
+                "name": "SoLaWi Gmias!",
+                "url": "www.gmias.at",
+                "long": 48.27380161125205,
+                "lat": 14.228417930779035,
+                "shortdescription": "In unserer solidarischen Landwirtschaft bauen wir regional, saisonal und biologisch Gemüse an."
+            }, {
+                "name": "Futter Natur Gemüse aus Bergern",
+                "url": "www.futter-natur.at",
+                "long": 48.24057134126316,
+                "lat": 13.931337548703123,
+                "shortdescription": "Die Herausforderungen unserer Zeit brauchen die Vision einer guten Zukunft, den Mut anzupacken und Menschen, die den ersten Schritt gehen."
+            }
+        ]
+    },
+    "FoodCoops": {
+        "icon": "shopping-cart",
+        "color": "gray",
+        "liste":
+        [{
+                "name": "Franckkistl",
+                "url": "www.franckkistl.at",
+                "long": 48.295286122494524,
+                "lat": 14.314487701213688,
+                "shortdescription": "Die Foodcoop Franck Kistl steht für Ernährungssouveränität, gute Nachbarschaft, Nachhaltigkeit, Solidarität, Umweltschutz und neue Ideen."
+            }, {
+                "name": "EKG Linz",
+                "url": "ekglinz.fcoop.at",
+                "long": 48.30620307928006,
+                "lat": 14.290096170525898,
+                "shortdescription": ""
+            }
+        ]
+    }
+}

+ 11 - 0
database_steps.json

@@ -0,0 +1,11 @@
+[
+    {"title": "Stoffsackerl",
+    "subtitle": "eigenes Sackerl beim Einkaufen",
+    "description": "nimm dir dein eigenes Stoffsackerl mit beim Einkaufen, sodass du auf Papier- und Plastiksackerl verzichten kannst."},
+    {"title": "Vergiss Einweg",
+    "subtitle": "Nimm deine eigene Trinkflasche mit",
+    "description": "Nimm dir eine Trinkflasche mit um unterwegs gratis und umweltschonend trinken zu können."},
+    {"title": "Einmal ohne Auto",
+    "subtitle": "Fahrrad und Öffis statt Autos",
+    "description": "Versuch einmal in der Woche auf das Auto zu verzichten. Versuche die Öffis zu nutzen oder tue etwas Gutes für deine Gesundheit und fahr mit dem Fahrrad."}
+]

BIN
static/css/images/markers-matte.png


BIN
static/css/images/markers-matte@2x.png


BIN
static/css/images/markers-plain.png


BIN
static/css/images/markers-shadow.png


BIN
static/css/images/markers-shadow@2x.png


BIN
static/css/images/markers-soft.png


BIN
static/css/images/markers-soft@2x.png


+ 124 - 0
static/css/leaflet.awesome-markers.css

@@ -0,0 +1,124 @@
+/*
+Author: L. Voogdt
+License: MIT
+Version: 1.0
+*/
+
+/* Marker setup */
+.awesome-marker {
+  background: url('images/markers-soft.png') no-repeat 0 0;
+  width: 35px;
+  height: 46px;
+  position:absolute;
+  left:0;
+  top:0;
+  display: block;
+  text-align: center;
+}
+
+.awesome-marker-shadow {
+  background: url('images/markers-shadow.png') no-repeat 0 0;
+  width: 36px;
+  height: 16px;
+}
+
+/* Retina displays */
+@media (min--moz-device-pixel-ratio: 1.5),(-o-min-device-pixel-ratio: 3/2),
+(-webkit-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5),(min-resolution: 1.5dppx) {
+ .awesome-marker {
+  background-image: url('images/markers-soft@2x.png');
+  background-size: 720px 46px;
+ }
+ .awesome-marker-shadow {
+  background-image: url('images/markers-shadow@2x.png');
+  background-size: 35px 16px;
+ }
+}
+
+.awesome-marker i {
+  color: #333;
+  margin-top: 10px;
+  display: inline-block;
+  font-size: 14px;
+}
+
+.awesome-marker .icon-white {
+  color: #fff;
+}
+
+/* Colors */
+.awesome-marker-icon-red {
+  background-position: 0 0;
+}
+
+.awesome-marker-icon-darkred {
+  background-position: -180px 0;
+}
+
+.awesome-marker-icon-lightred {
+  background-position: -360px 0;
+}
+
+.awesome-marker-icon-orange {
+  background-position: -36px 0;
+}
+
+.awesome-marker-icon-beige {
+  background-position: -396px 0;
+}
+
+.awesome-marker-icon-green {
+  background-position: -72px 0;
+}
+
+.awesome-marker-icon-darkgreen {
+  background-position: -252px 0;
+}
+
+.awesome-marker-icon-lightgreen {
+  background-position: -432px 0;
+}
+
+.awesome-marker-icon-blue {
+  background-position: -108px 0;
+}
+
+.awesome-marker-icon-darkblue {
+  background-position: -216px 0;
+}
+
+.awesome-marker-icon-lightblue {
+  background-position: -468px 0;
+}
+
+.awesome-marker-icon-purple {
+  background-position: -144px 0;
+}
+
+.awesome-marker-icon-darkpurple {
+  background-position: -288px 0;
+}
+
+.awesome-marker-icon-pink {
+  background-position: -504px 0;
+}
+
+.awesome-marker-icon-cadetblue {
+  background-position: -324px 0;
+}
+
+.awesome-marker-icon-white {
+  background-position: -574px 0;
+}
+
+.awesome-marker-icon-gray {
+  background-position: -648px 0;
+}
+
+.awesome-marker-icon-lightgray {
+  background-position: -612px 0;
+}
+
+.awesome-marker-icon-black {
+  background-position: -682px 0;
+}

+ 50 - 0
static/css/main.css

@@ -0,0 +1,50 @@
+:root {
+    --dark-bg: #468A37;
+    --body-bg: #ddd;
+    --content-bg: #eee;
+    --white: #ffffff;
+}
+
+body {
+    background-color: var(--body-bg);
+}
+
+.wrapper {
+    background-color: var(--content-bg);
+}
+
+.content{
+    min-height: calc(100vh - 65px - 61px);
+}
+
+nav, nav h1 {
+    background-color: var(--dark-bg);
+    color: white;
+}
+
+nav img{
+    height: 55px;
+}
+
+nav a span {
+    color: var(--white);
+    display: block;
+    text-align: center;
+    font-size: 13px;
+    position: relative;
+    top: -3px;
+}
+
+nav a svg, nav a i {
+    color: var(--white);
+    display: inline-block;
+}
+
+.bi {
+    font-size: 25px;
+}
+
+#map{
+    height: calc(100vh - 120px - 2em);
+    min-height: 200px;
+}

BIN
static/favicon.ico


+ 122 - 0
static/js/leaflet.awesome-markers.js

@@ -0,0 +1,122 @@
+/*
+  Leaflet.AwesomeMarkers, a plugin that adds colorful iconic markers for Leaflet, based on the Font Awesome icons
+  (c) 2012-2013, Lennard Voogdt
+
+  http://leafletjs.com
+  https://github.com/lvoogdt
+*/
+
+/*global L*/
+
+(function (window, document, undefined) {
+    "use strict";
+    /*
+     * Leaflet.AwesomeMarkers assumes that you have already included the Leaflet library.
+     */
+
+    L.AwesomeMarkers = {};
+
+    L.AwesomeMarkers.version = '2.0.1';
+
+    L.AwesomeMarkers.Icon = L.Icon.extend({
+        options: {
+            iconSize: [35, 45],
+            iconAnchor:   [17, 42],
+            popupAnchor: [1, -32],
+            shadowAnchor: [10, 12],
+            shadowSize: [36, 16],
+            className: 'awesome-marker',
+            prefix: 'glyphicon',
+            spinClass: 'fa-spin',
+            extraClasses: '',
+            icon: 'home',
+            markerColor: 'blue',
+            iconColor: 'white'
+        },
+
+        initialize: function (options) {
+            options = L.Util.setOptions(this, options);
+        },
+
+        createIcon: function () {
+            var div = document.createElement('div'),
+                options = this.options;
+
+            if (options.icon) {
+                div.innerHTML = this._createInner();
+            }
+
+            if (options.bgPos) {
+                div.style.backgroundPosition =
+                    (-options.bgPos.x) + 'px ' + (-options.bgPos.y) + 'px';
+            }
+
+            this._setIconStyles(div, 'icon-' + options.markerColor);
+            return div;
+        },
+
+        _createInner: function() {
+            var iconClass, iconSpinClass = "", iconColorClass = "", iconColorStyle = "", options = this.options;
+
+            if(options.icon.slice(0,options.prefix.length+1) === options.prefix + "-") {
+                iconClass = options.icon;
+            } else {
+                iconClass = options.prefix + "-" + options.icon;
+            }
+
+            if(options.spin && typeof options.spinClass === "string") {
+                iconSpinClass = options.spinClass;
+            }
+
+            if(options.iconColor) {
+                if(options.iconColor === 'white' || options.iconColor === 'black') {
+                    iconColorClass = "icon-" + options.iconColor;
+                } else {
+                    iconColorStyle = "style='color: " + options.iconColor + "' ";
+                }
+            }
+
+            return "<i " + iconColorStyle + "class='" + options.extraClasses + " " + options.prefix + " " + iconClass + " " + iconSpinClass + " " + iconColorClass + "'></i>";
+        },
+
+        _setIconStyles: function (img, name) {
+            var options = this.options,
+                size = L.point(options[name === 'shadow' ? 'shadowSize' : 'iconSize']),
+                anchor;
+
+            if (name === 'shadow') {
+                anchor = L.point(options.shadowAnchor || options.iconAnchor);
+            } else {
+                anchor = L.point(options.iconAnchor);
+            }
+
+            if (!anchor && size) {
+                anchor = size.divideBy(2, true);
+            }
+
+            img.className = 'awesome-marker-' + name + ' ' + options.className;
+
+            if (anchor) {
+                img.style.marginLeft = (-anchor.x) + 'px';
+                img.style.marginTop  = (-anchor.y) + 'px';
+            }
+
+            if (size) {
+                img.style.width  = size.x + 'px';
+                img.style.height = size.y + 'px';
+            }
+        },
+
+        createShadow: function () {
+            var div = document.createElement('div');
+
+            this._setIconStyles(div, 'shadow');
+            return div;
+      }
+    });
+
+    L.AwesomeMarkers.icon = function (options) {
+        return new L.AwesomeMarkers.Icon(options);
+    };
+
+}(this, document));

BIN
static/logo.jpeg


+ 6 - 0
templates/contribute.html

@@ -0,0 +1,6 @@
+{% extends "layout.html" %}
+
+{% block content %}
+    <h2>Mitgestalten</h2>
+
+{% endblock %}

+ 26 - 0
templates/events.html

@@ -0,0 +1,26 @@
+{% extends "layout.html" %}
+
+{% block content %}
+    <h2>Events</h2>
+
+    {% for event in events %}
+      <div class="card mb-2">
+        <div class="card-body">
+          <h5 class="card-title">{{event.name}}</h5>
+          <h6 class="card-subtitle mb-2 text-muted">{{event.startDate}}</h6>
+
+          {% for key, val in event["data"].items() %}
+              {% if "url" in key %}
+                  <a href="{{val}}" class="card-link mb-0">{{val}}</a>
+              {% else %}
+                  <p class="card-text mb-0">{{key}}: {{val}}</p>
+              {% endif %}
+
+
+          {% endfor %}
+        </div>
+      </div>
+    {% endfor %}
+
+
+{% endblock %}

+ 6 - 0
templates/index.html

@@ -0,0 +1,6 @@
+{% extends "layout.html" %}
+
+{% block content %}
+<h2>Herzlich Willkommen bei Nach & Nach </h2>
+
+{% endblock %}

+ 74 - 0
templates/layout.html

@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1">
+        <title>Nach & Nach</title>
+        <link rel="icon" type="image/x-icon" href="static/favicon.ico">
+
+        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-kenU1KFdBIe4zVF0s0G1M5b4hcpxyD9F7jL+jjXkk+Q2h455rYXK/7HAuoJl+0I4" crossorigin="anonymous"></script>
+
+        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-rbsA2VBKQhggwzxH7pPCaAqO46MgnOM80zW1RWuH61DGLwZJEdK2Kadq2F9CUG65" crossorigin="anonymous">
+        <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" />
+        <link href="{{ url_for('static', filename='css/main.css') }}" rel="stylesheet">
+
+        <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js" integrity="sha512-rmZcZsyhe0/MAjquhTgiUcb4d9knaFc7b5xAfju483gbEXTkeJRUMIPk6s3ySZMYUHEcjKbjLjyddGWMrNEvZg==" crossorigin="anonymous"></script>
+        <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js"></script>
+
+        <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css"
+             integrity="sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI="
+             crossorigin=""/>
+        <script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"
+             integrity="sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM="
+             crossorigin=""></script>
+        <link rel="stylesheet" href="static/css/leaflet.awesome-markers.css">
+        <script src="static/js/leaflet.awesome-markers.js"></script>
+        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
+
+    </head>
+    <body>
+        <div class="container wrapper p-0">
+
+            <nav class="navbar nav nav-pills nav-fill sticky-top p-0 ps-0">
+                <div class="container-fluid text-center">
+                    {% block app_title %}
+                        <a class="navbar-brand" href="/">
+                        <img src="static/logo.jpeg" alt="Nach & Nach Logo"/>
+                        </a>
+                    {% endblock %}
+                </div>
+            </nav>
+
+
+            <div class="content p-2">
+                {% block content %}
+                {% endblock %}
+            </div>
+
+            <nav class="nav nav-pills nav-fill sticky-bottom pt-1">
+                <a class="nav-link pt-0 pb-0 p-0" href="/">
+                    <i class="bi bi-house-door" title="Start" aria-hidden="true"></i>
+                    <span>Start</span>
+                </a>
+                <a class="nav-link pt-0 pb-0 p-0" href="/karte">
+                    <i class="bi bi-geo-alt" title="Karte" aria-hidden="true"></i>
+                    <span>Karte</span>
+                </a>
+                <a class="nav-link pt-0 pb-0 p-0" href="/schritte">
+                    <i class="bi bi-list-check" title="Schritte" aria-hidden="true"></i>
+                    <span>Schritte</span>
+                </a>
+                <a class="nav-link pt-0 pb-0 p-0" href="/events">
+                    <i class="bi bi-calendar-day" title="Events" aria-hidden="true"></i>
+                    <span>Events</span>
+                </a>
+                <a class="nav-link pt-0 pb-0 p-0" href="/mitgestaltung">
+                    <i class="bi bi-people" title="Mitgestaltung" aria-hidden="true"></i>
+                    <span>Mitgestaltung</span>
+                </a>
+            </nav>
+
+        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js"></script>
+        </div>
+    </body>
+</html>

+ 60 - 0
templates/map.html

@@ -0,0 +1,60 @@
+{% extends "layout.html" %}
+
+{% block content %}
+<div id="map"></div>
+
+<script type="text/javascript">
+
+var osm = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
+    maxZoom: 19,
+    attribution: '© OpenStreetMap'
+});
+
+var tileUrl = 'https://tile.openstreetmap.org/{z}/{x}/{y}.png';
+var mapAttribution = '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>';
+
+var map = L.map('map', {
+    center: [39.73, -104.99],
+    zoom: 10,
+    layers: [osm]
+});
+
+var baseMaps = {"OpenStreetMap": osm};
+var overlayMaps = {};
+
+
+{% for type, group in marker_groups.items() %}
+    var grmarkers = [];
+
+    var iconSettings = L.AwesomeMarkers.icon({
+      icon: "{{ group['icon'] }}",
+      markerColor: "{{ group['color'] }}",
+      prefix: "fa"
+    });
+
+    {% for marker in group["liste"] %}
+        var marker = L.marker(
+            [{{ marker['long'] }}, {{ marker['lat'] }}],
+            {icon: iconSettings}
+        ).addTo(map)
+        .bindPopup("<span class='fw-bold'>{{ marker['name'] }}</span></br> {{marker['shortdescription']}} </br><a href='{{marker['url']}}'>{{marker['url']}}</a>")
+        .openPopup();
+
+        grmarkers.push(marker);
+
+    {% endfor %}
+
+    var grLayerGr = L.layerGroup(grmarkers);
+    overlayMaps["<i class='fa fa-{{group['icon']}} me-1' style='color: {{group['color']}}'></i>{{ type }}"] = grLayerGr;
+
+{% endfor %}
+var layerControl = L.control.layers(baseMaps, overlayMaps, {collapsed:false, hideSingleBase:true}).addTo(map);
+
+var checkboxes = document.getElementsByTagName("input");
+for (var i = 0; i < checkboxes.length; i++) {
+    if (checkboxes[i].type == "checkbox") {
+        checkboxes[i].checked = true;
+    }
+}
+</script>
+{% endblock %}

+ 20 - 0
templates/steps.html

@@ -0,0 +1,20 @@
+{% extends "layout.html" %}
+
+{% block content %}
+    <h2>Nach und nach zu mehr Nachhaltigkeit</h2>
+
+    {% for step in steps %}
+      <div class="card mb-2">
+        <div class="card-body">
+          <h5 class="card-title">{{step.title}}</h5>
+          <h6 class="card-subtitle mb-2 text-muted">{{step.subtitle}}</h6>
+            <p class="card-text mb-0">{{step.description}}</p>
+
+            {% if step.url %}
+                <a href="{{step.url}}" class="card-link mb-0">{{step.url}}</a>
+            {% endif %}
+        </div>
+      </div>
+    {% endfor %}
+
+{% endblock %}