added upload and messages

geo
Alexander Roidl 2 years ago
parent 83dc392cac
commit 389f9b71cb

BIN
.DS_Store vendored

Binary file not shown.

@ -0,0 +1,7 @@
FROM tiangolo/uwsgi-nginx-flask:python3.8-alpine
RUN apk --update add bash nano
ENV STATIC_URL /static
ENV STATIC_PATH /var/www/app/static
COPY ./requirements.txt /var/www/requirements.txt
RUN pip install -r /var/www/requirements.txt

BIN
app/.DS_Store vendored

Binary file not shown.

@ -6,33 +6,26 @@ from wtforms import Form as NoCsrfForm
from wtforms.fields import StringField, FormField, SubmitField, SelectField, RadioField
from app.models import Location, LocationSchema
from wtforms.fields import DecimalRangeField
from wtforms.widgets import TextArea
# - - - Forms - - -
# class AuthorForm(NoCsrfForm):
# # this forms is never exposed so we can user the non CSRF version
# # this forms is never exposed so we can use the non CSRF version
# author_name = StringField('Author Name', validators=[DataRequired()])
# class UploadForm(FlaskForm):
# title = StringField('title', validators=[InputRequired()])
# author = FieldList(FormField(AuthorForm, default=lambda: Author()), min_entries=1)
# category = StringField('category', validators=[InputRequired()])
# year_published = StringField('year published', [validators.Length(max=4)],default=None)
# file = FileField()
# upload = SubmitField(label='Upload')
# wish = SubmitField(label='''I don't have the file, but wish I did.''')
# message = StringField('message', default=None)
# sameness = DecimalRangeField('sameness', default=0)
# diversity = DecimalRangeField('diversity', default=0)
# gender = DecimalRangeField('gender', default=50)
# choices = [('Student', 'Student'),
# ('Librarian', 'Librarian'),
# ('Pirate', 'Pirate'),
# ('Teacher', 'Teacher'),
# ('Institution', 'Institution'),
# ('All of the above', 'All of the above'),
# ('None of the above', 'None of the above')]
# who = SelectField('', choices=choices, default='Student')
class UploadText(FlaskForm):
message = StringField('message', widget=TextArea(), default=None)
longitude = DecimalRangeField('longitude', default=0)
latitude = DecimalRangeField('latitude', default=0)
class UploadAudio(FlaskForm):
message = StringField('message', widget=TextArea(), default=None)
audio = FileField()
longitude = DecimalRangeField('longitude', default=0)
latitude = DecimalRangeField('latitude', default=0)
# class EditForm(FlaskForm):

@ -25,6 +25,9 @@ class Location(db.Model):
id = db.Column(db.Integer, primary_key = True)
longitude = db.Column(db.Numeric(10,8))
latitude = db.Column(db.Numeric(10,8))
loc_type = db.Column(db.String(200))
message = db.Column(db.String(4000))
audio = db.Column(db.String(255))
# title = db.Column(db.String(255))
# file = db.Column(db.String(255))
@ -50,9 +53,12 @@ class Location(db.Model):
# who = db.Column(db.String(255))
def __init__(self, longitude, latitude):
def __init__(self, longitude, latitude, loc_type, message, audio):
self.longitude = longitude
self.latitude = latitude
self.loc_type = loc_type
self.message = message
self.audio = audio
# def __repr__(self):
# return '<Title %r>' % self.title
@ -66,6 +72,9 @@ class LocationSchema(Schema):
id = fields.Int(dump_only=True)
longitude = fields.Float()
latitude = fields.Float()
loc_type = fields.String()
message = fields.String()
audio = fields.String()
def must_not_be_blank(data):

@ -0,0 +1,12 @@
{% macro render_field(field) %}
<dt>{{ field.label }}
<dd>{{ field(**kwargs)|safe }}
{% if field.errors %}
<ul class=errors>
{% for error in field.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
</dd>
{% endmacro %}

@ -1,129 +0,0 @@
{% extends 'base.html' %}
{% block main %}
{% from "_formhelpers.html" import render_field %}
<head>
<script>
function outputUpdate(sameness) {
document.querySelector('#selected-sameness').value = sameness;
}
function outputUpdate2(diversity) {
document.querySelector('#selected-diversity').value = diversity;
}
function outputUpdate3(gender) {
document.querySelector('#selected-gender').value = gender;
}
</script>
</head>
<div class="container" style="float: left; width:50%;">
<div style="width: 98%; border-right: dashed; border-width: 1px;">
<h1 class="page-header">Add Book</h1>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-danger">
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('add_book') }}" enctype=multipart/form-data>
{{ form.csrf_token }}
<div class="form-group">Title:* <br> {{ form.title (size=50, class="form-control") }}</div>
<br>
<div data-toggle="fieldset" id="phone-fieldset">
Author(s):* <button type="button" data-toggle="fieldset-add-row" data-target="#phone-fieldset">+</button>
<table>
<tr>
<th></th>
<th></th>
</tr>
{% for author in form.author %}
<tr data-toggle="fieldset-entry">
<td>{{ author.author_name (size=50)}}</td>
<td><button type="button" data-toggle="fieldset-remove-row" id="phone-{{loop.index0}}-remove">-</button></td>
</tr>
{% endfor %}
</table>
</div>
<br>
Category:* <br> {{ form.category(size=50, class="form-control") }} <br><br>
Year published: <br> {{ form.year_published(size=8, class="form-control") }} <br><br>
How different is this item to the rest of the collection?
Or is it more of the same? <br>
{{ form.sameness(min=0, max=100, oninput="outputUpdate(value)") }} &nbsp;
<span style="color: #d3d3d3;"><output for="sameness" id="selected-sameness">{{ form.sameness.data }} </output> % different</span>
<br><hr align="left" style="width:96%;"><br>
Check the bibliography. How diverse are the references in this book? <br>
{{ form.diversity(min=0, max=100, oninput="outputUpdate2(value)") }} &nbsp;
<span style="color: #d3d3d3;"><output for="diversity" id="selected-diversity">{{ form.diversity.data }} </output> % diverse</span>
<br><hr align="left" style="width:96%;"><br>
Check the writing. Who is speaking? Is the voice more often male or female? <br>
{{ form.gender(min=1, max=100, oninput="outputUpdate3(value)") }} &nbsp;
<span style="color: #d3d3d3;"><output for="diversity" id="selected-gender">{{ form.gender.data }} </output> % female</span>
<br><hr align="left" style="width:96%;"><br>
Who are you? {{ render_field(form.who) }}
<br><hr align="left" style="width:96%;"><br>
<div style="width: 40%;">
Add a message for future readers: {{ form.message(size=90, class="form-control") }}
<br></div>
<br>
{{ form.file }}
{{ form.upload }}
{{ form.wish }}
</div>
</form>
</div>
<div>
<table class="library_table" id="table" style="width:30% padding:10px; padding-bottom: 400px;" >
<thead>
<tr id="header" style="height:15px;">
<th style="width: 10%;"> <h5> Currently in the library </h5></th>
<th style="width: 20%;"></th>
</tr>
</thead>
<tbody>
<tr>
<td> Titles: </td>
<td> {{ books_all }}</td>
</tr>
<tr>
<td> Authors: </td>
<td> {{ authors_all }} </td>
</tr>
<tr>
<td> Categories: </td>
<td> {{ categories|replace('[', '')|replace(']', '') }}</td>
</tr>
<tr>
<td> Stacks: </td>
<td> {{ stacks_all|replace('[', '')|replace(']', '') }}</td>
</tr>
<tr>
<td> From the years: </td>
<td> {{earliest}} {{latest}}</td>
</tr>
<tr>
<td> Gaps in the collection: </td>
<td> At least {{ books_potential }} potential books missing</td>
</tr>
</tbody>
</table>
</div>
</div>
<clear>
{% endblock %}

@ -0,0 +1,108 @@
{% extends 'base.html' %}
{% block main %}
{% from "_formhelpers.html" import render_field %}
<h1 class="page-header">Add Message</h1>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-danger">
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('addaudio') }}" enctype=multipart/form-data>
{{ form.csrf_token }}
<input type="hidden" name="longitude" value="{{ longitude }}" />
<input type="hidden" name="latitude" value="{{ latitude }}" />
<div style="width: 40%;">
message: {{ form.message(cols="45", rows="10", class="form-control") }}
</div>
<div id="audio">
<a onclick="record_audio()" href="#">record</a>
<a onclick="stop_audio()" href="#">stop</a>
<span id="seconds_rec"></span><span> seconds</span>
<div id="audio-player-container"></div>
</div>
<input type="file" name="file" id="uploadedFile" accept="audio/*"><br>
<button type="submit">Submit</button>
</form>
<clear>
{% endblock main %}
{% block js%}
<script>
var mediaRecorder;
var seconds_rec = 0;
var seconds_int;
function record_audio(){
seconds_int = setInterval(
function () {
document.getElementById("seconds_rec").innerHTML = seconds_rec;
seconds_rec += 1;
}, 1000);
navigator.mediaDevices.getUserMedia({ audio: true })
.then(stream => {
mediaRecorder = new MediaRecorder(stream);
mediaRecorder.start();
const audioChunks = [];
mediaRecorder.addEventListener("dataavailable", event => {
audioChunks.push(event.data);
});
mediaRecorder.addEventListener("stop", () => {
const audioBlob = new Blob(audioChunks);
const audioUrl = URL.createObjectURL(audioBlob);
var sound = document.createElement('audio');
sound.id = 'audio-player';
sound.controls = 'controls';
sound.src = audioUrl;
console.log(audioUrl)
sound.type = 'audio/ogg';
document.getElementById("audio-player-container").innerHTML = sound.outerHTML;
const audio = new Audio(audioUrl);
//audio.play();
let file = new File([audioBlob], "audio.ogg",{type:"audio/ogg"});
let container = new DataTransfer();
container.items.add(file);
document.getElementById("uploadedFile").files = container.files;
//const audio = new Audio(audioUrl);
//audio.play();
});
setTimeout(() => {
stop_audio()
}, 1000 * 60);
});
}
function stop_audio(){
clearInterval(seconds_int);
mediaRecorder.stop();
}
</script>
{% endblock js%}

@ -0,0 +1,33 @@
{% extends 'base.html' %}
{% block main %}
{% from "_formhelpers.html" import render_field %}
<h1 class="page-header">Add Message</h1>
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="alert alert-danger">
<ul>
{% for message in messages %}
<li>{{ message }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endwith %}
<form method="POST" action="{{ url_for('addtext') }}" enctype=multipart/form-data>
{{ form.csrf_token }}
<input type="hidden" name="longitude" value="{{ longitude }}" />
<input type="hidden" name="latitude" value="{{ latitude }}" />
<div style="width: 40%;">
message: {{ form.message(cols="45", rows="10", class="form-control") }}
</div>
<button type="submit">Submit</button>
</form>
<clear>
{% endblock main %}

@ -43,144 +43,12 @@
</div>
</footer>
{% block js %} {% endblock%}
<script src="{{ url_for("static", filename="js/jquery-3.3.1.min.js") }}"></script>
<script src="{{ url_for("static", filename="js/app.js") }}"></script>
{% block js %} {% endblock%}
<script>
var locations;
function assign_data(data) {
console.log(data);
locations = data;
}
assign_data({{ data_locations|tojson }});
var map = L.map('map', {zoomControl:false,
attributionControl:false,
scrollWheelZoom: false,
zoom: 2000
}).setView([48.505, 1.09], 10);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
zoom:2,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
locations.forEach(function (item, index) {
var circle = L.circle([item.latitude, item.longitude], {
color: 'red',
fillColor: 'red',
fillOpacity: 1,
radius: 30
}).addTo(map);
});
var myPositionCircle = L.circle([48.172934, 9.01236], {
color: 'black',
fillColor: 'black',
fillOpacity: 1,
radius: 30
}).addTo(map);
var x = document.getElementById("position");
let id;
let target;
let options;
//first: latitude, second: longitude
target = {
latitude : 48.172934,
longitude: 9.01236
};
var my_location = {
latitude : 0,
longitude: 0
};
function success(pos) {
const crd = pos.coords;
my_location.longitude = crd.longitude;
my_location.latitude = crd.latitude;
map.setView([my_location.latitude, my_location.longitude], 16);
myPositionCircle.setLatLng([my_location.latitude, my_location.longitude])
console.log("updated")
x.innerHTML = "Latitude: " + crd.latitude +
"<br>Longitude: " + crd.longitude +
"<br>Distance: " + distance(target.longitude, target.latitude, crd.longitude, crd.latitude);
// if we want to clear the watch
// if (target.latitude === crd.latitude && target.longitude === crd.longitude) {
// console.log('Congratulations, you reached the target');
// navigator.geolocation.clearWatch(id);
// }
}
function error(err) {
console.error(`ERROR(${err.code}): ${err.message}`);
console.log("trying again")
id = navigator.geolocation.watchPosition(success, error, options);
}
options = {
enableHighAccuracy: false,
timeout: 5000,
maximumAge: 0
};
id = navigator.geolocation.watchPosition(success, error, options);
function distance(lon1, lat1, lon2, lat2) {
var R = 6371; // Radius of the earth in km
var dLat = (lat2-lat1).toRad(); // Javascript functions in radians
var dLon = (lon2-lon1).toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c; // Distance in km
return d;
}
/** Converts numeric degrees to radians */
if (typeof(Number.prototype.toRad) === "undefined") {
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
}
function store_location(){
console.log("storing location");
console.log(my_location)
var xhr = new XMLHttpRequest();
xhr.open("POST", "/addlocation", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
longitude: my_location.longitude,
latitude: my_location.latitude
}));
location.reload();
}
</script>
</body>
</html>

@ -5,14 +5,186 @@
<h1 class="header" id="title">GEO</h1>
<p id="position"></p>
<button id="store_location" onclick="store_location()">store my position</button>
<button id="add_text" onclick="add_text()">add message</button>
<button id="add_audio" onclick="add_audio()">add audio</button>
<br><br><br>
{% for location in locations %}
<div >{{location.id}}: {{location.longitude}}, {{location.latitude}} <a href="location/{{location.id}}/delete">delete</a></div>
<div >{{location.id}}: {{location.longitude}}, {{location.latitude}} {{location.message if location.loc_type == "message"}}
{% if location.loc_type == "audio" %}
<audio id="audio-player" controls="" src="uploads/{{location.audio}}"></audio>
{% endif %}
<a href="location/{{location.id}}/delete">delete</a></div>
{% endfor %}
</div>
{% endblock %}
{% endblock main %}
{% block js %}
<script>
var locations;
function assign_data(data) {
console.log(data);
locations = data;
}
assign_data({{ data_locations|tojson }});
var map = L.map('map', {zoomControl:false,
attributionControl:false,
scrollWheelZoom: false,
zoom: 2000
}).setView([48.505, 1.09], 10);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
zoom:2,
attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
locations.forEach(function (item, index) {
try {
var circle = L.circle([item.latitude, item.longitude], {
color: 'red',
fillColor: 'red',
fillOpacity: 1,
radius: 30
}).addTo(map);
} catch (error) {
console.error(error);
}
});
var myPositionCircle = L.circle([48.172934, 9.01236], {
color: 'black',
fillColor: 'black',
fillOpacity: 1,
radius: 30
}).addTo(map);
var x = document.getElementById("position");
let id;
let target;
let options;
//first: latitude, second: longitude
target = {
latitude : 48.172934,
longitude: 9.01236
};
var my_location = {
latitude : 0,
longitude: 0
};
function success(pos) {
const crd = pos.coords;
my_location.longitude = crd.longitude;
my_location.latitude = crd.latitude;
map.setView([my_location.latitude, my_location.longitude], 16);
myPositionCircle.setLatLng([my_location.latitude, my_location.longitude])
console.log("updated")
x.innerHTML = "Latitude: " + crd.latitude +
"<br>Longitude: " + crd.longitude +
"<br>Distance: " + distance(target.longitude, target.latitude, crd.longitude, crd.latitude);
// if we want to clear the watch
// if (target.latitude === crd.latitude && target.longitude === crd.longitude) {
// console.log('Congratulations, you reached the target');
// navigator.geolocation.clearWatch(id);
// }
}
function error(err) {
console.error(`ERROR(${err.code}): ${err.message}`);
console.log("trying again")
// id = navigator.geolocation.watchPosition(success, error, options);
}
options = {
enableHighAccuracy: true,
timeout: 1000,
maximumAge: 1000
};
id = navigator.geolocation.watchPosition(success, error, options);
function distance(lon1, lat1, lon2, lat2) {
var R = 6371; // Radius of the earth in km
var dLat = (lat2-lat1).toRad(); // Javascript functions in radians
var dLon = (lon2-lon1).toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c; // Distance in km
return d;
}
/** Converts numeric degrees to radians */
if (typeof(Number.prototype.toRad) === "undefined") {
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
}
function store_location(){
console.log("storing location");
console.log(my_location)
var xhr = new XMLHttpRequest();
xhr.open("POST", "./addlocation", true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({
longitude: my_location.longitude,
latitude: my_location.latitude
}));
xhr.onreadystatechange=function(){
if (xhr.readyState==4 && xhr.status==200){
location.reload();
}
}
}
function add_text(){
window.location.href = "addtext?longitude="+ my_location.longitude +"&latitude="+my_location.latitude;
}
function add_audio(){
window.location.href = "addaudio?longitude="+ my_location.longitude +"&latitude="+my_location.latitude;
}
</script>
{% endblock js %}

@ -14,6 +14,7 @@ from sqlalchemy.sql.expression import func, select
from sqlalchemy.sql import except_
from sqlalchemy.ext.serializer import loads, dumps
# from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm
from app.forms import UploadText, UploadAudio
from app.models import Location, LocationSchema
from app.cover import get_cover
from app.getannot import get_annotations, get_annot_results, get_annot_book
@ -21,7 +22,7 @@ from urllib.parse import quote as urlquote
from app.extractText import extract_text
from os import environ
from flask_socketio import SocketIO, emit
import datetime
from datetime import datetime
import time
from csv import DictWriter, DictReader
import io
@ -44,6 +45,11 @@ def allowed_file(filename):
# Routing for your application.
###
@app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
@app.route('/', methods= ['POST','GET'])
def home():
@ -71,13 +77,69 @@ def hello(name):
def add_location():
if request.method == 'POST':
data = request.get_json()
print("UPDATING DATA")
print(data['longitude'])
location = Location(data['longitude'],data['latitude'])
location = Location(data['longitude'],data['latitude'], "mosquito", "", "")
db.session.add(location)
db.session.commit()
return redirect(url_for('home'))
@app.route('/addtext', methods=['POST', 'GET'])
def addtext():
upload_form = UploadText()
longitude = request.args.get('longitude')
latitude = request.args.get('latitude')
print(longitude)
print(latitude)
if request.method == 'POST':
if upload_form.validate_on_submit():
#get data from form
message = upload_form.message.data
longitude = upload_form.longitude.data
latitude = upload_form.latitude.data
location = Location(longitude,latitude, "message", message, "");
db.session.add(location)
db.session.commit()
return redirect(url_for('home'))
return render_template('addtext.html', form=upload_form, longitude=longitude, latitude=latitude)
@app.route('/addaudio', methods=['POST', 'GET'])
def addaudio():
upload_form = UploadAudio()
if request.method == 'GET':
longitude = request.args.get('longitude')
latitude = request.args.get('latitude')
if request.method == 'POST':
if upload_form.validate_on_submit():
#get data from form
message = upload_form.message.data
longitude = upload_form.longitude.data
latitude = upload_form.latitude.data
file = request.files['file']
print(file.filename)
locations = db.session.query(Location).all()
id = len(locations)+1
# Getting the current date and time
dt = datetime.now()
# getting the timestamp
ts = datetime.timestamp(dt)
filename = str(id) + "_" + str(ts) +"_"+ secure_filename(file.filename)
fullpath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(fullpath)
#add to database
location = Location(longitude,latitude, "audio", message, filename);
db.session.add(location)
db.session.commit()
return redirect(url_for('home'))
return render_template('addaudio.html', form=upload_form, longitude=longitude, latitude=latitude)
@app.route('/location/<int:id>/delete', methods=['POST', 'GET'])
def delete_location(id):
if request.method == 'GET':

@ -0,0 +1 @@
from app import app

@ -0,0 +1,13 @@
click==8.1.3
Flask==2.2.2
Flask_SocketIO==5.3.1
flask_sqlalchemy==3.0.2
Flask_WTF==1.0.1
marshmallow==3.18.0
PyPDF2==2.11.1
python-dotenv==0.21.0
requests==2.28.1
SQLAlchemy==1.4.42
Wand==0.6.10
Werkzeug==2.2.2
WTForms==3.0.1

@ -1,3 +1,3 @@
#! /usr/bin/env python
from app import app, socketio
socketio.run(app,host="0.0.0.0", port=8080, ssl_context='adhoc')
socketio.run(app,host="0.0.0.0", port=8080)

@ -0,0 +1,6 @@
#!/bin/bash
app="geo.app"
docker build -t ${app} .
docker run --platform linux/amd64 -p 56733:80 \
--name=${app} \
-v $PWD:/app ${app}

@ -0,0 +1,5 @@
[uwsgi]
module = main
callable = app
master = true
Loading…
Cancel
Save