Merge branch 'develop' into 'master'
Develop Closes #91 See merge request vtmk/web2.0!96
This commit is contained in:
@@ -1,23 +0,0 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from coffee_scale import mqtt
|
||||
|
||||
|
||||
class CoffeeScaleConfig(AppConfig):
|
||||
name = 'coffee_scale'
|
||||
|
||||
def ready(self):
|
||||
if ('makemigrations' in sys.argv or 'migrate' in sys.argv):
|
||||
return
|
||||
|
||||
try:
|
||||
logging.info('Connecting to MQTT (coffee scale) at {}...'.format(mqtt.HOST))
|
||||
logging.info('If there is no confirmation, the MQTT connection has probably failed.')
|
||||
mqtt.client.connect_async(mqtt.HOST, mqtt.PORT, 60)
|
||||
mqtt.client.loop_start()
|
||||
except Exception as ex:
|
||||
logging.error(ex)
|
||||
logging.error('Failed to connect to MQTT at {}'.format(mqtt.HOST))
|
||||
@@ -1,58 +0,0 @@
|
||||
import paho.mqtt.client as mqtt
|
||||
import logging
|
||||
import datetime
|
||||
from collections import deque
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
HOST = settings.MQTT_SETTINGS['HOST']
|
||||
PORT = settings.MQTT_SETTINGS['PORT']
|
||||
TOPICS = settings.MQTT_SETTINGS['TOPICS']
|
||||
latest = {}
|
||||
|
||||
|
||||
def on_connect(client, userdata, flags, rc):
|
||||
logging.info('Connected successfully to MQTT.')
|
||||
logging.info('Subscribing to all topics on {}.'.format(HOST))
|
||||
client.subscribe('sik/kiltahuone/kahvivaaka/#')
|
||||
|
||||
|
||||
def update_latest(msg):
|
||||
payload = msg.payload.decode('utf-8')
|
||||
if msg.topic == TOPICS['WEIGHT']:
|
||||
weight = float(payload)
|
||||
latest['weight'] = weight
|
||||
elif msg.topic == TOPICS['CUPS']:
|
||||
cups = float(payload)
|
||||
latest['cups'] = cups
|
||||
elif msg.topic == TOPICS['BREWING']:
|
||||
brewing = bool(int(payload))
|
||||
latest['brewing'] = brewing
|
||||
elif msg.topic == TOPICS['BREW_TIME']:
|
||||
brew_time = datetime.datetime.fromtimestamp(float(payload))
|
||||
latest['brew_time'] = brew_time
|
||||
|
||||
|
||||
def on_message(client, userdata, msg):
|
||||
try:
|
||||
update_latest(msg)
|
||||
except Exception as ex:
|
||||
logging.exception('Failed to parse MQTT payload.')
|
||||
|
||||
|
||||
def on_disconnect(client, userdata, rc):
|
||||
if rc != 0:
|
||||
logging.warning('MQTT unexpectedly disconnected.')
|
||||
else:
|
||||
client.loop_stop(force=False)
|
||||
logging.warning('MQTT disconnected.')
|
||||
|
||||
|
||||
def get_latest():
|
||||
return latest
|
||||
|
||||
|
||||
client = mqtt.Client()
|
||||
client.on_connect = on_connect
|
||||
client.on_message = on_message
|
||||
client.on_disconnect = on_disconnect
|
||||
@@ -47,7 +47,7 @@ body {
|
||||
background: green;
|
||||
border-radius: 10px;
|
||||
}
|
||||
#brewtime{
|
||||
.brewtime{
|
||||
text-align:right;
|
||||
position:absolute;
|
||||
right:0px;
|
||||
@@ -62,10 +62,13 @@ body {
|
||||
font-size:4vw;
|
||||
color: #333;
|
||||
}
|
||||
.layertwo{
|
||||
display: None;
|
||||
}
|
||||
noscript{
|
||||
color:red;
|
||||
}
|
||||
#text{
|
||||
.text{
|
||||
color:green;
|
||||
position:absolute;
|
||||
top:50%;
|
||||
@@ -102,9 +105,9 @@ noscript{
|
||||
}
|
||||
@keyframes coffeeready {
|
||||
0% {background-color:white;}
|
||||
25% {background-color:green;}
|
||||
25% {background-color:rgb(100, 255, 100);}
|
||||
50% {background-color:white;}
|
||||
75% {background-color:green;}
|
||||
75% {background-color:rgb(100, 255, 100);}
|
||||
100% {background-color:white;}
|
||||
}
|
||||
@keyframes unknown {
|
||||
|
||||
+142
-111
@@ -1,130 +1,161 @@
|
||||
var len = 0;
|
||||
var lastBrew = "∞";
|
||||
var brewtext = "";
|
||||
//Inner state
|
||||
var lastBrew = new Date(0);
|
||||
var brewing = false;
|
||||
var backoff = 2000;
|
||||
|
||||
$(document).ready(function(){
|
||||
$('#text').bind("DOMSubtreeModified", resize);
|
||||
updateTime();
|
||||
setInterval(updateTime,1000);
|
||||
formatBrewTime();
|
||||
setInterval(formatBrewTime,10000);
|
||||
});
|
||||
//MQTT client config
|
||||
var username = "coffee-user-"+ Math.random();
|
||||
// eslint-disable-next-line no-undef
|
||||
var client = new Paho.MQTT.Client("sika.sahkoinsinoorikilta.fi", 9001, username);
|
||||
client.onMessageArrived = function (message) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log("Topic: "+message.destinationName+" msg: "+message.payloadString);
|
||||
var ev = new CustomEvent(message.destinationName, {'detail': message.payloadString});
|
||||
window.dispatchEvent(ev);
|
||||
}
|
||||
|
||||
$(window).resize(resize);
|
||||
|
||||
function fetchdata(data, status){
|
||||
if (typeof status !== 'undefined'){
|
||||
if (status == "success"){
|
||||
parseData(data);
|
||||
function reconnect(responseObject){
|
||||
if (responseObject.errorCode !== 0) {
|
||||
console.log("connection lost! Reason: "+responseObject.errorMessage); // eslint-disable-line no-console
|
||||
setTimeout(function(){
|
||||
client.connect({onSuccess:onConnect, useSSL:true, onFailure: reconnect});
|
||||
}, backoff);
|
||||
}
|
||||
else if (status == "error"){
|
||||
handleError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$.getJSON("/coffee/cups", fetchdata);
|
||||
setInterval(function() {
|
||||
$.getJSON("/coffee/cups", fetchdata);
|
||||
}, 2000);
|
||||
|
||||
function formatBrewTime(){
|
||||
if (!brewtext && lastBrew instanceof Date){
|
||||
var now = new Date();
|
||||
var timeDiff = Math.max(now.getTime() - lastBrew.getTime(), 0);
|
||||
var tmp = (timeDiff < 3600000)
|
||||
? Math.round(timeDiff / 60000) + ' min'
|
||||
: '~' + Math.round(timeDiff / 3600000 * 2) / 2 + ' h';
|
||||
|
||||
$("#brewtime").html(tmp);
|
||||
} else {
|
||||
$("#brewtime").html(brewtext);
|
||||
function onConnect() {
|
||||
console.log("MQTT connected"); // eslint-disable-line no-console
|
||||
//set and reset reconnector
|
||||
client.onConnectionLost = reconnect
|
||||
// subscribe to topics
|
||||
client.subscribe("sik/kiltahuone/kahvivaaka/cups");
|
||||
client.subscribe("sik/kiltahuone/kahvivaaka/brewing");
|
||||
client.subscribe("sik/kiltahuone/kahvivaaka/brewtime");
|
||||
}
|
||||
|
||||
// data update and parse functions
|
||||
function parseCups(ev){
|
||||
var cups = parseFloat(ev.detail).toFixed(1)
|
||||
var cupsEvent = new CustomEvent("cupsChanged", {'detail': cups});
|
||||
window.dispatchEvent(cupsEvent);
|
||||
}
|
||||
function updateCups(ev){
|
||||
$("#text").text(ev.detail);
|
||||
}
|
||||
function updateScale(ev){
|
||||
$("#scale2").css({width: Math.min(ev.detail/9*100,100) + '%'});
|
||||
}
|
||||
|
||||
function handleError() {
|
||||
setData("?", 0, 0, Number.MAX_VALUE, Number.MAX_VALUE);
|
||||
function tick(){
|
||||
var ev = new CustomEvent("tick", {'detail': new Date()});
|
||||
window.dispatchEvent(ev);
|
||||
}
|
||||
|
||||
function parseData(data) {
|
||||
if (data) {
|
||||
var date = new Date(data.date);
|
||||
lastBrew = new Date(data.last_brew);
|
||||
var now = new Date();
|
||||
var cups = data.cups;
|
||||
var brewing = data.brewing;
|
||||
var timeDiff = Math.max(now.getTime() - lastBrew.getTime(),0);
|
||||
var opa = Math.max(100 - timeDiff / 90000,0);
|
||||
setData(cups, data.temp, opa,now.getTime()-date.getTime(), timeDiff, brewing);
|
||||
}
|
||||
else{
|
||||
handleError();
|
||||
}
|
||||
}
|
||||
|
||||
function setData(cups, temp, opa, timeFromUpdate, timeFromBrew, brewing){
|
||||
if (cups == 0) {
|
||||
opa = 0;
|
||||
}
|
||||
brewtext = "";
|
||||
$("#upper").css({opacity: opa/100});
|
||||
$("#scale2").css({width: Math.min(cups/9*100,100) + '%'});
|
||||
$("#text,body").removeClass();
|
||||
|
||||
cups = Number(cups).toFixed(1);
|
||||
|
||||
if(timeFromUpdate > 600000){
|
||||
cups = "?";
|
||||
brewtext = "∞";
|
||||
$("#text").addClass("unknown");
|
||||
}
|
||||
else if(brewing){
|
||||
cups = "+";
|
||||
brewtext = ":)";
|
||||
$("#text").addClass("brewing");
|
||||
}
|
||||
else if(cups <= 2){
|
||||
$("#text").addClass("hurry");
|
||||
}
|
||||
formatBrewTime();
|
||||
if($("#text").html() == "+" && !brewing)
|
||||
$("body").addClass("coffeeready");
|
||||
|
||||
var cupsString = cups.toString();
|
||||
len = cupsString.length;
|
||||
$("#text").html(cups);
|
||||
}
|
||||
|
||||
function updateTime(){
|
||||
var now = new Date();
|
||||
function updateTime(ev){
|
||||
var now = ev.detail;
|
||||
$("#time").html(formatTime(now.getHours(),now.getMinutes(),now.getSeconds()));
|
||||
}
|
||||
|
||||
function formatTime(hours, minutes, seconds){
|
||||
var str = "";
|
||||
|
||||
if(hours < 10)
|
||||
str += "0";
|
||||
str += hours;
|
||||
|
||||
str += ":";
|
||||
|
||||
if(minutes < 10)
|
||||
str += "0";
|
||||
str += minutes;
|
||||
|
||||
str += ":";
|
||||
|
||||
if(seconds < 10)
|
||||
str += "0";
|
||||
str += seconds;
|
||||
|
||||
return str;
|
||||
function coffeeLowEffect(ev){
|
||||
ev.detail <= 2 ? $("#text").addClass("hurry") : $("#text").removeClass("hurry");
|
||||
}
|
||||
function coffeeReadyEffect(){
|
||||
$("body").addClass("coffeeready");
|
||||
// autoclear animation class in 10s
|
||||
setTimeout(function(){$("body").removeClass("coffeeready");}, 10000);
|
||||
}
|
||||
function hotEffect(ev){
|
||||
var opa = Math.max(100 - ev.detail / 90000,0);
|
||||
$("#upper").css({opacity: opa/100});
|
||||
}
|
||||
function brewAnimStart(){
|
||||
$(".text").addClass("brewing");
|
||||
$(".layerone").hide();
|
||||
$(".layertwo").show();
|
||||
|
||||
}
|
||||
function brewAnimEnd(){
|
||||
$(".text").removeClass("brewing");
|
||||
$(".layertwo").hide();
|
||||
$(".layerone").show();
|
||||
}
|
||||
function brewNotifier(ev){
|
||||
var new_brewing = parseInt(ev.detail);
|
||||
if (new_brewing == 1 && brewing == 0){
|
||||
window.dispatchEvent(new Event("brewStart"));
|
||||
} else if (new_brewing == 0 && brewing == 1){
|
||||
window.dispatchEvent(new Event("brewEnd"));
|
||||
}
|
||||
brewing = new_brewing;
|
||||
}
|
||||
function brewTimeParser(ev){
|
||||
lastBrew = new Date(parseInt(ev.detail)*1000.0);
|
||||
}
|
||||
function updateBrewDiff(){
|
||||
var now = new Date();
|
||||
var timeDiff = Math.max(now.getTime() - lastBrew.getTime(), 0);
|
||||
var eve = new CustomEvent("dtUpdate", {'detail': timeDiff});
|
||||
window.dispatchEvent(eve);
|
||||
}
|
||||
function updateBrewTime(ev){
|
||||
var timeDiff = ev.detail;
|
||||
var timeStr;
|
||||
if (timeDiff < 3600000){
|
||||
timeStr = Math.round(timeDiff / 60000) + ' min'
|
||||
} else if (timeDiff < 10000* 3600 * 1000){ // 1000h
|
||||
timeStr = '~' + Math.round(timeDiff / 3600000 * 2) / 2 + ' h';
|
||||
} else {
|
||||
timeStr = "???"
|
||||
}
|
||||
$("#brewtime").html(timeStr);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
function nToS(num){
|
||||
return num < 10 ? "0" + num : "" + num;
|
||||
}
|
||||
|
||||
function formatTime(hours, minutes, seconds){
|
||||
return nToS(hours)+":"+nToS(minutes)+":"+nToS(seconds)
|
||||
}
|
||||
|
||||
function resize(){
|
||||
var w = $("#container").width();
|
||||
var h = $("#container").height();
|
||||
var s = w > h ? h : w;
|
||||
var font = s*0.8*0.38/Math.sqrt(len);
|
||||
$("#text").css({ top: s*0.16-font/2 + 'px', fontSize: font + 'px', marginLeft: -font*len*3/10 + 'px'});
|
||||
var font = s * 0.8 * 0.38/Math.sqrt(3);
|
||||
$(".text").css({ top: s*0.16-font/2 + 'px',
|
||||
fontSize: font + 'px',
|
||||
marginLeft: -font*3*3/10 + 'px'});
|
||||
}
|
||||
|
||||
// Init everything
|
||||
|
||||
$(document).ready(function(){
|
||||
client.connect({onSuccess:onConnect, useSSL:true, onFailure:reconnect});
|
||||
|
||||
//connect MQTT event listeners
|
||||
window.addEventListener("sik/kiltahuone/kahvivaaka/cups", parseCups);
|
||||
window.addEventListener("sik/kiltahuone/kahvivaaka/brewing", brewNotifier);
|
||||
window.addEventListener("sik/kiltahuone/kahvivaaka/brewtime", brewTimeParser);
|
||||
|
||||
//connect other event listeners
|
||||
window.addEventListener("cupsChanged", updateCups);
|
||||
window.addEventListener("cupsChanged", coffeeLowEffect);
|
||||
window.addEventListener("cupsChanged", updateScale);
|
||||
window.addEventListener("cupsChanged", resize);
|
||||
window.addEventListener("brewStart", brewAnimStart);
|
||||
window.addEventListener("brewEnd", brewAnimEnd);
|
||||
window.addEventListener("brewEnd", coffeeReadyEffect);
|
||||
window.addEventListener("tick", updateTime);
|
||||
window.addEventListener("tick", updateBrewDiff);
|
||||
window.addEventListener("dtUpdate", updateBrewTime);
|
||||
window.addEventListener("dtUpdate", hotEffect);
|
||||
|
||||
//start time based events
|
||||
setInterval(tick, 100);
|
||||
tick();
|
||||
|
||||
});
|
||||
$(window).resize(resize);
|
||||
|
||||
@@ -10,13 +10,16 @@
|
||||
<meta http-equiv="pragma" content="no-cache" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.2/mqttws31.js"
|
||||
type="text/javascript"></script>
|
||||
<link rel="stylesheet" href="/static/css/coffee.css" />
|
||||
<script src="/static/js/coffee.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="container">
|
||||
<span id="brewtime"></span>
|
||||
<span id="brewtime" class="brewtime layerone"></span>
|
||||
<span class="brewtime layertwo">:)</span>
|
||||
<span id="address">
|
||||
ka.dy.fi
|
||||
<noscript><br>This page uses JavaScript!</noscript>
|
||||
@@ -26,7 +29,8 @@
|
||||
</div>
|
||||
<!--Kahvinkeitin on rikki. Varakeittimellä keitettyä kahvia saattaa olla.-->
|
||||
<div id="lower" class="normal">
|
||||
<div id="text"></div>
|
||||
<div id="text" class="text layerone">???</div>
|
||||
<div class="text layertwo"> +</div>
|
||||
<div id="scale"><div id="scale2"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,30 +1,2 @@
|
||||
from django.test import TestCase, Client
|
||||
from django.conf import settings
|
||||
|
||||
from coffee_scale.mqtt import on_message
|
||||
|
||||
HOST = settings.MQTT_SETTINGS['HOST']
|
||||
PORT = settings.MQTT_SETTINGS['PORT']
|
||||
TOPICS = settings.MQTT_SETTINGS['TOPICS']
|
||||
|
||||
|
||||
class MQTTTestCase(TestCase):
|
||||
"""Tests MQTT functionality"""
|
||||
|
||||
class MockMessage:
|
||||
def __init__(self, payload, topic):
|
||||
self.payload = payload
|
||||
self.topic = topic
|
||||
|
||||
def setUp(self):
|
||||
payload = '10'.encode('utf-8')
|
||||
topic = TOPICS['CUPS']
|
||||
msg = MQTTTestCase.MockMessage(payload, topic)
|
||||
|
||||
on_message(None, None, msg)
|
||||
self.c = Client()
|
||||
|
||||
def test_receive_cups(self):
|
||||
response = self.c.get('/coffee/cups')
|
||||
payload = response.json()
|
||||
self.assertEquals(payload['cups'], 10)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.conf.urls import url
|
||||
from django.views.generic.base import RedirectView
|
||||
|
||||
from .views import coffee_view, cups_view
|
||||
from .views import coffee_view
|
||||
|
||||
favicon_view = RedirectView.as_view(url='static/img/favicon.ico', permanent=True)
|
||||
|
||||
@@ -9,6 +9,4 @@ urlpatterns = [
|
||||
|
||||
# landing page
|
||||
url(r'^$', coffee_view),
|
||||
url(r'^cups', cups_view),
|
||||
|
||||
]
|
||||
|
||||
@@ -3,25 +3,9 @@ from django.http import JsonResponse
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from .mqtt import get_latest
|
||||
import coffee_scale.mqtt # somehow this is needed
|
||||
|
||||
import logging
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
def coffee_view(request):
|
||||
return render(request, 'coffee.html')
|
||||
|
||||
|
||||
def cups_view(request):
|
||||
now = timezone.now()
|
||||
latest = get_latest()
|
||||
data = {
|
||||
'date': now,
|
||||
'cups': latest.get('cups'),
|
||||
'last_brew': latest.get('brew_time'),
|
||||
'brewing': latest.get('brewing'),
|
||||
'weight': latest.get('weight')
|
||||
}
|
||||
return JsonResponse(data)
|
||||
|
||||
+51
-54
@@ -1,73 +1,70 @@
|
||||
"""File containing Infoscreen HSL data fetcher classes."""
|
||||
|
||||
import urllib.request
|
||||
import requests
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pytz
|
||||
|
||||
from datetime import timedelta, datetime
|
||||
from django.utils import timezone
|
||||
from django.utils import timezone, dateparse
|
||||
from django.utils.dateformat import format
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from infoscreen.models import HSLDataModel
|
||||
|
||||
with open(os.path.join(settings.BASE_DIR, 'infoscreen', 'hsl_stops.graphql')) as stops_file:
|
||||
STOPS_QUERY = stops_file.read()
|
||||
|
||||
with open(os.path.join(settings.BASE_DIR, 'infoscreen', 'hsl_stops_variables.json')) as vars_file:
|
||||
STOPS_VARS = json.loads(vars_file.read())
|
||||
|
||||
API_URL = 'https://api.digitransit.fi/routing/v1/routers/hsl/index/graphql'
|
||||
API_HEADERS = {'Content-Type': 'application/json'}
|
||||
|
||||
|
||||
class HSLFetcher:
|
||||
"""Main class of Infoscreen HSL fetcher."""
|
||||
def fetch():
|
||||
"""Fetch data from HSL API."""
|
||||
|
||||
last_fetched = datetime.fromtimestamp(86400) # epoch
|
||||
INTERVAL = 1 # minutes
|
||||
# logging.info(
|
||||
# "Set up scheduled HSL API fetch every {} minutes".format(INTERVAL))
|
||||
query_vars = STOPS_VARS.copy()
|
||||
query_vars['startTime_6'] = format(timezone.now(), 'U')
|
||||
|
||||
def fetch_if_needed(self):
|
||||
"""Check if new fetch from HSL API is needed."""
|
||||
if (timezone.now() - HSLFetcher.last_fetched >
|
||||
timedelta(minutes=HSLFetcher.INTERVAL)):
|
||||
self.fetch()
|
||||
post_data = json.dumps({
|
||||
'operationName': 'NearestRoutesContainer',
|
||||
'query': STOPS_QUERY,
|
||||
'variables': query_vars,
|
||||
})
|
||||
|
||||
def fetch(self):
|
||||
"""Fetch data from HSL API."""
|
||||
location_coords = (2545565, 6675319)
|
||||
src = urllib.request.urlopen(
|
||||
("https://api.reittiopas.fi/hsl/prod/?userhash={}"
|
||||
"&request=stops_area¢er_coordinate={},{}")
|
||||
.format(settings.HSL_USERHASH, location_coords[0],
|
||||
location_coords[1]))\
|
||||
.read().decode("utf-8")
|
||||
resp = requests.post(API_URL, data=post_data, headers=API_HEADERS)
|
||||
|
||||
data = json.loads(src)
|
||||
data = resp.json()
|
||||
|
||||
arr = []
|
||||
items = data['data']['viewer']['_nearest']['edges']
|
||||
places = map(lambda item: item['node']['place'], items)
|
||||
|
||||
time = (timezone.now() +
|
||||
timedelta(minutes=settings.HSL_DEPARTURE_THRESHOLD))
|
||||
time = "{0:02d}{0:02d}".format(time.hour, time.minute)
|
||||
for element in data:
|
||||
src = urllib.request.urlopen(
|
||||
("https://api.reittiopas.fi/hsl/prod/?userhash={}"
|
||||
"&request=stop&code={}&dep_limit=20&time={}")
|
||||
.format(settings.HSL_USERHASH, element['code'], time)
|
||||
).read().decode("utf-8")
|
||||
schedule = []
|
||||
for place in places:
|
||||
route = place['pattern']['route']['shortName']
|
||||
stop_times = place['_stoptimes']
|
||||
for stop_time in stop_times:
|
||||
timestamp = time_utc = stop_time['serviceDay'] + stop_time['realtimeArrival']
|
||||
headsign = stop_time['stopHeadsign']
|
||||
stop_name = stop_time['stop']['name']
|
||||
time_diff = (timestamp - timezone.now().timestamp()) / 60 # minutes
|
||||
|
||||
parsed = json.loads(src)[0]
|
||||
arr.append({
|
||||
"name": parsed['name_fi'],
|
||||
"lines": parsed['lines'],
|
||||
"dist": element['dist'],
|
||||
"departures": parsed['departures']})
|
||||
if time_diff < settings.HSL_DEPARTURE_THRESHOLD:
|
||||
continue
|
||||
elif time_diff < settings.HSL_HURRY_THRESHOLD:
|
||||
time = '{} min'.format(int(time_diff))
|
||||
else:
|
||||
time = pytz.utc.localize(datetime.fromtimestamp(timestamp)).strftime('%H:%M')
|
||||
|
||||
model_arr = HSLDataModel.objects.all()
|
||||
count = len(model_arr)
|
||||
json_dump = json.dumps(arr)
|
||||
schedule.append({
|
||||
'route': route,
|
||||
'headsign': headsign,
|
||||
'timestamp': time,
|
||||
'stop': stop_name,
|
||||
'utc': time_utc,
|
||||
})
|
||||
|
||||
if count == 0:
|
||||
HSLDataModel.objects.create(data=json_dump)
|
||||
else:
|
||||
obj = model_arr[count - 1]
|
||||
obj.data = json_dump
|
||||
obj.save()
|
||||
now = timezone.now()
|
||||
HSLFetcher.last_fetched = now
|
||||
|
||||
logging.info(
|
||||
"Fetched HSL timetable data with size {} bytes.".format(len(src)))
|
||||
return schedule
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
query NearestRoutesContainer($lat_0: Float!, $lon_1: Float!, $maxDistance_2: Int!, $maxResults_3: Int!, $timeRange_7: Int!, $numberOfDepartures_8: Int!, $filterByModes_4: [Mode]!, $filterByPlaceTypes_5: [FilterPlaceType]!, $startTime_6: Long!) {
|
||||
viewer {
|
||||
...F5
|
||||
}
|
||||
}
|
||||
|
||||
fragment F0 on DepartureRow {
|
||||
_stoptimes4caEfh: stoptimes(startTime: $startTime_6, timeRange: $timeRange_7, numberOfDepartures: $numberOfDepartures_8) {
|
||||
pickupType
|
||||
serviceDay
|
||||
realtimeDeparture
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
fragment F1 on DepartureRow {
|
||||
pattern {
|
||||
route {
|
||||
shortName
|
||||
}
|
||||
}
|
||||
_stoptimes: stoptimes(startTime: $startTime_6, timeRange: $timeRange_7, numberOfDepartures: $numberOfDepartures_8) {
|
||||
realtimeArrival
|
||||
serviceDay
|
||||
stopHeadsign
|
||||
stop {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment F2 on BikeRentalStation {
|
||||
id
|
||||
}
|
||||
|
||||
fragment F3 on placeAtDistance {
|
||||
distance
|
||||
place {
|
||||
id
|
||||
__typename
|
||||
...F1
|
||||
...F2
|
||||
}
|
||||
id
|
||||
}
|
||||
|
||||
fragment F4 on placeAtDistanceConnection {
|
||||
edges {
|
||||
node {
|
||||
distance
|
||||
place {
|
||||
id
|
||||
__typename
|
||||
...F0
|
||||
}
|
||||
id
|
||||
...F3
|
||||
}
|
||||
cursor
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
hasPreviousPage
|
||||
}
|
||||
}
|
||||
|
||||
fragment F5 on QueryType {
|
||||
_nearest: nearest(lat: $lat_0, lon: $lon_1, maxDistance: $maxDistance_2, maxResults: $maxResults_3, first: $maxResults_3, filterByModes: $filterByModes_4, filterByPlaceTypes: $filterByPlaceTypes_5) {
|
||||
...F4
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"lat_0": 60.190480099999995,
|
||||
"lon_1": 24.8275665,
|
||||
"maxDistance_2": 1000,
|
||||
"maxResults_3": 50,
|
||||
"numberOfDepartures_8": 2,
|
||||
"timeRange_7": 7200,
|
||||
"filterByModes_4": ["BUS"],
|
||||
"filterByPlaceTypes_5": ["DEPARTURE_ROW"]
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2017-10-31 14:56
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('infoscreen', '0005_auto_20170913_1841'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='HSLDataModel',
|
||||
),
|
||||
]
|
||||
@@ -406,9 +406,3 @@ class UploadFileForm(forms.Form):
|
||||
|
||||
name = forms.CharField()
|
||||
video = forms.FileField()
|
||||
|
||||
|
||||
class HSLDataModel(models.Model):
|
||||
"""Model representing HSL data."""
|
||||
|
||||
data = models.TextField(default="", editable=False)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
table {
|
||||
font-size: 5vh;
|
||||
font-size: 4vh;
|
||||
font-family: 'Droid Sans Mono', monospace;
|
||||
}
|
||||
.red {
|
||||
@@ -58,9 +58,9 @@ thead{
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.repeat-item.ng-leave {
|
||||
-webkit-transition:0.5s linear all;
|
||||
transition:0.5s linear all;
|
||||
|
||||
}
|
||||
|
||||
.repeat-item.ng-leave.ng-leave-active {
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
<link rel="stylesheet" href="/static/css/hsl.css">
|
||||
<link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono" rel="stylesheet">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/locale/fi.js"></script>
|
||||
<div class="container" ng-app="myApp" ng-controller="timetableCtrl">
|
||||
<div class="header-row row">
|
||||
<div class="col-sm-2"></div>
|
||||
<div class="col-sm-2"><p>{{clock | date:'HH:mm'}}</p></div>
|
||||
<div class="col-sm-8">HSL-Aikataulut</div>
|
||||
<div class="col-sm-2 time"><p>{{ clock | date:'HH:mm'}}</p></div>
|
||||
<div class="col-sm-2 time"></div>
|
||||
</div>
|
||||
<table class="table table-striped row">
|
||||
<h1 style="font-size: 10vh; text-align: center" ng-if="error">
|
||||
{{error}}
|
||||
</h1>
|
||||
<table ng-if="!error" class="table table-striped row">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
@@ -18,25 +22,19 @@
|
||||
<th>
|
||||
Pysäkki
|
||||
</th>
|
||||
<th>
|
||||
Päätepysäkki
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="repeat-item" ng-repeat="x in arr | orderBy: ['date','time'] | limitTo: 10">
|
||||
<td ng-class='{red : x.hurry, black: !x.hurry}'>
|
||||
{{x.timedelta < 10 ?x.timedelta + ' min' : x.time}}
|
||||
<tr class="repeat-item" ng-repeat="x in stoptimes | orderBy: ['utc'] | limitTo: 11">
|
||||
<td style="min-width: 300px">
|
||||
{{x.timestamp}}
|
||||
</td>
|
||||
<td>
|
||||
{{x.bus}}
|
||||
<strong>{{x.route}}</strong>, {{x.headsign}}
|
||||
</td>
|
||||
<td>
|
||||
{{x.stop}}
|
||||
</td>
|
||||
<td>
|
||||
{{x.laststop}}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var app = angular.module('infoApp', ['ngAnimate', 'ngRoute']);
|
||||
|
||||
app.controller('infoscreen_main', function($scope,$http,$timeout){
|
||||
var templates = [];
|
||||
$scope.init = function(rot){
|
||||
@@ -103,13 +104,22 @@ app.controller('EventController', function($scope, $http) {
|
||||
})
|
||||
});
|
||||
|
||||
app.filter('unixTimeToDifference', function() {
|
||||
return function(input) {
|
||||
var date = moment.unix(input);
|
||||
var now = moment();
|
||||
var res = date.diff(now, 'minutes');
|
||||
return res;
|
||||
}
|
||||
})
|
||||
|
||||
app.controller('timetableCtrl',
|
||||
function($scope, $http, $interval) {
|
||||
function load(){
|
||||
function load() {
|
||||
$http.get('/infoscreen/hsl_data')
|
||||
.then(function(data, status, headers, config) { //eslint-disable-line no-unused-vars
|
||||
$scope.arr=[];
|
||||
parse(data);
|
||||
$scope.stoptimes = data.data;
|
||||
$scope.error = data.data.error || null;
|
||||
});
|
||||
$http.get('/infoscreen/hsl_data/settings')
|
||||
.then(function(data, status, headers, config) { //eslint-disable-line no-unused-vars
|
||||
@@ -117,112 +127,20 @@ app.controller('timetableCtrl',
|
||||
$scope.hurryThreshold = data.data['hurry_threshold'];
|
||||
});
|
||||
}
|
||||
$scope.$on('$destroy', function() {
|
||||
$interval.cancel(inter1);
|
||||
$interval.cancel(inter2);
|
||||
$interval.cancel(inter3);
|
||||
});
|
||||
var objects;
|
||||
$scope.arr=[];
|
||||
var dict=[];
|
||||
function parse(data){
|
||||
objects=data['data'];
|
||||
for(var objectIndex in objects){
|
||||
var stop = objects[objectIndex];
|
||||
|
||||
var lineIndex;
|
||||
for (lineIndex in stop['lines']){
|
||||
var elem=stop['lines'][lineIndex].split(":");
|
||||
dict[elem[0]]=elem[1];
|
||||
}
|
||||
for (lineIndex in stop['departures']){
|
||||
var line = stop['departures'][lineIndex];
|
||||
var time = line['time'];
|
||||
var date = line['date'];
|
||||
var hours = Math.floor(time / 100);
|
||||
var minutes = time % 100;
|
||||
if (hours >= 24) {
|
||||
hours -= 24;
|
||||
date++;
|
||||
}
|
||||
var code = line['code'].substring(1, 5);
|
||||
if (code.charAt(0) == '0') {
|
||||
code = code.substring(1,4);
|
||||
}
|
||||
|
||||
var departure = {
|
||||
"stop": stop['name'].split(",")[0],
|
||||
"dist": stop['dist'],
|
||||
"bus": code,
|
||||
"date": date,
|
||||
"time": pad(hours, 2) + ":" + pad(minutes, 2),
|
||||
"laststop": dict[line['code']].split(",")[0].split(" l.")[0],
|
||||
"hurry": false
|
||||
};
|
||||
if(departure['laststop']=='Otaniemi')
|
||||
break;
|
||||
if(departure['stop']=='Alvar Aallon puisto')
|
||||
departure['stop']="A. A. puisto"
|
||||
var trigger = true;
|
||||
for (var arrIndex = $scope.arr.length - 1; arrIndex >= 0; arrIndex--) {
|
||||
if ($scope.arr[arrIndex]['bus'] == departure['bus'] &&
|
||||
$scope.arr[arrIndex]['laststop'] == departure['laststop']) {
|
||||
|
||||
if ($scope.arr[arrIndex]['dist'] == departure['dist']){
|
||||
break;
|
||||
}
|
||||
else if ($scope.arr[arrIndex]['dist'] > departure['dist']){
|
||||
$scope.arr.splice(arrIndex, 1);
|
||||
}
|
||||
else {
|
||||
trigger = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (trigger) {
|
||||
$scope.arr.push(departure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pad(num, size) {
|
||||
var s = num + "";
|
||||
while (s.length < size) {
|
||||
s = "0" + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
delOld();
|
||||
}
|
||||
function delOld(){
|
||||
var tooSoon = typeof($scope.departureThreshold) != 'undefined' ? $scope.departureThreshold: 0;
|
||||
var hurry = typeof($scope.hurryThreshold) != 'undefined' ? $scope.hurryThreshold : 0;
|
||||
var f = new Date();
|
||||
for (var a=$scope.arr.length-1; a>=0; a--) {
|
||||
var time=$scope.arr[a]['time'].split(":");
|
||||
var date=$scope.arr[a]['date'].toString();
|
||||
var d = new Date(f);
|
||||
d.setFullYear(date.substring(0,4), date.substring(4,6)-1, date.substring(6,8));
|
||||
d.setHours(time[0]);
|
||||
d.setMinutes(time[1]);
|
||||
var diff=(d.getTime()-f.getTime());
|
||||
$scope.arr[a]['timedelta']=Math.floor(diff/60000);
|
||||
if(diff < tooSoon*60000) {
|
||||
$scope.arr.splice(a,1);
|
||||
}
|
||||
else if (diff < hurry*60000) {
|
||||
$scope.arr[a]['hurry']=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
function updateTime(){
|
||||
function update_clock() {
|
||||
$scope.clock = Date.now();
|
||||
}
|
||||
$scope.clock = Date.now();
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
$interval.cancel(load_interval);
|
||||
$interval.cancel(clock_interval);
|
||||
});
|
||||
|
||||
var load_interval = $interval(load, 5000);
|
||||
var clock_interval = $interval(update_clock, 1000);
|
||||
|
||||
update_clock();
|
||||
load();
|
||||
var inter1=$interval(delOld,2000);
|
||||
var inter2=$interval(load,10000);
|
||||
var inter3=$interval(updateTime, 1000);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -20,7 +20,6 @@ from infoscreen.models import (ABBInfoItem, ExternalImageInfoItem,
|
||||
from infoscreen.models import EventInfoItem
|
||||
from infoscreen.models import ExternalWebsiteInfoItem
|
||||
from infoscreen.models import ImageUploadForm
|
||||
from infoscreen.models import HSLDataModel
|
||||
from infoscreen.models import ApyInfoItem
|
||||
from infoscreen.models import VideoInfoItem
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.conf import settings
|
||||
|
||||
from infoscreen.models import Rotation, InfoItem, InfoInstance
|
||||
from infoscreen.hsl_fetcher import HSLFetcher
|
||||
from infoscreen.hsl_fetcher import fetch as hsl_fetch
|
||||
|
||||
import json
|
||||
import logging
|
||||
import threading
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
@@ -82,22 +84,18 @@ def hsl_timetable_settings(request, *args, **kwargs):
|
||||
"""Set HSL timetable settings."""
|
||||
d = {"departure_threshold": settings.HSL_DEPARTURE_THRESHOLD,
|
||||
"hurry_threshold": settings.HSL_HURRY_THRESHOLD}
|
||||
resp = json.dumps(d)
|
||||
return HttpResponse(resp, status=200)
|
||||
|
||||
return JsonResponse(d, status=200)
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
def CurrentHSLView(request, *args, **kwargs):
|
||||
"""Get HSL data and return it."""
|
||||
fetcher = HSLFetcher()
|
||||
fetcherThread = threading.Thread(target=fetcher.fetch_if_needed, args=[])
|
||||
fetcherThread.setDaemon(False)
|
||||
fetcherThread.start()
|
||||
try:
|
||||
api_resp = hsl_fetch()
|
||||
except Exception as ex:
|
||||
logging.exception('Failed to fetch HSL timetables.')
|
||||
error = {'error': 'Aikataulujen haku epäonnistui.'}
|
||||
return JsonResponse(error, status=200)
|
||||
|
||||
data = HSLDataModel.objects.all()
|
||||
if len(data) < 1:
|
||||
return HttpResponse(
|
||||
'{"error" : "Could not find timetables from database."}',
|
||||
status=500)
|
||||
|
||||
return HttpResponse(data[len(data) - 1].data, status=200)
|
||||
return JsonResponse(api_resp, status=200, safe=False)
|
||||
|
||||
+1
-1
@@ -25,8 +25,8 @@ dealer==2.0.5
|
||||
django-modeltranslation==0.12.1
|
||||
django-auditlog==0.4.3
|
||||
django-phonenumber-field==1.3.0
|
||||
paho-mqtt==1.3.0
|
||||
django-autocomplete-light==3.2.10
|
||||
six==1.10.0
|
||||
django-suit==0.2.25
|
||||
telepot==12.3
|
||||
django-password-reset==1.0
|
||||
|
||||
+2
-1
@@ -78,13 +78,14 @@ INSTALLED_APPS = [
|
||||
'webapp',
|
||||
'members',
|
||||
'infoscreen',
|
||||
'coffee_scale.apps.CoffeeScaleConfig',
|
||||
'coffee_scale',
|
||||
'rest_framework',
|
||||
'django_nose',
|
||||
'bootstrap3',
|
||||
'django_tables2',
|
||||
'auditlog',
|
||||
'phonenumber_field',
|
||||
'password_reset',
|
||||
]
|
||||
|
||||
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
|
||||
|
||||
@@ -26,6 +26,7 @@ import webapp.urls
|
||||
import infoscreen.urls
|
||||
import members.urls
|
||||
import coffee_scale.urls
|
||||
import password_reset.urls
|
||||
|
||||
urlpatterns = [
|
||||
url(r'', include('webapp.urls')),
|
||||
@@ -35,6 +36,7 @@ urlpatterns = [
|
||||
|
||||
# admin
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'^reset/', include('password_reset.urls')),
|
||||
|
||||
# i18n default view for changing the active language
|
||||
url(r'^i18n/', include('django.conf.urls.i18n')),
|
||||
|
||||
+3
-13
@@ -1,18 +1,8 @@
|
||||
#footer-div {
|
||||
height:10vh;
|
||||
}
|
||||
|
||||
footer {
|
||||
/* position: absolute; */
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px; /* Set the fixed height of the footer here */
|
||||
/* line-height: 60px; /* Vertically center the text there */ */
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
footer .container .col .nav .nav-item {
|
||||
/*footer .container .col .nav .nav-item {
|
||||
display: inline-block;
|
||||
margin-right: 3vh;
|
||||
}
|
||||
@@ -31,4 +21,4 @@ footer .container .col .nav .nav-item {
|
||||
|
||||
footer .lang-form {
|
||||
margin: 1rem auto 0;
|
||||
}
|
||||
}*/
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Aalto-yliopiston Sähköinsinöörikilta ry">
|
||||
<meta name="author" content="Aalto-yliopiston Sähköinsinöörikilta ry">
|
||||
|
||||
<title>{% trans "Aalto-yliopiston Sähköinsinöörikilta ry" %}</title>
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<!-- <script src="{% static "js/lib/jquery-3.1.0.min.js" %}"></script> -->
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<script src="{% static "js/lib/underscore-min.js" %}"></script>
|
||||
<!-- <link href="https://fonts.googleapis.com/css?family=Didact+Gothic" rel="stylesheet"> -->
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
{% block body %}
|
||||
{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
+63
-29
@@ -3,35 +3,69 @@
|
||||
{% load staticfiles %}
|
||||
<link rel="stylesheet" href="{% static "css/footer.css" %}">
|
||||
|
||||
<footer style="text-align: center">
|
||||
<div>
|
||||
<form class="lang-form form" action="{% url 'set_language' %}" method="post">{% csrf_token %}
|
||||
<span>
|
||||
<input name="next" type="hidden" value="{{ redirect_to }}" />
|
||||
<select onchange="this.form.submit()" class="lang-select form-control" name="language">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% for language in languages %}
|
||||
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
|
||||
{{ language.name_local }} ({{ language.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
</form>
|
||||
<span>{% trans "Copyright Aalto-yliopiston Sähköinsinöörikilta ry" %} {% now 'Y' %}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="nav-item">
|
||||
<a href="/members"><i class="fa fa-group"></i></a>
|
||||
</span>
|
||||
<span class="nav-item">
|
||||
<a href="/infoscreen"><i class="fa fa-info"></i></a>
|
||||
</span>
|
||||
<span class="nav-item">
|
||||
<a href="/admin"><i class="fa fa-gears"></i></a>
|
||||
</span>
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<div class="d-flex align-items-center justify-content-end">
|
||||
<div class="p-2">
|
||||
<span>{% trans "Copyright Aalto-yliopiston Sähköinsinöörikilta ry" %} {% now 'Y' %}</span>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<form class="lang-form form" action="{% url 'set_language' %}" method="post">{% csrf_token %}
|
||||
<span class="form-group">
|
||||
<input name="next" type="hidden" value="{{ redirect_to }}" />
|
||||
<select onchange="this.form.submit()" class="lang-select form-control" name="language">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% for language in languages %}
|
||||
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
|
||||
{{ language.name_local }} ({{ language.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
</form>
|
||||
</div>
|
||||
<div class="ml-auto p-2">
|
||||
<span class="nav-item">
|
||||
<a href="/members"><i class="fa fa-group"></i></a>
|
||||
</span>
|
||||
<span class="nav-item">
|
||||
<a href="/infoscreen"><i class="fa fa-info"></i></a>
|
||||
</span>
|
||||
<span class="nav-item">
|
||||
<a href="/admin"><i class="fa fa-gears"></i></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- <form class="lang-form form" action="{% url 'set_language' %}" method="post">{% csrf_token %}
|
||||
<span>
|
||||
<input name="next" type="hidden" value="{{ redirect_to }}" />
|
||||
<select onchange="this.form.submit()" class="lang-select form-control" name="language">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% for language in languages %}
|
||||
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
|
||||
{{ language.name_local }} ({{ language.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
</form>
|
||||
<span>{% trans "Copyright Aalto-yliopiston Sähköinsinöörikilta ry" %} {% now 'Y' %}</span>
|
||||
-->
|
||||
|
||||
<!-- <span class="nav-item">
|
||||
<a href="/members"><i class="fa fa-group"></i></a>
|
||||
</span>
|
||||
<span class="nav-item">
|
||||
<a href="/infoscreen"><i class="fa fa-info"></i></a>
|
||||
</span>
|
||||
<span class="nav-item">
|
||||
<a href="/admin"><i class="fa fa-gears"></i></a>
|
||||
</span> -->
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
{% extends "login_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<div class="d-flex justify-content-center">
|
||||
<h1>SIK Admin</h1>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<form method="POST" class="form-horizontal" action=""> {% csrf_token %}
|
||||
<div class="form-group">
|
||||
<label for="input-username" class="col-sm-2 col-form-label">{% trans "Username" %}</label>
|
||||
<input type="text" name="username" id="input-username" class="form-control" placeholder="{% trans "Username" %}"></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="input-password" class="col-sm-2 col-form-label">{% trans "Password" %}</label>
|
||||
<input type="password" name="passwd" id="input-passwd" class="form-control" placeholder="{% trans "Password" %}"></input>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<a href={% url "password_reset_recover" %}>{% trans "Forgot password?" %}</a>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="text-danger">{{ error }}</div>
|
||||
</div>
|
||||
<div class="form-group" id="login-button">
|
||||
<button type="submit" class="btn btn-primary">{% trans "Log in" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
@@ -0,0 +1,22 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% block body %}
|
||||
{% block header %}
|
||||
<div class="header">
|
||||
{% include "sik_header.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<div class="page-content">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="footer">
|
||||
{% block footer %}
|
||||
{% include "footer.html" %}
|
||||
{% endblock footer %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1 @@
|
||||
{% extends "login_base.html" %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends "password_reset/base.html" %}{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "New password set" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>{% trans "Your password has successfully been reset. You can use it right now on the login page." %}</p>
|
||||
<p><a href="/login">Log in</a></p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,12 @@
|
||||
{% extends "password_reset/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Password recovery" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post" action="{{ url }}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p><input type="submit" value="{% trans "Send" %}"></p>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,14 @@
|
||||
{% extends "password_reset/base.html" %}{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
{% if invalid %}{% url "password_reset_recover" as recovery_url %}
|
||||
<p>{% blocktrans %}Sorry, this password reset link is invalid. You can still <a href="{{ recovery_url }}">request a new one</a>.{% endblocktrans %}</p>
|
||||
{% else %}
|
||||
<p>{% blocktrans %}Hi, <strong>{{ username }}</strong>. Please choose your new password.{% endblocktrans %}</p>
|
||||
<form method="post" action="{% url "password_reset_reset" token %}">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<p><input type="submit" value="{% trans "Set new password" %}"></p>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,15 @@
|
||||
{% autoescape off %}
|
||||
You're receiving this e-mail because you requested a password reset for your user account at {{ site_name }}.
|
||||
|
||||
Please go to the following page and choose a new password:
|
||||
{% block reset_link %}
|
||||
{{ protocol }}://{{ domain }}{% url django.contrib.auth.views.password_reset_confirm uidb36=uid, token=token %}
|
||||
{% endblock %}
|
||||
|
||||
Your username, in case you've forgotten: {{ user.username }}
|
||||
|
||||
Thanks for using our site!
|
||||
|
||||
The {{ site_name }} team.
|
||||
|
||||
{% endautoescape %}
|
||||
@@ -0,0 +1,8 @@
|
||||
{% extends "password_reset/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Password recovery sent" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>{% blocktrans with ago=timestamp|timesince %}An email was sent to <strong>{{ email }}</strong> {{ ago }} ago. Use the link in it to set a new password.{% endblocktrans %}</p>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,34 @@
|
||||
#footer-div {
|
||||
height:10vh;
|
||||
}
|
||||
|
||||
footer {
|
||||
/* position: absolute; */
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 60px; /* Set the fixed height of the footer here */
|
||||
/* line-height: 60px; /* Vertically center the text there */ */
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
footer .container .col .nav .nav-item {
|
||||
display: inline-block;
|
||||
margin-right: 3vh;
|
||||
}
|
||||
|
||||
.lang-button {
|
||||
height: 4vh;
|
||||
width: 6vh;
|
||||
margin-left: 1vh;
|
||||
margin-right: 1vh;
|
||||
}
|
||||
|
||||
.lang-select {
|
||||
width: 10rem;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
footer .lang-form {
|
||||
margin: 1rem auto 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
.header-content {
|
||||
|
||||
}
|
||||
|
||||
.header-content .logo {
|
||||
|
||||
}
|
||||
|
||||
.header-content .logo img {
|
||||
display: block;
|
||||
height: auto;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.kaehmy-banner {
|
||||
max-width: 1000px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1000px) {
|
||||
.kaehmy_header-content {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: #052f5f;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.kaehmy_header {
|
||||
margin-bottom: 331px;
|
||||
}
|
||||
}
|
||||
|
||||
.kaehmy-banner-image {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
.main_navigation .navbar-border .navbar-nav .nav-item {
|
||||
/*.main_navigation .navbar-border .navbar-nav .nav-item {
|
||||
display: block;
|
||||
}
|
||||
}*/
|
||||
|
||||
@@ -11,27 +11,3 @@
|
||||
height: auto;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.kaehmy-banner {
|
||||
max-width: 1000px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1000px) {
|
||||
.kaehmy_header-content {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background-color: #052f5f;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.kaehmy_header {
|
||||
margin-bottom: 331px;
|
||||
}
|
||||
}
|
||||
|
||||
.kaehmy-banner-image {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,26 +1,9 @@
|
||||
.page-content {
|
||||
margin-top: 1vh;
|
||||
width: 90%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
html {
|
||||
position: relative;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 1000px;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
/*Margin bottom by footer hight*/
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
|
||||
div.tooltip-inner {
|
||||
max-width: 25rem;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.role-filter-form {
|
||||
max-width: 30rem;
|
||||
width: auto;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
.page-content {
|
||||
margin-top: 1vh;
|
||||
width: 90%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
body {
|
||||
max-width: 1000px;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
div.tooltip-inner {
|
||||
max-width: 25rem;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
.role-filter-form {
|
||||
max-width: 30rem;
|
||||
width: auto;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "webapp_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Aalto-yliopiston Sähköinsinöörikilta ry">
|
||||
<meta name="author" content="Aalto-yliopiston Sähköinsinöörikilta ry">
|
||||
<link rel="stylesheet" href="{% static "css/webapp.css" %}">
|
||||
|
||||
<title>{% trans "Aalto-yliopiston Sähköinsinöörikilta ry" %}</title>
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="{% static "js/lib/jquery-3.1.0.min.js" %}"></script>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.2/css/bootstrap.min.css" integrity="sha384-y3tfxAZXuh4HwSYylfB+J125MxIs6mR5FOHamPBG064zB+AFeWH94NdvaCBm8qnd" crossorigin="anonymous">
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<script src="{% static "js/lib/underscore-min.js" %}"></script>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% block header %}
|
||||
<div class="header">
|
||||
{% include "sik_header.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<div class="page-content">
|
||||
{% block navigation %}
|
||||
{% include "navigation.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class="footer">
|
||||
{% block footer %}
|
||||
{% include "footer.html" %}
|
||||
{% endblock footer %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,10 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "webapp_base.html" %}
|
||||
{% block content %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
<div class="contact_div">
|
||||
<h2>{% trans "Contact" %}</h2>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2>{% trans "Contact" %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "webapp_base.html" %}
|
||||
{% block content %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
<div class="event_calendar_div">
|
||||
<h2>{% trans "Event calendar" %}</h2>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2>{% trans "Event calendar" %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "webapp_base.html" %}
|
||||
{% block content %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
<div class="freshmen_div">
|
||||
<h2>{% trans "Freshmen" %}</h2>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2>{% trans "Freshmen" %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "webapp_base.html" %}
|
||||
{% block content %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
<div class="guild_div">
|
||||
<h2>{% trans "Kilta" %}</h2>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2>{% trans "Kilta" %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "webapp_base.html" %}
|
||||
{% block content %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
<div class="international_div">
|
||||
<h2>{% trans "International" %}</h2>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2>{% trans "International" %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "webapp_base.html" %}
|
||||
{% block content %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
<div class="jobs_div">
|
||||
<h2>{% trans "Jobs" %}</h2>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2>{% trans "Jobs" %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,14 +1,50 @@
|
||||
{% extends "base.html" %}
|
||||
<!DOCTYPE html>
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block navigation %}
|
||||
{% include "kaehmy_navigation.html" %}
|
||||
{% endblock %}
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="Aalto-yliopiston Sähköinsinöörikilta ry">
|
||||
<meta name="author" content="Aalto-yliopiston Sähköinsinöörikilta ry">
|
||||
<link rel="stylesheet" href="/static/css/webapp_kaehmy.css">
|
||||
<title>{% trans "Aalto-yliopiston Sähköinsinöörikilta ry" %}</title>
|
||||
|
||||
{% block header %}
|
||||
<div class="kaehmy_header">
|
||||
{% include "kaehmy_header.html" %}
|
||||
</div>
|
||||
{% endblock header %}
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="{% static "js/lib/jquery-3.1.0.min.js" %}"></script>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.2/css/bootstrap.min.css" integrity="sha384-y3tfxAZXuh4HwSYylfB+J125MxIs6mR5FOHamPBG064zB+AFeWH94NdvaCBm8qnd" crossorigin="anonymous">
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
<script src="{% static "js/lib/underscore-min.js" %}"></script>
|
||||
|
||||
<script>
|
||||
$(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip()
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
{% block header %}
|
||||
<div class="kaehmy_header">
|
||||
{% include "kaehmy_header.html" %}
|
||||
</div>
|
||||
{% endblock header %}
|
||||
|
||||
{% block navigation %}
|
||||
{% include "kaehmy_navigation.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
|
||||
<div class="footer">
|
||||
{% block footer %}
|
||||
{% include "kaehmy_footer.html" %}
|
||||
{% endblock footer %}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
<link rel="stylesheet" href="/static/css/kaehmy_footer.css">
|
||||
|
||||
<footer style="text-align: center">
|
||||
<div>
|
||||
<form class="lang-form form" action="{% url 'set_language' %}" method="post">{% csrf_token %}
|
||||
<span>
|
||||
<input name="next" type="hidden" value="{{ redirect_to }}" />
|
||||
<select onchange="this.form.submit()" class="lang-select form-control" name="language">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% for language in languages %}
|
||||
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
|
||||
{{ language.name_local }} ({{ language.code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
</form>
|
||||
<span>{% trans "Copyright Aalto-yliopiston Sähköinsinöörikilta ry" %} {% now 'Y' %}</span>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -1,5 +1,5 @@
|
||||
{% load i18n %}
|
||||
<link rel="stylesheet" href="/static/css/sik_header.css">
|
||||
<link rel="stylesheet" href="/static/css/kaehmy_header.css">
|
||||
|
||||
<div class="kaehmy_header-content">
|
||||
<div class="kaehmy-banner logo">
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>SIK - Login</title>
|
||||
<meta name="viewport" charset="utf-8" content="width=device-width" />
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<script src="{% static "js/lib/jquery-3.1.0.min.js" %}"></script>
|
||||
<script src="{% static "js/lib/bootstrap.min.js" %}"></script>
|
||||
|
||||
<link rel="stylesheet" href="{% static "css/lib/bootstrap.min.css" %}">
|
||||
<link rel="stylesheet" href="{% static "css/base.css" %}">
|
||||
<link rel="stylesheet" href="{% static "css/login.css" %}">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="content-body" class="container">
|
||||
<h1>SIK Admin</h1>
|
||||
<form method="POST" class="form-horizontal" action=""> {% csrf_token %}
|
||||
<div class="form-group row">
|
||||
<label for="input-username" class="col-sm-2 col-form-label">{% trans "Username" %}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="text" name="username" id="input-username" class="form-control" placeholder="{% trans "Username" %}"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="input-password" class="col-sm-2 col-form-label">{% trans "Password" %}</label>
|
||||
<div class="col-sm-10">
|
||||
<input type="password" name="passwd" id="input-passwd" class="form-control" placeholder="{% trans "Password" %}"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<div class="text-danger">{{ error }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row" id="login-button">
|
||||
<div class="col-sm-2">
|
||||
<button type="submit" class="btn btn-primary">{% trans "Log in" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,11 +1,19 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "webapp_base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h3>HURDURR DUMMY SITE</h3>
|
||||
<div>
|
||||
<a href="/members">{% trans "Members" %}</a>
|
||||
<a href="/infoscreen">{% trans "Infoscreen" %}</a>
|
||||
<a href="/admin">{% trans "Admin tools" %}</a>
|
||||
<div class="main_page">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h3>{% trans "Aalto-yliopiston Sähköinsinöörikilta ry" %}</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h5>Lisää vain vesi</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,36 +1,34 @@
|
||||
{% load i18n %}
|
||||
<link rel="stylesheet" href="/static/css/main_nav.css">
|
||||
<div class="main_navigation">
|
||||
<nav class="navbar-border navbar navbar-toggleable-md navbar-light bg-faded">
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/guild">{% trans "Guild" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/freshmen">{% trans "Freshmen" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/event_calendar">{% trans "Event calendar" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/international">{% trans "International" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/sosso">{% trans "Sössö" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-item nav-link" href="/contact">{% trans "Contact" %}</a>
|
||||
</li>
|
||||
<li class="nav-item btn-group">
|
||||
<a class="dropdown-toggle nav-link" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<nav class="navbar navbar-toggleable-md navbar-light bg-faded">
|
||||
<ul class="navbar-nav mx-auto">
|
||||
<li class="nav-item text-center">
|
||||
<a class="nav-link" href="/guild">{% trans "Guild" %}</a>
|
||||
</li>
|
||||
<li class="nav-item text-center">
|
||||
<a class="nav-link" href="/freshmen">{% trans "Freshmen" %}</a>
|
||||
</li>
|
||||
<li class="nav-item text-center">
|
||||
<a class="nav-link" href="/event_calendar">{% trans "Event calendar" %}</a>
|
||||
</li>
|
||||
<li class="nav-item text-center">
|
||||
<a class="nav-link" href="/international">{% trans "International" %}</a>
|
||||
</li>
|
||||
<li class="nav-item text-center">
|
||||
<a class="nav-link" href="/sosso">{% trans "Sössö" %}</a>
|
||||
</li>
|
||||
<li class="nav-item text-center">
|
||||
<a class="nav-item nav-link" href="/contact">{% trans "Contact" %}</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" id="corpDropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{% trans "Corporate" %}
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="corpDropdown">
|
||||
<a class="dropdown-item" href="/jobs">{% trans "Jobs" %}</a>
|
||||
<a class="dropdown-item" href="/">Yritysdadaa</a>
|
||||
<a class="dropdown-item" href="/">Something else here</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="corpDropdown">
|
||||
<a class="dropdown-item" href="/jobs">{% trans "Jobs" %}</a>
|
||||
<a class="dropdown-item" href="/">Yritysdadaa</a>
|
||||
<a class="dropdown-item" href="/">Something else here</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "webapp_base.html" %}
|
||||
|
||||
{% load bootstrap3 %}
|
||||
{% load i18n %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "webapp_base.html" %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% extends "webapp_base.html" %}
|
||||
{% block content %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
<div class="sosso_div">
|
||||
<h2>{% trans "Sössö" %}</h2>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h2>{% trans "Sössö" %}</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load staticfiles %}
|
||||
|
||||
{% block body %}
|
||||
<link rel="stylesheet" href="{% static "css/webapp.css" %}">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{% block header %}
|
||||
{% include "sik_header.html" %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{% block navigation %}
|
||||
{% include "navigation.html" %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{% block footer %}
|
||||
{% include "footer.html" %}
|
||||
{% endblock footer %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user