Commit 3786087a authored by tigabeatz's avatar tigabeatz

init

parents
.ocfg
.idea/
.vscode/
.pycharm/
.db
__pycache__
sessionfinder.code-workspace
.env
localconf.json
spare_code.py
static/sidebar
static/markers
static/leaflet
static/fontawesome
flask_session/
https://creativecommons.org/licenses/by-sa/3.0/de/
\ No newline at end of file
This diff is collapsed.
# Manual
Find artists nearby who wish to collaborate by starting a new instant session. Or just look for a running session nearby.
1. Allow location detection or set a starting location by clicking on the map.
2. Within the blue markers popup, whether choose to just search, or to create an instant session.
3. Alternatively, if you are logged in, you may choose to schedule a session.
To see details of any session, move you pointing device to the marker or click on it for even more details.
Any session will be visible on the map for not longer than one hour. Start a new one if you are still in progress, so
others know it is still time left to join. ;-)
To schedule a session, instead to start one instantly, you need an Microsoft account for authentication. You can cancel your scheduled sessions
when logged in by selecting its marker on the map and clicking the cancel button within its popup.
Hint: Logout, open a new browser window, login again for every new session you like to schedule.
The screen will refresh every 5 minutes. Hit the refresh button of your browser to update in between this interval.
** Please note: Although you may log into the app, you are still not authorized. You may get authorized upon request or invitation. **
# About
I created this small web app to be used in the coming summer,
to find other musicians for spontaneous sessions and to learn about azure web apps.
## Self hosting
Actually the app is prepared to run as Azure Web App (Python)
Configuration must be set in localconf.json in project root:
{
"db_account_name": "",
"db_account_key": "",
"web_contact_info": "",
"web_legal_info": "",
"map_url": "https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png",
"map_attribution": "Kartendaten: OpenStreetMap, Mitwirkende, http://www.openstreetmap.org/copyright ",
"ad_consumer_key": "",
"ad_consumer_secret": "",
"ad_tenant_name": "",
"schedules_secret": ""
}
or may be set via environment variables.
Java script dependencies need to be included manually:
- .min css and js files from https://github.com/nickpeihl/leaflet-sidebar-v2
need to be copied into static/sb
- copy the dist/images directory, awesome-markers.css, and awesome-markers.js
from https://github.com/lvoogdt/Leaflet.awesome-markers to static/am
Python dependencies are included in requirements.txt.
On Azure deploy by git repo, otherwise the dependencies will not get build.
these files / folders need to be added:
.env
localconf.json
spare_code.py
static/sidebar
static/markers
static/leaflet
static/fontawesome
some manual changes with in the app services active directory may be needed.
remember the callback url for the MS oauth. as ssl happens outside of the app,
the callback url will be http only. needs to be manually overwritten, for now.
Users need to get managed within azure ad enterprise apps.
### Storage backends
Only azure tables actually
### Stored data
I try to collect as less data as possible within the app.
- unique instant session or scheduled session id,
- its position and type, the time of the request for one hour
- schedules also store an encrypted and hashed user identifier to allow for later editing through the user
- schedules are stored up to 7 days after creation
- logs and metrics mayy get collected, based upon the chosen provider
## Thanks to
- https://www.openstreetmap.de/
- https://stackoverflow.com/
- http://flask.pocoo.org/
- https://leafletjs.com/
- https://geopy.readthedocs.io/en/stable/
- https://python-markdown.github.io
- https://github.com/nickpeihl/leaflet-sidebar-v2
- https://github.com/lvoogdt/Leaflet.awesome-markers
- https://github.com/cicorias/python-flask-aad-v2
- https://github.com/microsoftgraph/python-sample-auth
- https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps
- https://dev.to/gimlet2/prometheus-python-flask-4ik0
from setuptools import setup
setup(
name='findsession',
version='',
packages=['sfds'],
url='',
license='',
author='tigabeatz',
author_email='',
description=''
)
This diff is collapsed.
"<div style=\"border:2px solid whitesmoke; min-width: max-content\" ><button onclick=\"sidebar.open('search');\">" +
"<div class=\"fa fa-search\"> </div> Search for sessions" + "</button> </div>" +
" " +
"<div style=\"border:2px solid whitesmoke; min-width: max-content\" > <button onclick=\"sidebar.open('sessions');\">" +
"<div class=\"fa fa-plus\"> </div> start a session instantly" + "</button> </div>" +
" " +
"<div style=\"border:2px solid whitesmoke; min-width: max-content\" ><button onclick=\"sidebar.open('schedules');\">" +
"<div class=\"fa fa-calendar\"> </div> or plan it upfront" + "</button> </div>"
;
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Kreaterra Services 4 Artists Launcher</title>
</head>
<body>
<iframe src="https://sessionfinder.azurewebsites.net/"
width="95%" height="100" name="sprichwort_des_tages"></iframe>
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<title>Session Finder</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> -->
<meta http-equiv="Refresh" content="300">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css" rel="stylesheet">
<!--
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.2/css/all.css" integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay" crossorigin="anonymous">
-->
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" crossorigin=""/>
<!-- <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/> -->
<link rel="stylesheet" href="{{ url_for('static',filename='sidebar/leaflet-sidebar.min.css') }}" />
<link rel="stylesheet" href="{{ url_for('static',filename='markers/leaflet.awesome-markers.css') }}">
<style>
body {
padding: 0;
margin: 0;
}
html, body, #map {
height: 100%;
width: 100vw;
font: 10pt "Helvetica Neue", Arial, Helvetica, sans-serif;
}
sf_forms {
width: 30vw;
}
input {
color: #0074d9;
margin: 1px;
}
span {
float: right;
clear: both;
}
label {
margin: 1px;
}
input[type=submit] {
background:#0074d9;
border:0 none;
color: #fff;
margin: 5px;
float: right;
clear: both;
}
input[type=submit]:hover {
background: #1597eb;
}
button {
border:0 none;
background: #1597eb;
margin: 3px;
}
</style>
</head>
<body>
{% include 'sf_sidebar.html' %}
{% include 'sf_map.html' %}
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Session Finder Error Message</title>
</head>
<body>
<h1>oops, something went wrong</h1>
<p>{{ error }}</p>
</body>
</html>
\ No newline at end of file
<form class="sf_forms" action="/sessions/add" >
<span><label for="schedule_lng">Longitude</label>
<input type="text" id="schedule_lng" name="longitude" value="{{ longitude }}">
</span><br>
<span><label for="schedule_lat">Latitude</label>
<input type="text" id="schedule_lat" name="latitude" value="{{ latitude }}">
</span><br>
<!-- time format: 2019-06-03T21:37 -->
<span><label for="schedule_when">When</label>
<input type="datetime-local" id="schedule_when" name="schedule_when" value="2019-06-01T00:24" min="" max="">
</span><br>
<span><label for="type">Type of Session</label>
<select name="type">
<option value="music">Music</option>
<option value="yoga">Yoga</option>
<option value="flow">Flow</option>
<option value="paint">Paint</option>
<option value="code">Code</option>
</select></span>
<br><br>
<span><label for="distance">Show sessions in a distance of</label>
<select name="distance">
<option value="3">3 Km</option>
<option value="5">5 Km</option>
<option value="10">10 Km</option>
<option value="15">15 Km</option>
<option value="10000">Almost No Limit</option>
</select></span>
<br>
<br>
<span>
<input type="hidden" name="create" value="schedule" readonly>
<input type="submit" value="Submit"> <br><br>
<p><b><div id="schedule_adr"></div></b>
</p></span>
</form>
\ No newline at end of file
<form class="sf_forms" action="/sessions" >
<span><label for="search_lng">Longitude</label>
<input type="text" id="search_lng" name="longitude" value="{{ longitude }}">
</span>
<br>
<span><label for="search_lat">Latitude</label>
<input type="text" id="search_lat" name="latitude" value="{{ latitude }}">
</span>
<br>
<span><label for="distance">Show sessions in a distance of</label>
<select name="distance">
<option value="3">3 Km</option>
<option value="5">5 Km</option>
<option value="10">10 Km</option>
<option value="15">15 Km</option>
<option value="10000">Almost No Limit</option>
</select></span>
<br>
<br>
<span><input type="submit" value="Submit">
<br>
<br>
<b><div id="search_adr"></div></b>
</span>
</form>
\ No newline at end of file
<form class="sf_forms" action="/sessions/add" > <!-- method="post" enctype="multipart/form-data" -->
<br>
<span><label for="lng">Longitude</label>
<input type="text" id="session_lng" name="longitude" value={{ longitude }}></span>
<br>
<span><label for="lat">Latitude</label>
<input type="text" id="session_lat" name="latitude" value={{ latitude }}></span>
<br>
<br>
<span><label for="type">Type of Session</label>
<select name="type">
<option value="music">Music</option>
<option value="yoga">Yoga</option>
<option value="flow">Flow</option>
<option value="paint">Paint</option>
<option value="code">Code</option>
</select></span>
<br>
<span><label for="distance">Show sessions in a distance of</label>
<select name="distance">
<option value="3">3 Km</option>
<option value="5">5 Km</option>
<option value="10">10 Km</option>
<option value="15">15 Km</option>
<option value="10000">Almost No Limit</option>
</select></span>
<br>
<br>
<span>
<input type="hidden" name="create" value="session" readonly>
<input type="submit" value="Submit">
<br>
<br>
<b><div id="session_adr"></div></b>
</span>
</form>
\ No newline at end of file
// on click or auto detect
// the popup for the marker
var address = ' ... ';
var myposmarker;
function formpop(e) {
var lat = e.latlng.lat;
var lng = e.latlng.lng;
var url = '/getadress?latitude=' + lat + '&longitude=' + lng;
function adresser(addr) {
// update forms
document.getElementById('search_lat').value = lat;
document.getElementById('search_lng').value = lng;
document.getElementById('search_adr').innerHTML = addr;
document.getElementById('session_lat').value = lat;
document.getElementById('session_lng').value = lng;
document.getElementById('session_adr').innerHTML = addr;
document.getElementById('schedule_lat').value = lat;
document.getElementById('schedule_lng').value = lng;
document.getElementById('schedule_adr').innerHTML = addr;
// console.log(addr);
address = addr;
// update values within the popup
//document.getElementById('popup_lat').value = lat;
//document.getElementById('popup_lng').value = lng;
//document.getElementById('popup_adr').innerHTML = addr;
};
// remove the old users marker if clicked on the map
if (myposmarker != undefined) {
map.removeLayer(myposmarker);
};
// the popup for the marker
var formpopup = {{ form_session|safe }}
myposmarker = L.marker(e.latlng).addTo(map).bindPopup(formpopup).openPopup();
// getting position address
fetch(url)
.then(function(response) {
return response.json();
})
.then(function(myJson) {
adresser(JSON.stringify(myJson.address));
});
};
\ No newline at end of file
// add hotspots
L.marker([50.011473095343405,8.267417908741615],
{
icon: HotSpotMarker}
).addTo(map)
.bindTooltip(
"Hotspot 4 Music: B´s Terrace"
).bindPopup(
"B´s Terrace, a hotspot for low volume handcrafted music. "
);
// add hotspots
L.marker([50.0103562877157,8.255104821301085],
{
icon: HotSpotMarker}
).addTo(map)
.bindTooltip(
"Hotspot: Kinder´s Garden"
).bindPopup(
"Kinder´s Garden, a hotspot for low volume handcrafted music, sports, yoga, painting, juggling, flowing, ... "
);
<div id="map"></div>
<!-- <script src="https://unpkg.com/[email protected]/dist/leaflet.js" integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og==" crossorigin=""></script> -->
<script src="https://unpkg.com/leaflet/dist/leaflet.js" crossorigin=""></script>
<script src="{{ url_for('static',filename='sidebar/leaflet-sidebar.min.js') }}"></script>
<script src="{{ url_for('static',filename='markers/leaflet.awesome-markers.js') }}"></script>
<script>
// leaflet map setup
var streetsde = L.tileLayer('{{de_map_url}}', {
'maxZoom': 18,
'useCache': false,
'attribution': '{{de_map_attribution}}'
});
/*
var streetsen = L.tileLayer('{{en_map_url}}', {
'maxZoom': 18,
'useCache': false,
'attribution': '{{en_map_attribution}}'
});
*/
/*
var temperature = L.tileLayer('{{temp_map_url}}', {
'maxZoom': 18,
'useCache': true
});
var wind = L.tileLayer('{{wind_map_url}}', {
'maxZoom': 18,
'useCache': true
});
*/
/* var rain = L.tileLayer('{{rain_map_url}}', {
'maxZoom': 18,
'useCache': false
});
*/
// var map = L.map('map').fitWorld();
var map = L.map('map', {
center: [50.0, 8.0],
zoom: 10,
layers: [
streetsde,
//temperature,
//streetsen,
//wind,
//rain
]
});
var baseMaps = {
"Streets DE": streetsde,
// "Streets OSM": streetsen
};
var overlayMaps = {
//"Temperature": temperature,
//"Wind": wind,
//"Rain": rain
};
L.control.layers(baseMaps, overlayMaps).addTo(map);
// click sets position and opens menu
map.on('click',
function(e){
formpop(e);}
);
function getUrlVars() {
var vars = {};
var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
vars[key] = value;
});
return vars;
};
if (getUrlVars()["latitude"]) {
try {
map.setView([getUrlVars()["latitude"], getUrlVars()["longitude"]], 12);
} catch(err) {
window.location = window.location.href.split("?")[0];
// click sets position and opens menu
map.on('click',
function(e){
alert('clicked');
formpop(e);}
);
}
} else {
map.locate({setView: false, maxZoom: 18});
map.on('locationfound', onLocationFound);
};
// create the sidebar instance
var sidebar = L.control.sidebar({ container: 'sidebar' }).addTo(map);
//.open('home')
// add the form popup
{% include 'sf_formpopup.html' %}
// set user location if given by parameter, or try to detect location
function onLocationFound(e) {
formpop(e);
};
// add markers
{% include 'sf_markers.html' %}
// add hotspots
{% include 'sf_hotspots.html' %}
// add sessions
{% include 'sf_sessions.html' %}
// add latest submitted session
var latestsession = JSON.parse('{{latest|safe}}');
L.circle([latestsession['lat'],latestsession['lon']],12).addTo(map).bindTooltip( 'latest session, type: ' + latestsession['type']);
</script>
// create markers for session categories
L.AwesomeMarkers.Icon.prototype.options.prefix = 'fa';
// Creates a red marker with the coffee icon
var musicMarker = L.AwesomeMarkers.icon({
icon: 'music',
markerColor: 'orange'
});
// Creates a red marker with the coffee icon
var yogaMarker = L.AwesomeMarkers.icon({
icon: 'heart',
markerColor: 'green'
});
// Creates a red marker with the coffee icon
var flowMarker = L.AwesomeMarkers.icon({
icon: 'leaf',
markerColor: 'blue'
});
// Creates a red marker with the coffee icon
var paintMarker = L.AwesomeMarkers.icon({
icon: 'leaf',
markerColor: 'red'
});
// Creates a red marker with the coffee icon
var codeMarker = L.AwesomeMarkers.icon({
icon: 'code',
markerColor: 'black'
});
// Creates a red marker with the coffee icon
var HotSpotMarker = L.AwesomeMarkers.icon({
icon: 'users',
markerColor: 'purple'
});
\ No newline at end of file
// add session markers
{% for item in sessions %}
L.marker([
{{item['lat']|safe }},
{{item['lon']|safe }}
],
{
icon: {{item['type']|safe }}Marker}
).addTo(map)
.bindTooltip(
"{{item['type']|safe }} <br> {{item['schedule_when']|safe }} "
).bindPopup(
{% if not username == '' %}
{% if item['managed_by'] == session['managed_by'] %}