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;
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
View File
@@ -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 = "&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();
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);
+6 -2
View File
@@ -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">&nbsp;+</div>
<div id="scale"><div id="scale2"></div></div>
</div>
</div>
-28
View File
@@ -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 -3
View File
@@ -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),
]
-16
View File
@@ -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
View File
@@ -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&center_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
+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()
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 {
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 {
+11 -13
View File
@@ -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&#228;kki
</th>
<th>
P&#228;&#228;tepys&#228;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>
+24 -106
View File
@@ -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);
}
);
-1
View File
@@ -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
+12 -14
View File
@@ -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
View File
@@ -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
View File
@@ -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'
+2
View File
@@ -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
View File
@@ -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;
}
}*/
+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 %}
<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> -->
+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;
}
}*/
-24
View File
@@ -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%;
}
+5 -22
View File
@@ -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;
}
+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 %}
{% 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 %}
{% 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 %}
+8 -2
View File
@@ -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 %}
+8 -2
View File
@@ -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 %}
+8 -2
View File
@@ -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 %}
+8 -2
View File
@@ -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 %}
+8 -2
View File
@@ -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 %}
+46 -10
View File
@@ -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>
+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 %}
<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">
-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 %}
{% 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 %}
+31 -33
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
{% extends "base.html" %}
{% extends "webapp_base.html" %}
{% load bootstrap3 %}
{% load i18n %}
+1 -1
View File
@@ -1,4 +1,4 @@
{% extends "base.html" %}
{% extends "webapp_base.html" %}
{% load static %}
{% load i18n %}
+8 -2
View File
@@ -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 %}
+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 %}