From c0b1f88c714bcc1a813f38b92682b4a93ff67e8f Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Thu, 25 Apr 2013 10:05:22 -0700 Subject: [PATCH 01/16] remove the servlet filter comment, it's too yelp-specific --- kohlrabi/handlers.py | 1 + templates/report.html | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/kohlrabi/handlers.py b/kohlrabi/handlers.py index 1a81515..e1ccfad 100644 --- a/kohlrabi/handlers.py +++ b/kohlrabi/handlers.py @@ -135,6 +135,7 @@ def get(self): self.env['date'] = date self.env['data'] = getattr(db, tbl).report_data(date) self.env['columns'] = getattr(db, tbl).html_table + self.env['first_column'] = getattr(db, tbl).html_table[0].display self.env['title'] = getattr(db, tbl).display_name + ' ' + date.strftime('%Y-%m-%d') self.render('report.html') diff --git a/templates/report.html b/templates/report.html index ea77a07..c38d300 100644 --- a/templates/report.html +++ b/templates/report.html @@ -13,10 +13,12 @@ {% block body %}
Back to All Reports
{{escape(title)}}
- + + {% for c in columns %} From 7b7540903337992c8a28ba92aff98c6c7ad7f101 Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Thu, 25 Apr 2013 10:14:15 -0700 Subject: [PATCH 02/16] add a requirements.txt --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c944083 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +PyYAML +sqlalchemy +tornado From 19e36971e24491a610d50a3b2b7bcc856e143351 Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Thu, 25 Apr 2013 10:17:54 -0700 Subject: [PATCH 03/16] add syntax highlighting to the README file --- README.md | 94 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 33d7bf8..9890a35 100644 --- a/README.md +++ b/README.md @@ -32,29 +32,31 @@ Adding New Reports It's easiest to explain this with an example. Suppose the report module specified by the config `module` variable has the following code in it: - from sqlalchemy import * - from kohlrabi.db import * - - class DailySignups(Base): - __tablename__ = 'daily_signups_report' - __metaclass__ = ReportMeta - - id = Column(Integer, primary_key=True) - date = Column(Date, nullable=False) - referrer = Column(String, nullable=False) - clickthroughs = Column(Integer, nullable=False, default=0) - signups = Column(Integer, nullable=False, default=0) - - display_name = 'Daily Signups' - html_table = [ - ReportColumn('Referrer', 'referrer'), - ReportColumn('Click-Throughs', 'clickthroughs'), - ReportColumn('Signups', 'signups'), - ] - - @classmethod - def report_data(cls, date): - return session.query(cls).filter(cls.date == date).order_by(cls.signups.id) +```python +from sqlalchemy import * +from kohlrabi.db import * + +class DailySignups(Base): + __tablename__ = 'daily_signups_report' + __metaclass__ = ReportMeta + + id = Column(Integer, primary_key=True) + date = Column(Date, nullable=False) + referrer = Column(String, nullable=False) + clickthroughs = Column(Integer, nullable=False, default=0) + signups = Column(Integer, nullable=False, default=0) + + display_name = 'Daily Signups' + html_table = [ + ReportColumn('Referrer', 'referrer'), + ReportColumn('Click-Throughs', 'clickthroughs'), + ReportColumn('Signups', 'signups'), + ] + + @classmethod + def report_data(cls, date): + return session.query(cls).filter(cls.date == date).order_by(cls.signups.id) +``` This is a data source that will track users who sign up on your site, based on the HTTP `Referrer` header. The table has three columns: `referrer` will track @@ -68,15 +70,17 @@ have any indexes. In most cases you should probably at least add an index on the `date` column, and probably an index on the full set of columns you plan on querying from the `report_data` method: - CREATE TABLE daily_signups_report ( - id INTEGER NOT NULL, - date DATE NOT NULL, - referrer VARCHAR NOT NULL, - clickthroughs INTEGER NOT NULL, - signups INTEGER NOT NULL, - PRIMARY KEY (id) - ); - CREATE INDEX daily_signups_date_idx ON daily_signups_report (date, signups); +```sql +CREATE TABLE daily_signups_report ( + id INTEGER NOT NULL, + date DATE NOT NULL, + referrer VARCHAR NOT NULL, + clickthroughs INTEGER NOT NULL, + signups INTEGER NOT NULL, + PRIMARY KEY (id) +); +CREATE INDEX daily_signups_date_idx ON daily_signups_report (date, signups); +``` OK, that's all the setup you need to do on the Kohlrabi side of things: create a Python SQLAlchemy class, and create a table in your SQLite database. The second @@ -92,18 +96,20 @@ and the following POST parameters: For instance, if we were running Kohlrabi on `http://localhost:8888`, then the following Python code would generate a sample report for 2001-01-1: - import json - import urllib - - urllib.urlopen('http://localhost:8888/upload', - urllib.urlencode({'date': '2010-01-01', - 'data': json.dumps([{'referrer': 'www.yahoo.com', - 'clickthroughs': 100, - 'signups': 7}, - {'referrer': 'www.google.com', - 'clickthroughs': 500, - 'signups': 42}]), - 'table': 'DailySignups'})) +```python +import json +import urllib + +urllib.urlopen('http://localhost:8888/upload', + urllib.urlencode({'date': '2010-01-01', + 'data': json.dumps([{'referrer': 'www.yahoo.com', + 'clickthroughs': 100, + 'signups': 7}, + {'referrer': 'www.google.com', + 'clickthroughs': 500, + 'signups': 42}]), + 'table': 'DailySignups'})) +``` Just to reiterate: because the interface to Kohlrabi is a normal HTTP request using JSON, you can use any language to send data to Kohlrabi. You can use Java, From 8f184369537590194f28f7072ef05671c76bb403 Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Fri, 26 Apr 2013 12:00:00 -0700 Subject: [PATCH 04/16] remove load_pkl, it is stupid --- .gitignore | 3 ++- bin/load_pkl | 23 ----------------------- 2 files changed, 2 insertions(+), 24 deletions(-) delete mode 100755 bin/load_pkl diff --git a/.gitignore b/.gitignore index 040169b..741428c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.pkl -*~ *.py[co] *.sqlite *.swp +*~ config.yaml +env diff --git a/bin/load_pkl b/bin/load_pkl deleted file mode 100755 index dc8e053..0000000 --- a/bin/load_pkl +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -import optparse -import datetime -import cPickle as pickle - -import kohlrabi.db - -if __name__ == '__main__': - parser = optparse.OptionParser() - parser.add_option('-c', '--class', dest='class_', default=None, help='the class') - parser.add_option('-d', '--date', default=None, help='the date') - opts, args = parser.parse_args() - if len(args) != 1: - parser.error('must specify a .pkl file') - if not opts.class_: - parser.error('must specify the class') - if not opts.date: - opts.date = datetime.date.today() - - kohlrabi.db.bind('sqlite:////tmp/kohlrabi.sqlite', 'kohlrabi.modules.example', True) - cls = getattr(kohlrabi.db, opts.class_) - cls.load_report(pickle.load(open(args[0]))) From 8c03ee6e78b4857734dc99193fa6e5601439adad Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Fri, 26 Apr 2013 12:04:31 -0700 Subject: [PATCH 05/16] upgrade to jquery 2.0.0 --- kohlrabi/handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kohlrabi/handlers.py b/kohlrabi/handlers.py index e1ccfad..0d8ee82 100644 --- a/kohlrabi/handlers.py +++ b/kohlrabi/handlers.py @@ -35,7 +35,7 @@ def initialize(self): self.env = { 'title': '(kohlrabi)', 'uri': self.uri, - 'jquery_url': '//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js' + 'jquery_url': '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js' } def parse_date(self, date_string): From 3c4e335f79feaa21ae3a33aebd8d67be8d24a944 Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Fri, 26 Apr 2013 12:18:38 -0700 Subject: [PATCH 06/16] make large columns collapsed by default --- static/css/report.css | 3 ++- static/js/report.js | 2 ++ templates/report.html | 8 +++----- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/static/css/report.css b/static/css/report.css index ccc8d14..8f2e784 100644 --- a/static/css/report.css +++ b/static/css/report.css @@ -7,11 +7,12 @@ th { padding-right: 1em; font-weight: bold; min-width: 6em; text-align: left; } th:hover { cursor: pointer; } td { padding-right: 1em; text-align: left; max-width: 60em; word-wrap: break-word; } -.hoverable:hover { background: #c6daf4; } +.hoverable:hover { background: #c6daf4; cursor: pointer; } .align-right { text-align: right; } .number { text-align: right; } .clicked { background: #d1c5ff !important; } +.no-overflow { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } td.tiny { font-size: 80%; } label { font-weight: bold; } diff --git a/static/js/report.js b/static/js/report.js index 3490bcc..9837702 100644 --- a/static/js/report.js +++ b/static/js/report.js @@ -114,10 +114,12 @@ $(document).ready(function () { } if (clickedRow) { $(clickedRow).removeClass('clicked'); + $(clickedRow.children[0]).addClass('no-overflow'); } if (clickedRow !== this) { clickedRow = this; $(this).addClass('clicked'); + $(clickedRow.children[0]).removeClass('no-overflow'); } else { clickedRow = null; } diff --git a/templates/report.html b/templates/report.html index c38d300..baff32b 100644 --- a/templates/report.html +++ b/templates/report.html @@ -14,11 +14,9 @@
{{escape(title)}}
-
{% for c in columns %} @@ -27,8 +25,8 @@ {% for datum in data %} - {% for c in columns %} - + {% for i, c in enumerate(columns) %} + {% end %} {% end %} From d5a365f3b6ad6e99058d3c4bb9ca1f713c03c4ec Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Mon, 24 Jun 2013 15:06:23 -0700 Subject: [PATCH 07/16] allow abstract base classes --- kohlrabi/db.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kohlrabi/db.py b/kohlrabi/db.py index cac714d..fb03cf8 100644 --- a/kohlrabi/db.py +++ b/kohlrabi/db.py @@ -28,6 +28,8 @@ def bind(engine_path, import_module, create_tables=False): class _Base(object): + _abstract = True + @classmethod def current_date(cls): row = session.query(cls).order_by(cls.date.desc()).first() @@ -42,7 +44,8 @@ class ReportMeta(DeclarativeMeta): def __init__(cls, name, bases, cls_dict): super(ReportMeta, cls).__init__(name, bases, cls_dict) - report_tables.add(cls) + if not cls_dict.get('_abstract', False): + report_tables.add(cls) def format_float(v): return '%1.2f' % (v or 0) From eca5e29ee4d123735737a40c2e938cd30ded7211 Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Mon, 24 Jun 2013 15:08:38 -0700 Subject: [PATCH 08/16] upgrade to jquery 2.0.2 --- kohlrabi/handlers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kohlrabi/handlers.py b/kohlrabi/handlers.py index 0d8ee82..dac370b 100644 --- a/kohlrabi/handlers.py +++ b/kohlrabi/handlers.py @@ -35,7 +35,7 @@ def initialize(self): self.env = { 'title': '(kohlrabi)', 'uri': self.uri, - 'jquery_url': '//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js' + 'jquery_url': '//ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js' } def parse_date(self, date_string): From 01b1ce137ce451634768357175ffeca3ed683fbd Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Tue, 25 Jun 2013 22:12:43 -0700 Subject: [PATCH 09/16] fix how abstract classes work --- kohlrabi/db.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/kohlrabi/db.py b/kohlrabi/db.py index fb03cf8..fcfa9a5 100644 --- a/kohlrabi/db.py +++ b/kohlrabi/db.py @@ -28,7 +28,7 @@ def bind(engine_path, import_module, create_tables=False): class _Base(object): - _abstract = True + __abstract__ = True @classmethod def current_date(cls): @@ -44,8 +44,10 @@ class ReportMeta(DeclarativeMeta): def __init__(cls, name, bases, cls_dict): super(ReportMeta, cls).__init__(name, bases, cls_dict) - if not cls_dict.get('_abstract', False): - report_tables.add(cls) + if cls_dict.get('__abstract__', False): + return + + report_tables.add(cls) def format_float(v): return '%1.2f' % (v or 0) From aeb323e2fa5059889577f620caa23883a4fd2796 Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Wed, 17 Jul 2013 23:12:12 -0700 Subject: [PATCH 10/16] use NullPool --- kohlrabi/db.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kohlrabi/db.py b/kohlrabi/db.py index fcfa9a5..0078048 100644 --- a/kohlrabi/db.py +++ b/kohlrabi/db.py @@ -1,6 +1,7 @@ import datetime from sqlalchemy import * from sqlalchemy.orm import sessionmaker +from sqlalchemy.pool import NullPool from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta report_tables = set() @@ -10,7 +11,9 @@ def bind(engine_path, import_module, create_tables=False): global session - create_kw = {} + create_kw = { + 'poolclass': NullPool, + } if engine_path.startswith('mysql+mysqldb'): create_kw['pool_recycle'] = 3600 engine = create_engine(engine_path, **create_kw) From 680acf32e1f615eec679c05752479ef3d27386e1 Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Wed, 17 Jul 2013 23:41:41 -0700 Subject: [PATCH 11/16] use scoped sessions --- kohlrabi/db.py | 19 +++++++++---------- kohlrabi/handlers.py | 4 ++++ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/kohlrabi/db.py b/kohlrabi/db.py index 0078048..31e3168 100644 --- a/kohlrabi/db.py +++ b/kohlrabi/db.py @@ -7,18 +7,17 @@ report_tables = set() metadata = MetaData() -session = None +Session = None def bind(engine_path, import_module, create_tables=False): - global session + global Session create_kw = { 'poolclass': NullPool, } if engine_path.startswith('mysql+mysqldb'): create_kw['pool_recycle'] = 3600 engine = create_engine(engine_path, **create_kw) - Session = sessionmaker(bind=engine) - session = Session() + Session = scoped_session(sessionmaker(bind=engine)) if import_module: __import__(import_module) @@ -35,7 +34,7 @@ class _Base(object): @classmethod def current_date(cls): - row = session.query(cls).order_by(cls.date.desc()).first() + row = Session.query(cls).order_by(cls.date.desc()).first() if row: return row.date else: @@ -76,8 +75,8 @@ def format_str(s): def load_report(cls, data, date=None): date = date or datetime.date.today() - for row in session.query(cls).filter(cls.date == date): - session.delete(row) + for row in Session.query(cls).filter(cls.date == date): + Session.delete(row) for datum in data: if hasattr(cls, 'column_map'): datum = dict((cls.column_map[k], v) for k, v in datum.iteritems()) @@ -91,11 +90,11 @@ def load_report(cls, data, date=None): if not hasattr(cls, k): del datum[k] - session.add(cls(**datum)) - session.commit() + Session.add(cls(**datum)) + Session.commit() def dates(cls, limit=None): - ds = session.query(cls.date).group_by(cls.date).order_by(cls.date.desc()) + ds = Session.query(cls.date).group_by(cls.date).order_by(cls.date.desc()) if limit is not None: return (row.date for row in ds[:limit]) else: diff --git a/kohlrabi/handlers.py b/kohlrabi/handlers.py index dac370b..2f36af0 100644 --- a/kohlrabi/handlers.py +++ b/kohlrabi/handlers.py @@ -38,6 +38,10 @@ def initialize(self): 'jquery_url': '//ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js' } + def finish(self, chunk=None): + db.Session.remove() + super(RequestHandler, self).finish(chunk) + def parse_date(self, date_string): if date_string and date_string != 'current': return datetime.datetime.strptime(date_string, '%Y-%m-%d').date() From f307db1281160553b55d487975fb1adda00e8fa1 Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Wed, 17 Jul 2013 23:43:24 -0700 Subject: [PATCH 12/16] actually import this --- kohlrabi/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kohlrabi/db.py b/kohlrabi/db.py index 31e3168..fd146ce 100644 --- a/kohlrabi/db.py +++ b/kohlrabi/db.py @@ -1,6 +1,6 @@ import datetime from sqlalchemy import * -from sqlalchemy.orm import sessionmaker +from sqlalchemy.orm import sessionmaker, scoped_session from sqlalchemy.pool import NullPool from sqlalchemy.ext.declarative import declarative_base, DeclarativeMeta From a150b29e129c98ddd3f6915663776a305ce92d6b Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Wed, 17 Jul 2013 23:45:24 -0700 Subject: [PATCH 13/16] fix this too --- kohlrabi/handlers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/kohlrabi/handlers.py b/kohlrabi/handlers.py index 2f36af0..75f9499 100644 --- a/kohlrabi/handlers.py +++ b/kohlrabi/handlers.py @@ -63,7 +63,6 @@ def render_json(self, obj): self.write(json.dumps(obj)) def get_error_html(self, status_code, **kwargs): - db.session.rollback() self.set_header('Content-Type', 'text/plain') return traceback.format_exc() From bee63c22ed603f4b46340fa92b0a2808e60009e6 Mon Sep 17 00:00:00 2001 From: Evan Klitzke Date: Wed, 31 Jul 2013 23:29:26 -0700 Subject: [PATCH 14/16] add variants --- kohlrabi/db.py | 24 ++++++++++++++++++++++++ kohlrabi/handlers.py | 36 ++++++++++++++++-------------------- static/css/main.css | 2 ++ static/js/home.js | 25 +++++++++++++++++++++++++ templates/home.html | 23 +++++++++++++++++------ 5 files changed, 84 insertions(+), 26 deletions(-) diff --git a/kohlrabi/db.py b/kohlrabi/db.py index fd146ce..b4ee30c 100644 --- a/kohlrabi/db.py +++ b/kohlrabi/db.py @@ -1,4 +1,6 @@ import datetime +import time + from sqlalchemy import * from sqlalchemy.orm import sessionmaker, scoped_session from sqlalchemy.pool import NullPool @@ -32,6 +34,8 @@ class _Base(object): __abstract__ = True + _variant_cache = (0, None) + @classmethod def current_date(cls): row = Session.query(cls).order_by(cls.date.desc()).first() @@ -40,6 +44,26 @@ def current_date(cls): else: return None + @classmethod + def variant_map(cls): + now = time.time() + then, vals = getattr(cls, '_variant_cache', (0, [])) + if now - then <= 60: # cache for one minute + return vals + + variants = [] + for variant in getattr(cls, 'variants', []): + values = [] + for val in Session.query(getattr(cls, variant)).distinct(): + val, = val + if val: + values.append(val) + values.sort() + variants.append((variant, values)) + + cls._variant_cache = (now, variants) + return variants + Base = declarative_base(metadata=metadata, cls=_Base) class ReportMeta(DeclarativeMeta): diff --git a/kohlrabi/handlers.py b/kohlrabi/handlers.py index 75f9499..2c8d4de 100644 --- a/kohlrabi/handlers.py +++ b/kohlrabi/handlers.py @@ -4,6 +4,7 @@ import datetime import sqlalchemy import traceback +import urlparse try: from sqlalchemy.exception import OperationalError except ImportError: @@ -73,39 +74,29 @@ class Home(RequestHandler): DATES_PER_PAGE = 14 def get(self): - # this is pretty inefficient + # we want to know for each date, which tables are available date_map = defaultdict(list) for tbl in db.report_tables: for d in tbl.dates(): date_map[d].append(tbl) + date_map[d] = sorted(date_map[d], key=lambda x: x.display_name) + # these are all the dates available to display reports for dates = sorted(date_map.keys(), reverse=True) self.env['num_pages'] = int(math.ceil(len(dates) / float(self.DATES_PER_PAGE))) # show DATES_PER_PAGE things per page - page = int(self.request.uri.split('/')[-1] or 1) + page = int(self.request.uri.split('/')[-1] or 1) # FIXME self.env['page'] = page dates = dates[self.DATES_PER_PAGE * (page - 1) : self.DATES_PER_PAGE * page] self.env['dates'] = dates - report_names = set() + reports = set() for d in dates: - report_names |= set((r.display_name, r.__name__) for r in date_map[d]) + reports |= set(date_map[d]) - self.env['report_names'] = list(sorted(report_names)) - self.env['reports'] = [] - - # this is *also* really inefficient and ghetto - for d in dates: - links = [] - for report_name, report_table in self.env['report_names']: - for t in date_map[d]: - if t.display_name == report_name: - links.append((report_name, report_table)) - break - else: - links.append(None) - self.env['reports'].append((d.strftime('%Y-%m-%d'), links)) + self.env['report_names'] = sorted(report.display_name for report in reports) + self.env['date_map'] = date_map self.env['title'] = 'kohlrabi: home' self.render('home.html') @@ -128,15 +119,20 @@ class Report(RequestHandler): path = '/report/.*/.*' def get(self): - path = self.request.uri.lstrip('/') + path = self.request.path.lstrip('/') components = path.split('/') tbl, date = components[-2:] if not date or date == 'current': date = getattr(db, tbl).current_date() else: date = self.parse_date(date) + self.env['date'] = date - self.env['data'] = getattr(db, tbl).report_data(date) + table = getattr(db, tbl) + data = table.report_data(date) + for k, v in urlparse.parse_qsl(self.request.query): + data = data.filter(getattr(table, k) == v) + self.env['data'] = data self.env['columns'] = getattr(db, tbl).html_table self.env['first_column'] = getattr(db, tbl).html_table[0].display self.env['title'] = getattr(db, tbl).display_name + ' ' + date.strftime('%Y-%m-%d') diff --git a/static/css/main.css b/static/css/main.css index 1d3a214..91cac50 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -53,3 +53,5 @@ ul.reports > li { border-radius: 5px; padding: 5px; } + +.variant_name { margin-left: 1em; } diff --git a/static/js/home.js b/static/js/home.js index 8f961c8..58b266b 100644 --- a/static/js/home.js +++ b/static/js/home.js @@ -23,4 +23,29 @@ $(document).ready(function () { }); + + $('.report_link').each(function (index, value) { + $(this).click(function(event) { + console.log('got a click durrr'); + var params = {}; + var variants = this.parentNode.getElementsByClassName('variant'); + var hasAny = false; + for (var i = 0; i < variants.length; i++) { + var variant = variants[i]; + var variantName = $(variant.getElementsByClassName('variant_name')).text(); + var variantValue = variant.getElementsByTagName('select')[0].value; + if (variantValue) { + params[variantName] = variantValue; + hasAny = true; + } + } + console.log(params); + console.log(params.length) + event.preventDefault(); + if (hasAny) { + event.preventDefault(); + window.location = this.href + '?' + $.param(params); + } + }); + }); }); diff --git a/templates/home.html b/templates/home.html index df2bc9e..4c2f101 100644 --- a/templates/home.html +++ b/templates/home.html @@ -35,18 +35,29 @@
{{ c.format(getattr(datum, c.name)) }}{{ c.format(getattr(datum, c.name)) }}
{% else %}