Merge branch 'develop' into 'master'

Develop

Closes #91

See merge request vtmk/web2.0!96
This commit is contained in:
Jan Tuomi
2017-11-01 14:22:31 +02:00
56 changed files with 892 additions and 702 deletions
-23
View File
@@ -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))
-58
View File
@@ -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
+7 -4
View File
@@ -47,7 +47,7 @@ body {
background: green; background: green;
border-radius: 10px; border-radius: 10px;
} }
#brewtime{ .brewtime{
text-align:right; text-align:right;
position:absolute; position:absolute;
right:0px; right:0px;
@@ -62,10 +62,13 @@ body {
font-size:4vw; font-size:4vw;
color: #333; color: #333;
} }
.layertwo{
display: None;
}
noscript{ noscript{
color:red; color:red;
} }
#text{ .text{
color:green; color:green;
position:absolute; position:absolute;
top:50%; top:50%;
@@ -102,9 +105,9 @@ noscript{
} }
@keyframes coffeeready { @keyframes coffeeready {
0% {background-color:white;} 0% {background-color:white;}
25% {background-color:green;} 25% {background-color:rgb(100, 255, 100);}
50% {background-color:white;} 50% {background-color:white;}
75% {background-color:green;} 75% {background-color:rgb(100, 255, 100);}
100% {background-color:white;} 100% {background-color:white;}
} }
@keyframes unknown { @keyframes unknown {
+142 -111
View File
@@ -1,130 +1,161 @@
var len = 0; //Inner state
var lastBrew = "∞"; var lastBrew = new Date(0);
var brewtext = ""; var brewing = false;
var backoff = 2000;
$(document).ready(function(){ //MQTT client config
$('#text').bind("DOMSubtreeModified", resize); var username = "coffee-user-"+ Math.random();
updateTime(); // eslint-disable-next-line no-undef
setInterval(updateTime,1000); var client = new Paho.MQTT.Client("sika.sahkoinsinoorikilta.fi", 9001, username);
formatBrewTime(); client.onMessageArrived = function (message) {
setInterval(formatBrewTime,10000); // 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 reconnect(responseObject){
if (responseObject.errorCode !== 0) {
function fetchdata(data, status){ console.log("connection lost! Reason: "+responseObject.errorMessage); // eslint-disable-line no-console
if (typeof status !== 'undefined'){ setTimeout(function(){
if (status == "success"){ client.connect({onSuccess:onConnect, useSSL:true, onFailure: reconnect});
parseData(data); }, backoff);
} }
else if (status == "error"){
handleError();
}
}
} }
$.getJSON("/coffee/cups", fetchdata); function onConnect() {
setInterval(function() { console.log("MQTT connected"); // eslint-disable-line no-console
$.getJSON("/coffee/cups", fetchdata); //set and reset reconnector
}, 2000); client.onConnectionLost = reconnect
// subscribe to topics
function formatBrewTime(){ client.subscribe("sik/kiltahuone/kahvivaaka/cups");
if (!brewtext && lastBrew instanceof Date){ client.subscribe("sik/kiltahuone/kahvivaaka/brewing");
var now = new Date(); client.subscribe("sik/kiltahuone/kahvivaaka/brewtime");
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);
} }
// 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() { function tick(){
setData("?", 0, 0, Number.MAX_VALUE, Number.MAX_VALUE); var ev = new CustomEvent("tick", {'detail': new Date()});
window.dispatchEvent(ev);
} }
function parseData(data) { function updateTime(ev){
if (data) { var now = ev.detail;
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 = "&infin;";
$("#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();
$("#time").html(formatTime(now.getHours(),now.getMinutes(),now.getSeconds())); $("#time").html(formatTime(now.getHours(),now.getMinutes(),now.getSeconds()));
} }
function formatTime(hours, minutes, seconds){ function coffeeLowEffect(ev){
var str = ""; ev.detail <= 2 ? $("#text").addClass("hurry") : $("#text").removeClass("hurry");
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 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(){ function resize(){
var w = $("#container").width(); var w = $("#container").width();
var h = $("#container").height(); var h = $("#container").height();
var s = w > h ? h : w; var s = w > h ? h : w;
var font = s*0.8*0.38/Math.sqrt(len); var font = s * 0.8 * 0.38/Math.sqrt(3);
$("#text").css({ top: s*0.16-font/2 + 'px', fontSize: font + 'px', marginLeft: -font*len*3/10 + 'px'}); $(".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);
+6 -2
View File
@@ -10,13 +10,16 @@
<meta http-equiv="pragma" content="no-cache" /> <meta http-equiv="pragma" content="no-cache" />
<meta name="viewport" content="width=device-width, initial-scale=1"> <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://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" /> <link rel="stylesheet" href="/static/css/coffee.css" />
<script src="/static/js/coffee.js"></script> <script src="/static/js/coffee.js"></script>
</head> </head>
<body> <body>
<div id="container"> <div id="container">
<span id="brewtime"></span> <span id="brewtime" class="brewtime layerone"></span>
<span class="brewtime layertwo">:)</span>
<span id="address"> <span id="address">
ka.dy.fi ka.dy.fi
<noscript><br>This page uses JavaScript!</noscript> <noscript><br>This page uses JavaScript!</noscript>
@@ -26,7 +29,8 @@
</div> </div>
<!--Kahvinkeitin on rikki. Varakeittimellä keitettyä kahvia saattaa olla.--> <!--Kahvinkeitin on rikki. Varakeittimellä keitettyä kahvia saattaa olla.-->
<div id="lower" class="normal"> <div id="lower" class="normal">
<div id="text"></div> <div id="text" class="text layerone">???</div>
<div class="text layertwo">&nbsp;+</div>
<div id="scale"><div id="scale2"></div></div> <div id="scale"><div id="scale2"></div></div>
</div> </div>
</div> </div>
-28
View File
@@ -1,30 +1,2 @@
from django.test import TestCase, Client from django.test import TestCase, Client
from django.conf import settings 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 -3
View File
@@ -1,7 +1,7 @@
from django.conf.urls import url from django.conf.urls import url
from django.views.generic.base import RedirectView 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) favicon_view = RedirectView.as_view(url='static/img/favicon.ico', permanent=True)
@@ -9,6 +9,4 @@ urlpatterns = [
# landing page # landing page
url(r'^$', coffee_view), url(r'^$', coffee_view),
url(r'^cups', cups_view),
] ]
-16
View File
@@ -3,25 +3,9 @@ from django.http import JsonResponse
from django.utils import timezone from django.utils import timezone
from .mqtt import get_latest
import coffee_scale.mqtt # somehow this is needed
import logging import logging
from django.conf import settings from django.conf import settings
def coffee_view(request): def coffee_view(request):
return render(request, 'coffee.html') 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
View File
@@ -1,73 +1,70 @@
"""File containing Infoscreen HSL data fetcher classes.""" """File containing Infoscreen HSL data fetcher classes."""
import urllib.request import requests
import json import json
import logging import logging
import os
import pytz
from datetime import timedelta, datetime 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 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: def fetch():
"""Main class of Infoscreen HSL fetcher.""" """Fetch data from HSL API."""
last_fetched = datetime.fromtimestamp(86400) # epoch query_vars = STOPS_VARS.copy()
INTERVAL = 1 # minutes query_vars['startTime_6'] = format(timezone.now(), 'U')
# logging.info(
# "Set up scheduled HSL API fetch every {} minutes".format(INTERVAL))
def fetch_if_needed(self): post_data = json.dumps({
"""Check if new fetch from HSL API is needed.""" 'operationName': 'NearestRoutesContainer',
if (timezone.now() - HSLFetcher.last_fetched > 'query': STOPS_QUERY,
timedelta(minutes=HSLFetcher.INTERVAL)): 'variables': query_vars,
self.fetch() })
def fetch(self): resp = requests.post(API_URL, data=post_data, headers=API_HEADERS)
"""Fetch data from HSL API."""
location_coords = (2545565, 6675319)
src = urllib.request.urlopen(
("https://api.reittiopas.fi/hsl/prod/?userhash={}"
"&request=stops_area&center_coordinate={},{}")
.format(settings.HSL_USERHASH, location_coords[0],
location_coords[1]))\
.read().decode("utf-8")
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() + schedule = []
timedelta(minutes=settings.HSL_DEPARTURE_THRESHOLD)) for place in places:
time = "{0:02d}{0:02d}".format(time.hour, time.minute) route = place['pattern']['route']['shortName']
for element in data: stop_times = place['_stoptimes']
src = urllib.request.urlopen( for stop_time in stop_times:
("https://api.reittiopas.fi/hsl/prod/?userhash={}" timestamp = time_utc = stop_time['serviceDay'] + stop_time['realtimeArrival']
"&request=stop&code={}&dep_limit=20&time={}") headsign = stop_time['stopHeadsign']
.format(settings.HSL_USERHASH, element['code'], time) stop_name = stop_time['stop']['name']
).read().decode("utf-8") time_diff = (timestamp - timezone.now().timestamp()) / 60 # minutes
parsed = json.loads(src)[0] if time_diff < settings.HSL_DEPARTURE_THRESHOLD:
arr.append({ continue
"name": parsed['name_fi'], elif time_diff < settings.HSL_HURRY_THRESHOLD:
"lines": parsed['lines'], time = '{} min'.format(int(time_diff))
"dist": element['dist'], else:
"departures": parsed['departures']}) time = pytz.utc.localize(datetime.fromtimestamp(timestamp)).strftime('%H:%M')
model_arr = HSLDataModel.objects.all() schedule.append({
count = len(model_arr) 'route': route,
json_dump = json.dumps(arr) 'headsign': headsign,
'timestamp': time,
'stop': stop_name,
'utc': time_utc,
})
if count == 0: return schedule
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)))
+71
View File
@@ -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
}
}
+10
View File
@@ -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',
),
]
-6
View File
@@ -406,9 +406,3 @@ class UploadFileForm(forms.Form):
name = forms.CharField() name = forms.CharField()
video = forms.FileField() video = forms.FileField()
class HSLDataModel(models.Model):
"""Model representing HSL data."""
data = models.TextField(default="", editable=False)
+3 -3
View File
@@ -1,5 +1,5 @@
table { table {
font-size: 5vh; font-size: 4vh;
font-family: 'Droid Sans Mono', monospace; font-family: 'Droid Sans Mono', monospace;
} }
.red { .red {
@@ -58,9 +58,9 @@ thead{
margin-left: 0; margin-left: 0;
margin-right: 0; margin-right: 0;
} }
.repeat-item.ng-leave { .repeat-item.ng-leave {
-webkit-transition:0.5s linear all;
transition:0.5s linear all;
} }
.repeat-item.ng-leave.ng-leave-active { .repeat-item.ng-leave.ng-leave-active {
+11 -13
View File
@@ -1,12 +1,16 @@
<link rel="stylesheet" href="/static/css/hsl.css"> <link rel="stylesheet" href="/static/css/hsl.css">
<link href="https://fonts.googleapis.com/css?family=Droid+Sans+Mono" rel="stylesheet"> <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="container" ng-app="myApp" ng-controller="timetableCtrl">
<div class="header-row row"> <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-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> </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> <thead>
<tr> <tr>
<th> <th>
@@ -18,25 +22,19 @@
<th> <th>
Pys&#228;kki Pys&#228;kki
</th> </th>
<th>
P&#228;&#228;tepys&#228;kki
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr class="repeat-item" ng-repeat="x in arr | orderBy: ['date','time'] | limitTo: 10"> <tr class="repeat-item" ng-repeat="x in stoptimes | orderBy: ['utc'] | limitTo: 11">
<td ng-class='{red : x.hurry, black: !x.hurry}'> <td style="min-width: 300px">
{{x.timedelta < 10 ?x.timedelta + ' min' : x.time}} {{x.timestamp}}
</td> </td>
<td> <td>
{{x.bus}} <strong>{{x.route}}</strong>, {{x.headsign}}
</td> </td>
<td> <td>
{{x.stop}} {{x.stop}}
</td> </td>
<td>
{{x.laststop}}
</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
+24 -106
View File
@@ -1,4 +1,5 @@
var app = angular.module('infoApp', ['ngAnimate', 'ngRoute']); var app = angular.module('infoApp', ['ngAnimate', 'ngRoute']);
app.controller('infoscreen_main', function($scope,$http,$timeout){ app.controller('infoscreen_main', function($scope,$http,$timeout){
var templates = []; var templates = [];
$scope.init = function(rot){ $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', app.controller('timetableCtrl',
function($scope, $http, $interval) { function($scope, $http, $interval) {
function load(){ function load() {
$http.get('/infoscreen/hsl_data') $http.get('/infoscreen/hsl_data')
.then(function(data, status, headers, config) { //eslint-disable-line no-unused-vars .then(function(data, status, headers, config) { //eslint-disable-line no-unused-vars
$scope.arr=[]; $scope.stoptimes = data.data;
parse(data); $scope.error = data.data.error || null;
}); });
$http.get('/infoscreen/hsl_data/settings') $http.get('/infoscreen/hsl_data/settings')
.then(function(data, status, headers, config) { //eslint-disable-line no-unused-vars .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.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; function update_clock() {
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(){
$scope.clock = Date.now(); $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(); load();
var inter1=$interval(delOld,2000);
var inter2=$interval(load,10000);
var inter3=$interval(updateTime, 1000);
} }
); );
-1
View File
@@ -20,7 +20,6 @@ from infoscreen.models import (ABBInfoItem, ExternalImageInfoItem,
from infoscreen.models import EventInfoItem from infoscreen.models import EventInfoItem
from infoscreen.models import ExternalWebsiteInfoItem from infoscreen.models import ExternalWebsiteInfoItem
from infoscreen.models import ImageUploadForm from infoscreen.models import ImageUploadForm
from infoscreen.models import HSLDataModel
from infoscreen.models import ApyInfoItem from infoscreen.models import ApyInfoItem
from infoscreen.models import VideoInfoItem from infoscreen.models import VideoInfoItem
+12 -14
View File
@@ -1,12 +1,14 @@
from django.shortcuts import render from django.shortcuts import render
from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest from django.http import HttpResponse, JsonResponse, HttpResponseBadRequest
from django.views.decorators.http import require_http_methods from django.views.decorators.http import require_http_methods
from django.conf import settings
from infoscreen.models import Rotation, InfoItem, InfoInstance 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 json
import logging import logging
import threading
@require_http_methods(["GET"]) @require_http_methods(["GET"])
@@ -82,22 +84,18 @@ def hsl_timetable_settings(request, *args, **kwargs):
"""Set HSL timetable settings.""" """Set HSL timetable settings."""
d = {"departure_threshold": settings.HSL_DEPARTURE_THRESHOLD, d = {"departure_threshold": settings.HSL_DEPARTURE_THRESHOLD,
"hurry_threshold": settings.HSL_HURRY_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"]) @require_http_methods(["GET"])
def CurrentHSLView(request, *args, **kwargs): def CurrentHSLView(request, *args, **kwargs):
"""Get HSL data and return it.""" """Get HSL data and return it."""
fetcher = HSLFetcher() try:
fetcherThread = threading.Thread(target=fetcher.fetch_if_needed, args=[]) api_resp = hsl_fetch()
fetcherThread.setDaemon(False) except Exception as ex:
fetcherThread.start() logging.exception('Failed to fetch HSL timetables.')
error = {'error': 'Aikataulujen haku epäonnistui.'}
return JsonResponse(error, status=200)
data = HSLDataModel.objects.all() return JsonResponse(api_resp, status=200, safe=False)
if len(data) < 1:
return HttpResponse(
'{"error" : "Could not find timetables from database."}',
status=500)
return HttpResponse(data[len(data) - 1].data, status=200)
+1 -1
View File
@@ -25,8 +25,8 @@ dealer==2.0.5
django-modeltranslation==0.12.1 django-modeltranslation==0.12.1
django-auditlog==0.4.3 django-auditlog==0.4.3
django-phonenumber-field==1.3.0 django-phonenumber-field==1.3.0
paho-mqtt==1.3.0
django-autocomplete-light==3.2.10 django-autocomplete-light==3.2.10
six==1.10.0 six==1.10.0
django-suit==0.2.25 django-suit==0.2.25
telepot==12.3 telepot==12.3
django-password-reset==1.0
+2 -1
View File
@@ -78,13 +78,14 @@ INSTALLED_APPS = [
'webapp', 'webapp',
'members', 'members',
'infoscreen', 'infoscreen',
'coffee_scale.apps.CoffeeScaleConfig', 'coffee_scale',
'rest_framework', 'rest_framework',
'django_nose', 'django_nose',
'bootstrap3', 'bootstrap3',
'django_tables2', 'django_tables2',
'auditlog', 'auditlog',
'phonenumber_field', 'phonenumber_field',
'password_reset',
] ]
TEST_RUNNER = 'django_nose.NoseTestSuiteRunner' TEST_RUNNER = 'django_nose.NoseTestSuiteRunner'
+2
View File
@@ -26,6 +26,7 @@ import webapp.urls
import infoscreen.urls import infoscreen.urls
import members.urls import members.urls
import coffee_scale.urls import coffee_scale.urls
import password_reset.urls
urlpatterns = [ urlpatterns = [
url(r'', include('webapp.urls')), url(r'', include('webapp.urls')),
@@ -35,6 +36,7 @@ urlpatterns = [
# admin # admin
url(r'^admin/', admin.site.urls), url(r'^admin/', admin.site.urls),
url(r'^reset/', include('password_reset.urls')),
# i18n default view for changing the active language # i18n default view for changing the active language
url(r'^i18n/', include('django.conf.urls.i18n')), url(r'^i18n/', include('django.conf.urls.i18n')),
+3 -13
View File
@@ -1,18 +1,8 @@
#footer-div {
height:10vh;
}
footer { footer {
/* position: absolute; */ background-color: #f5f5f5;
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 { /*footer .container .col .nav .nav-item {
display: inline-block; display: inline-block;
margin-right: 3vh; margin-right: 3vh;
} }
@@ -31,4 +21,4 @@ footer .container .col .nav .nav-item {
footer .lang-form { footer .lang-form {
margin: 1rem auto 0; margin: 1rem auto 0;
} }*/
+36
View File
@@ -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
View File
@@ -3,35 +3,69 @@
{% load staticfiles %} {% load staticfiles %}
<link rel="stylesheet" href="{% static "css/footer.css" %}"> <link rel="stylesheet" href="{% static "css/footer.css" %}">
<footer style="text-align: center"> <footer class="footer">
<div> <div class="container">
<form class="lang-form form" action="{% url 'set_language' %}" method="post">{% csrf_token %} <div class="d-flex align-items-center justify-content-end">
<span> <div class="p-2">
<input name="next" type="hidden" value="{{ redirect_to }}" /> <span>{% trans "Copyright Aalto-yliopiston Sähköinsinöörikilta ry" %} {% now 'Y' %}</span>
<select onchange="this.form.submit()" class="lang-select form-control" name="language"> </div>
{% get_current_language as LANGUAGE_CODE %} <div class="p-2">
{% get_available_languages as LANGUAGES %} <form class="lang-form form" action="{% url 'set_language' %}" method="post">{% csrf_token %}
{% get_language_info_list for LANGUAGES as languages %} <span class="form-group">
{% for language in languages %} <input name="next" type="hidden" value="{{ redirect_to }}" />
<option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}> <select onchange="this.form.submit()" class="lang-select form-control" name="language">
{{ language.name_local }} ({{ language.code }}) {% get_current_language as LANGUAGE_CODE %}
</option> {% get_available_languages as LANGUAGES %}
{% endfor %} {% get_language_info_list for LANGUAGES as languages %}
</select> {% for language in languages %}
</span> <option value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
</form> {{ language.name_local }} ({{ language.code }})
<span>{% trans "Copyright Aalto-yliopiston Sähköinsinöörikilta ry" %} {% now 'Y' %}</span> </option>
</div> {% endfor %}
<div> </select>
<span class="nav-item"> </span>
<a href="/members"><i class="fa fa-group"></i></a> </form>
</span> </div>
<span class="nav-item"> <div class="ml-auto p-2">
<a href="/infoscreen"><i class="fa fa-info"></i></a> <span class="nav-item">
</span> <a href="/members"><i class="fa fa-group"></i></a>
<span class="nav-item"> </span>
<a href="/admin"><i class="fa fa-gears"></i></a> <span class="nav-item">
</span> <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>
</div> </div>
</footer> </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> -->
+31
View File
@@ -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 %}
+22
View File
@@ -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 %}
+1
View File
@@ -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 %}
+14
View File
@@ -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 %}
+15
View File
@@ -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 %}
+8
View File
@@ -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 %}
+34
View File
@@ -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;
}
+37
View File
@@ -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%;
}
+2 -2
View File
@@ -1,3 +1,3 @@
.main_navigation .navbar-border .navbar-nav .nav-item { /*.main_navigation .navbar-border .navbar-nav .nav-item {
display: block; display: block;
} }*/
-24
View File
@@ -11,27 +11,3 @@
height: auto; height: auto;
margin: 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%;
}
+5 -22
View File
@@ -1,26 +1,9 @@
.page-content { html {
margin-top: 1vh; position: relative;
width: 90%; min-height: 100%;
margin-left: auto;
margin-right: auto;
} }
body { body {
max-width: 1000px; /*Margin bottom by footer hight*/
margin-left: auto !important; margin-bottom: 60px;
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;
}
+26
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "webapp_base.html" %}
{% load i18n %} {% load i18n %}
{% block content %} {% block content %}
-52
View File
@@ -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>
+8 -2
View File
@@ -1,10 +1,16 @@
{% extends "base.html" %} {% extends "webapp_base.html" %}
{% block content %} {% block content %}
{% load i18n %} {% load i18n %}
<div class="contact_div"> <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> </div>
{% endblock %} {% endblock %}
+8 -2
View File
@@ -1,10 +1,16 @@
{% extends "base.html" %} {% extends "webapp_base.html" %}
{% block content %} {% block content %}
{% load i18n %} {% load i18n %}
<div class="event_calendar_div"> <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> </div>
{% endblock %} {% endblock %}
+8 -2
View File
@@ -1,10 +1,16 @@
{% extends "base.html" %} {% extends "webapp_base.html" %}
{% block content %} {% block content %}
{% load i18n %} {% load i18n %}
<div class="freshmen_div"> <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> </div>
{% endblock %} {% endblock %}
+8 -2
View File
@@ -1,10 +1,16 @@
{% extends "base.html" %} {% extends "webapp_base.html" %}
{% block content %} {% block content %}
{% load i18n %} {% load i18n %}
<div class="guild_div"> <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> </div>
{% endblock %} {% endblock %}
+8 -2
View File
@@ -1,10 +1,16 @@
{% extends "base.html" %} {% extends "webapp_base.html" %}
{% block content %} {% block content %}
{% load i18n %} {% load i18n %}
<div class="international_div"> <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> </div>
{% endblock %} {% endblock %}
+8 -2
View File
@@ -1,10 +1,16 @@
{% extends "base.html" %} {% extends "webapp_base.html" %}
{% block content %} {% block content %}
{% load i18n %} {% load i18n %}
<div class="jobs_div"> <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> </div>
{% endblock %} {% endblock %}
+46 -10
View File
@@ -1,14 +1,50 @@
{% extends "base.html" %} <!DOCTYPE html>
{% load static %}
{% load i18n %} {% load i18n %}
{% load static %}
{% load staticfiles %}
{% block navigation %} <html lang="en">
{% include "kaehmy_navigation.html" %} <head>
{% endblock %} <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 %} <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<div class="kaehmy_header"> <script src="{% static "js/lib/jquery-3.1.0.min.js" %}"></script>
{% include "kaehmy_header.html" %} <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">
</div> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
{% endblock header %} <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>
+25
View File
@@ -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 -1
View File
@@ -1,5 +1,5 @@
{% load i18n %} {% 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_header-content">
<div class="kaehmy-banner logo"> <div class="kaehmy-banner logo">
-49
View File
@@ -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>
+14 -6
View File
@@ -1,11 +1,19 @@
{% extends "base.html" %} {% extends "webapp_base.html" %}
{% load i18n %} {% load i18n %}
{% block content %} {% block content %}
<h3>HURDURR DUMMY SITE</h3> <div class="main_page">
<div> <div class="container">
<a href="/members">{% trans "Members" %}</a> <div class="row">
<a href="/infoscreen">{% trans "Infoscreen" %}</a> <div class="col">
<a href="/admin">{% trans "Admin tools" %}</a> <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> </div>
{% endblock %} {% endblock %}
+31 -33
View File
@@ -1,36 +1,34 @@
{% load i18n %} {% load i18n %}
<link rel="stylesheet" href="/static/css/main_nav.css"> <link rel="stylesheet" href="/static/css/main_nav.css">
<div class="main_navigation"> <nav class="navbar navbar-toggleable-md navbar-light bg-faded">
<nav class="navbar-border navbar navbar-toggleable-md navbar-light bg-faded"> <ul class="navbar-nav mx-auto">
<ul class="navbar-nav"> <li class="nav-item text-center">
<li class="nav-item"> <a class="nav-link" href="/guild">{% trans "Guild" %}</a>
<a class="nav-link" href="/guild">{% trans "Guild" %}</a> </li>
</li> <li class="nav-item text-center">
<li class="nav-item"> <a class="nav-link" href="/freshmen">{% trans "Freshmen" %}</a>
<a class="nav-link" href="/freshmen">{% trans "Freshmen" %}</a> </li>
</li> <li class="nav-item text-center">
<li class="nav-item"> <a class="nav-link" href="/event_calendar">{% trans "Event calendar" %}</a>
<a class="nav-link" href="/event_calendar">{% trans "Event calendar" %}</a> </li>
</li> <li class="nav-item text-center">
<li class="nav-item"> <a class="nav-link" href="/international">{% trans "International" %}</a>
<a class="nav-link" href="/international">{% trans "International" %}</a> </li>
</li> <li class="nav-item text-center">
<li class="nav-item"> <a class="nav-link" href="/sosso">{% trans "Sössö" %}</a>
<a class="nav-link" href="/sosso">{% trans "Sössö" %}</a> </li>
</li> <li class="nav-item text-center">
<li class="nav-item"> <a class="nav-item nav-link" href="/contact">{% trans "Contact" %}</a>
<a class="nav-item nav-link" href="/contact">{% trans "Contact" %}</a> </li>
</li> <li class="nav-item dropdown">
<li class="nav-item btn-group"> <a class="nav-link dropdown-toggle" id="corpDropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<a class="dropdown-toggle nav-link" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{% trans "Corporate" %} {% trans "Corporate" %}
</a> </a>
<div class="dropdown-menu" aria-labelledby="corpDropdown"> <div class="dropdown-menu" aria-labelledby="corpDropdown">
<a class="dropdown-item" href="/jobs">{% trans "Jobs" %}</a> <a class="dropdown-item" href="/jobs">{% trans "Jobs" %}</a>
<a class="dropdown-item" href="/">Yritysdadaa</a> <a class="dropdown-item" href="/">Yritysdadaa</a>
<a class="dropdown-item" href="/">Something else here</a> <a class="dropdown-item" href="/">Something else here</a>
</div> </div>
</li> </li>
</ul> </ul>
</nav> </nav>
</div>
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "webapp_base.html" %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load i18n %} {% load i18n %}
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends "base.html" %} {% extends "webapp_base.html" %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
+8 -2
View File
@@ -1,10 +1,16 @@
{% extends "base.html" %} {% extends "webapp_base.html" %}
{% block content %} {% block content %}
{% load i18n %} {% load i18n %}
<div class="sosso_div"> <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> </div>
{% endblock %} {% endblock %}
+38
View File
@@ -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 %}