Initial commit, converting laundry app to site
This commit is contained in:
commit
2e2ee19a6b
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
*~
|
||||
*.log
|
||||
*.pot
|
||||
*.py[cod]
|
||||
*.so
|
||||
*.db
|
||||
*.DS_Store
|
||||
*.swp
|
||||
*.sw[on]
|
||||
venv
|
||||
apache
|
||||
old
|
||||
*db
|
||||
laundry/secrets.py
|
||||
static
|
||||
*.svg
|
1
laundry.json
Normal file
1
laundry.json
Normal file
File diff suppressed because one or more lines are too long
0
laundry/__init__.py
Normal file
0
laundry/__init__.py
Normal file
166
laundry/settings.py
Normal file
166
laundry/settings.py
Normal file
@ -0,0 +1,166 @@
|
||||
# Django settings for laundry project.
|
||||
import os
|
||||
import secrets
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
ADMINS = (
|
||||
('thallada', 'thallada@mit.edu'),
|
||||
('tyler', 'tyler@hallada.net'),
|
||||
)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
PROJECT_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), '..')
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': secrets.DATABASE_NAME,
|
||||
'USER': secrets.DATABASE_USER,
|
||||
'PASSWORD': secrets.DATABASE_PASSWORD,
|
||||
'HOST': secrets.DATABASE_HOST,
|
||||
'PORT': '', # Set to empty string for default. Not used with sqlite3.
|
||||
}
|
||||
}
|
||||
|
||||
# Hosts/domain names that are valid for this site; required if DEBUG is False
|
||||
# See https://docs.djangoproject.com/en/1.5/ref/settings/#allowed-hosts
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
# Local time zone for this installation. Choices can be found here:
|
||||
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
|
||||
# although not all choices may be available on all operating systems.
|
||||
# In a Windows environment this must be set to your system time zone.
|
||||
TIME_ZONE = 'America/New_York'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
# http://www.i18nguy.com/unicode/language-identifiers.html
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# If you set this to False, Django will make some optimizations so as not
|
||||
# to load the internationalization machinery.
|
||||
USE_I18N = True
|
||||
|
||||
# If you set this to False, Django will not format dates, numbers and
|
||||
# calendars according to the current locale.
|
||||
USE_L10N = True
|
||||
|
||||
# If you set this to False, Django will not use timezone-aware datetimes.
|
||||
USE_TZ = True
|
||||
|
||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||
# Example: "/var/www/example.com/media/"
|
||||
MEDIA_ROOT = ''
|
||||
|
||||
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
|
||||
# trailing slash.
|
||||
# Examples: "http://example.com/media/", "http://media.example.com/"
|
||||
MEDIA_URL = ''
|
||||
|
||||
# Absolute path to the directory static files should be collected to.
|
||||
# Don't put anything in this directory yourself; store your static files
|
||||
# in apps' "static/" subdirectories and in STATICFILES_DIRS.
|
||||
# Example: "/var/www/example.com/static/"
|
||||
STATIC_ROOT = os.path.join(PROJECT_PATH, 'static')
|
||||
|
||||
# URL prefix for static files.
|
||||
# Example: "http://example.com/static/", "http://static.example.com/"
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# Additional locations of static files
|
||||
STATICFILES_DIRS = (
|
||||
# Put strings here, like "/home/html/static" or "C:/www/django/static".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
os.path.join(PROJECT_PATH, 'laundry_app', 'static'),
|
||||
)
|
||||
|
||||
# List of finder classes that know how to find static files in
|
||||
# various locations.
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
# 'django.contrib.staticfiles.finders.DefaultStorageFinder',
|
||||
)
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = secrets.SECRET_KEY
|
||||
|
||||
# List of callables that know how to import templates from various sources.
|
||||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
# 'django.template.loaders.eggs.Loader',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
# Uncomment the next line for simple clickjacking protection:
|
||||
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'laundry.urls'
|
||||
|
||||
# Python dotted path to the WSGI application used by Django's runserver.
|
||||
WSGI_APPLICATION = 'laundry.wsgi.application'
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
os.path.join(PROJECT_PATH, 'templates'),
|
||||
)
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.sites',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
# Uncomment the next line to enable the admin:
|
||||
# 'django.contrib.admin',
|
||||
# Uncomment the next line to enable admin documentation:
|
||||
# 'django.contrib.admindocs',
|
||||
'laundry_app',
|
||||
'south',
|
||||
)
|
||||
|
||||
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'
|
||||
|
||||
# A sample logging configuration. The only tangible logging
|
||||
# performed by this configuration is to send an email to
|
||||
# the site admins on every HTTP 500 error when DEBUG=False.
|
||||
# See http://docs.djangoproject.com/en/dev/topics/logging for
|
||||
# more details on how to customize your logging configuration.
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'filters': {
|
||||
'require_debug_false': {
|
||||
'()': 'django.utils.log.RequireDebugFalse'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'mail_admins': {
|
||||
'level': 'ERROR',
|
||||
'filters': ['require_debug_false'],
|
||||
'class': 'django.utils.log.AdminEmailHandler'
|
||||
}
|
||||
},
|
||||
'loggers': {
|
||||
'django.request': {
|
||||
'handlers': ['mail_admins'],
|
||||
'level': 'ERROR',
|
||||
'propagate': True,
|
||||
},
|
||||
}
|
||||
}
|
18
laundry/urls.py
Normal file
18
laundry/urls.py
Normal file
@ -0,0 +1,18 @@
|
||||
from django.conf.urls import patterns, include, url
|
||||
|
||||
# Uncomment the next two lines to enable the admin:
|
||||
from django.contrib import admin
|
||||
admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# Examples:
|
||||
# url(r'^$', 'laundry.views.home', name='home'),
|
||||
# url(r'^laundry/', include('laundry.foo.urls')),
|
||||
|
||||
# Uncomment the admin/doc line below to enable admin documentation:
|
||||
# url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
|
||||
# Uncomment the next line to enable the admin:
|
||||
# url(r'^admin/', include(admin.site.urls)),
|
||||
url(r'', include('laundry_app.urls')),
|
||||
)
|
32
laundry/wsgi.py
Normal file
32
laundry/wsgi.py
Normal file
@ -0,0 +1,32 @@
|
||||
"""
|
||||
WSGI config for laundry project.
|
||||
|
||||
This module contains the WSGI application used by Django's development server
|
||||
and any production WSGI deployments. It should expose a module-level variable
|
||||
named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover
|
||||
this application via the ``WSGI_APPLICATION`` setting.
|
||||
|
||||
Usually you will have the standard Django WSGI application here, but it also
|
||||
might make sense to replace the whole Django WSGI application with a custom one
|
||||
that later delegates to the Django one. For example, you could introduce WSGI
|
||||
middleware here, or combine a Django application with an application of another
|
||||
framework.
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks
|
||||
# if running multiple sites in the same mod_wsgi process. To fix this, use
|
||||
# mod_wsgi daemon mode with each site in its own daemon process, or use
|
||||
# os.environ["DJANGO_SETTINGS_MODULE"] = "laundry.settings"
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "laundry.settings")
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
# setting points here.
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
application = get_wsgi_application()
|
||||
|
||||
# Apply WSGI middleware here.
|
||||
# from helloworld.wsgi import HelloWorldApplication
|
||||
# application = HelloWorldApplication(application)
|
0
laundry_app/__init__.py
Normal file
0
laundry_app/__init__.py
Normal file
120
laundry_app/laundry.py
Normal file
120
laundry_app/laundry.py
Normal file
@ -0,0 +1,120 @@
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
from urllib2 import urlopen
|
||||
import pygal
|
||||
from pygal.style import Style
|
||||
from models import LaundryMachine, LaundryRecord, Timeslot, LaundrySummary
|
||||
import datetime
|
||||
|
||||
WASHER = LaundryMachine.WASHER
|
||||
DRYER = LaundryMachine.DRYER
|
||||
AVAILABLE = LaundryRecord.AVAILABLE
|
||||
IN_USE = LaundryRecord.IN_USE
|
||||
CYCLE_COMPLETE = LaundryRecord.CYCLE_COMPLETE
|
||||
UNAVAILABLE = LaundryRecord.UNAVAILABLE
|
||||
BASE_URL_QUERY = 'http://gmu.esuds.net/RoomStatus/machineStatus.i?bottomLocationId='
|
||||
|
||||
def load_data(hall):
|
||||
"""Extract table data from esuds for specified hall"""
|
||||
htsl = urlopen(BASE_URL_QUERY+str(int(hall.location_id))).read()
|
||||
soup = BeautifulSoup(htsl) # Start cook'n
|
||||
rows = [row for row in soup.findAll('tr')[1:] if len(row('td')) > 1]
|
||||
return rows
|
||||
|
||||
def get_num_machines_per_status(status, records):
|
||||
"""
|
||||
Count the number of machines in a hall which have the desired status and
|
||||
return a list that can be inputed to the pygal add series function.
|
||||
|
||||
Returns a list of two ints. The first element is the number of washers
|
||||
with the desired status and the second is the number of dryers.
|
||||
"""
|
||||
return [len(filter(lambda r: r.machine.type == WASHER and
|
||||
r.availability == status, records)),
|
||||
len(filter(lambda r: r.machine.type == DRYER and
|
||||
r.availability == status, records))]
|
||||
|
||||
def generate_current_chart(filepath, records, hall):
|
||||
"""
|
||||
Generate stacked bar chart of current laundry usage for specified hall and
|
||||
save svg at filepath.
|
||||
"""
|
||||
custom_style = Style(colors=('#B6E354', '#FF5995', '#FEED6C', '#E41B17'))
|
||||
chart = pygal.StackedBar(style=custom_style, width=800, height=512, explicit_size=True)
|
||||
chart.title = 'Current laundry machine usage in ' + hall.name
|
||||
chart.x_labels = ['Washers', 'Dryers']
|
||||
chart.add('Available', get_num_machines_per_status(AVAILABLE, records))
|
||||
chart.add('In Use', get_num_machines_per_status(IN_USE, records))
|
||||
chart.add('Cycle Complete', get_num_machines_per_status(CYCLE_COMPLETE, records))
|
||||
chart.add('Unavailable', get_num_machines_per_status(UNAVAILABLE, records))
|
||||
chart.range = [0, 11]
|
||||
chart.render_to_file(filepath)
|
||||
|
||||
# NOTE: Abandoning generating the weekly chart via mysql and Django for now.
|
||||
# (cron script and csv file is just easier) Sorry if there are a lot of
|
||||
# skeletons lying around as a result of this abandonment.
|
||||
#def generate_weekly_chart(filepath, hall):
|
||||
# First, add a new LaundrySummary for the most recent record.
|
||||
# TODO: determine if this step actually needs to be done or not.
|
||||
#records = list()
|
||||
#for machine in hall.machines:
|
||||
#records.append(machine.get_latest_record())
|
||||
#ts = records[0].timeslot
|
||||
#ts = ts - datetime.timedelta(minutes=ts.minute % 15,
|
||||
#seconds=ts.second, microseconds=ts.microsecond)
|
||||
#day = ts.weekday()
|
||||
#slot = Timeslot.objects.get_or_create(hall=hall, time=ts.time(), day=day)
|
||||
#LaundrySummary.objects.create(timeslot=slot,
|
||||
#washers = len([w for w in records if
|
||||
#(w.machine.type == LaundryMachine.WASHER and
|
||||
#w.availability == LaundryRecord.AVAILABLE)]),
|
||||
#dryers = len([w for w in records if
|
||||
#(w.machine.type == LaundryMachine.DRYER and
|
||||
#w.availability == LaundryRecord.AVAILABLE)]),
|
||||
#)
|
||||
# Now, actually generate the chart by adding all of the LaundrySummaries
|
||||
# to the chart and making all of the Timeslots the x-axis.
|
||||
|
||||
def update(hall, filepath=None):
|
||||
"""
|
||||
Scrape data from esuds for the specified hall, create a new LaundryRecord
|
||||
for each machine, and optionally save a new current chart for the hall.
|
||||
|
||||
To generate a current chart with this update, set filepath to a valid
|
||||
filepath to save and svg file.
|
||||
"""
|
||||
rows = load_data(hall)
|
||||
records = list()
|
||||
for row in rows:
|
||||
cells = row('td')
|
||||
# Some small laundry rooms (with only two machines) do not label their
|
||||
# machines. Our database represents "unlabeled" as 0. We will just have
|
||||
# to rely on the type of the machine when searching.
|
||||
if str(cells[1].contents[0]) == "unlabeled":
|
||||
number = 0
|
||||
else:
|
||||
number = int(cells[1].contents[0])
|
||||
type = cells[2].contents[0]
|
||||
# For some reason there is a distinction between normal and "stacked"
|
||||
# washers/dryers in esuds; we only need normal, so cut "stacked" off.
|
||||
if type == 'Stacked Dryer': type = type[8:]
|
||||
if type == 'Stacked Washer': type = type[8:]
|
||||
# Since Presidents Park - Harrison apparently has freak Washer/Dryer
|
||||
# hybrids, we will just ignore the type field when getting the machine.
|
||||
if type == 'Stacked Washer/Dryer': type = None
|
||||
availability = cells[3].contents[1].contents[0]
|
||||
time_remaining = None
|
||||
if cells[4].contents[0] != ' ':
|
||||
time_remaining = int(cells[4].contents[0])
|
||||
if type:
|
||||
machine = LaundryMachine.objects.get(number=number, type=type,
|
||||
hall=hall)
|
||||
else: # Search without type, for Presidents Park - Harrison
|
||||
machine = LaundryMachine.objects.get(number=number, hall=hall)
|
||||
record = LaundryRecord(machine=machine, availability=availability,
|
||||
time_remaining=time_remaining)
|
||||
if filepath:
|
||||
records.append(record)
|
||||
else:
|
||||
record.save()
|
||||
if filepath:
|
||||
generate_current_chart(filepath, records, hall)
|
54
laundry_app/models.py
Normal file
54
laundry_app/models.py
Normal file
@ -0,0 +1,54 @@
|
||||
from django.db import models
|
||||
|
||||
class Hall(models.Model):
|
||||
name = models.TextField(max_length=100)
|
||||
location_id = models.IntegerField() # id used by esuds
|
||||
|
||||
class LaundryMachine(models.Model):
|
||||
WASHER = 'Washer'
|
||||
DRYER = 'Dryer'
|
||||
MACHINE_TYPES = (
|
||||
('Washer', 'Washer'),
|
||||
('Dryer', 'Dryer'),
|
||||
)
|
||||
number = models.PositiveSmallIntegerField() # number assigned by esuds
|
||||
type = models.TextField(choices=MACHINE_TYPES)
|
||||
hall = models.ForeignKey('Hall', related_name='machine')
|
||||
|
||||
def get_latest_record(self):
|
||||
records = self.records.all()
|
||||
return records[len(records)-1]
|
||||
|
||||
class LaundryRecord(models.Model):
|
||||
AVAILABLE = 'Available'
|
||||
IN_USE = 'In Use'
|
||||
CYCLE_COMPLETE = 'Cycle Complete'
|
||||
UNAVAILABLE = 'Unavailable'
|
||||
AVAILABILITIES = (
|
||||
(AVAILABLE, 'Available'),
|
||||
(IN_USE, 'In Use'),
|
||||
(CYCLE_COMPLETE, 'Cycle Complete'),
|
||||
(UNAVAILABLE, 'Unavailable'),
|
||||
)
|
||||
machine = models.ForeignKey('LaundryMachine', related_name='records')
|
||||
availability = models.TextField(choices=AVAILABILITIES)
|
||||
time_remaining = models.IntegerField(null=True)
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Timeslot(models.Model):
|
||||
hall = models.ForeignKey('Hall', related_name='timeslots')
|
||||
day = models.SmallIntegerField()
|
||||
time = models.TimeField()
|
||||
|
||||
def washer_avg(self):
|
||||
summaries = self.summaries.all()
|
||||
return (sum(s.washers for s in summaries) / (len(summaries)) or 1)
|
||||
|
||||
def dryer_avg(self):
|
||||
summaries = self.summaries.all()
|
||||
return (sum(s.dryers for s in summaries) / (len(summaries)) or 1)
|
||||
|
||||
class LaundrySummary(models.Model):
|
||||
timeslot = models.ForeignKey('Timeslot', related_name='summaries')
|
||||
washers = models.IntegerField()
|
||||
dryers = models.IntegerField()
|
16
laundry_app/tests.py
Normal file
16
laundry_app/tests.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
6
laundry_app/urls.py
Normal file
6
laundry_app/urls.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.conf.urls.defaults import patterns, url
|
||||
|
||||
urlpatterns = patterns('laundry_app.views',
|
||||
url(r'^ajax/current/(?P<hall>\d+)/$', 'ajax_get_current', name='get_current_chart'),
|
||||
url(r'^$', 'main_page', name='laundry_main'),
|
||||
)
|
29
laundry_app/views.py
Normal file
29
laundry_app/views.py
Normal file
@ -0,0 +1,29 @@
|
||||
from django.template import RequestContext
|
||||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from models import Hall
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from os.path import join
|
||||
import laundry
|
||||
|
||||
SVG_DIR = join(settings.PROJECT_PATH, 'laundry_app', 'static')
|
||||
SVG_URL = settings.STATIC_URL
|
||||
|
||||
def main_page(request):
|
||||
# pass the halls to the html, and let cookies/user decide which to pick
|
||||
halls = [(hall.name, hall.id) for hall in
|
||||
Hall.objects.all().order_by('name')]
|
||||
return render_to_response('laundry/main.html', {
|
||||
'halls': halls,
|
||||
},
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
def ajax_get_current(request, hall):
|
||||
hall_obj = get_object_or_404(Hall, pk=hall)
|
||||
filename = str(hall_obj.id) + '_current.svg'
|
||||
try:
|
||||
laundry.update(hall_obj, filepath=join(SVG_DIR, filename))
|
||||
except ObjectDoesNotExist:
|
||||
return HttpResponse(status=500);
|
||||
return HttpResponse(join(SVG_URL, filename))
|
10
manage.py
Executable file
10
manage.py
Executable file
@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "laundry.settings")
|
||||
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
execute_from_command_line(sys.argv)
|
65
templates/laundry/main.html
Normal file
65
templates/laundry/main.html
Normal file
@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
{% block head %}
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
{% block title %}<title>GMU Laundry</title>{% endblock %}
|
||||
{% block description %}
|
||||
<meta name="Description" content="Current laundry machine usage charts for George Mason University's dorms. An experiment by Tyler Hallada.">
|
||||
{% endblock %}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="{{ STATIC_URL }}css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="{{ STATIC_URL }}css/font-awesome.css" rel="stylesheet">
|
||||
<link href="{{ STATIC_URL }}css/bootstrap-responsive.min.css" rel="stylesheet">
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/bootstrap.min.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/laundry.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/jquery.cookie.js"></script>
|
||||
<link href="{{ STATIC_URL }}img/glyphicons-halflings.png" rel="icons">
|
||||
<link href="{{ STATIC_URL }}css/style.css" rel="stylesheet">
|
||||
<!-- Creative Commons - Attribution - The Noun Project -->
|
||||
<link href="/favicon.ico" rel="shortcut icon">
|
||||
{% block head-extra %}{% endblock %}
|
||||
</head>
|
||||
{% endblock %}
|
||||
<script>
|
||||
var halls = {
|
||||
{% for h in halls %}
|
||||
"{{ h.0 }}" : {{ h.1 }},
|
||||
{% endfor %}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body {
|
||||
background:#000000;
|
||||
}
|
||||
</style>
|
||||
<body class="laundry">
|
||||
<div class="container">
|
||||
<div class="row hall-select-container">
|
||||
<select id="hall-select">
|
||||
{% for h in halls %}
|
||||
{% if h.1 == 1 %}
|
||||
<option selected="selected">{{ h.0 }}</option>
|
||||
{% else %}
|
||||
<option>{{ h.0 }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="row chart-container">
|
||||
<div class="current-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-39880341-1', 'hallada.net');
|
||||
ga('send', 'pageview');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user