L
The website of a web programmer, footie fan, rubbish photographer, and jack of literally some trades.

t Half-Life 2 is older now than Half-Life was when Half-Life 2 was released. (The same is true for HL2 Episode 2 and HL2).

b commit #190 to johnny-cache

Jan. 20, 2012, 4:10 a.m.

Added a get_generation utility function.

johnny/cache.py

g commit 7ca03c45 to humanize

Jan. 19, 2012, 2:47 a.m.

100% test coverage, fix a few bugs with humanize.time.naturaltime, added a humanize.time.naturaldelta which returns strings like "a day" or "1 year, 3 months" without any tense (ago/from now), version bump

  • @@ -6,16 +6,15 @@ humanize
     -----------
     .. automodule:: humanize
    -
     .. highlight:: sh
     You can install humanize with pip::
         pip install humanize
    -You can fork humanize `from its hg repository
    -<http://bitbucket.org/jmoiron/humanize/>`_::
    +You can fork humanize `from its git repository
    +<http://github.com/jmoiron/humanize/>`_::
    -    hg clone http://bitbucket.org/jmoiron/humanize/
    +    git clone https://github.com/jmoiron/humanize.git
     .. highlight:: python
    
  • @@ -1,9 +1,9 @@
    -VERSION = (0,1)
    +VERSION = (0,2)
     from humanize.time import *
     from humanize.number import *
     from humanize.filesize import *
     __all__ = ['VERSION', 'naturalday', 'naturaltime', 'ordinal', 'intword',
    -    'intcomma', 'apnumber', 'naturalsize']
    +    'naturaldelta', 'intcomma', 'apnumber', 'naturalsize']
    
  • @@ -39,58 +39,90 @@ def date_and_delta(value):
                 return (None, value)
         return date, abs_timedelta(delta)
    -def naturaltime(value, date_fallback=30, format='%b %d'):
    +def naturaldelta(value, months=True):
    +    """Given a timedelta or a number of seconds, return a natural
    +    representation of the amount of time elapsed.  This is similar to
    +    ``naturaltime``, but does not add tense to the result.  If ``months``
    +    is True, then a number of months (based on 30.5 days) will be used
    +    for fuzziness between years."""
    +    now = _now()
    +    date, delta = date_and_delta(value)
    +    if date is None:
    +        return value
    +
    +    use_months = months
    +
    +    seconds = abs(delta.seconds)
    +    days = abs(delta.days)
    +    years = days // 365
    +    days = days % 365
    +    months = int(days // 30.5)
    +
    +    if not years and days < 1:
    +        if seconds == 0:
    +            return "a moment"
    +        elif seconds == 1:
    +            return "a second"
    +        elif seconds < 60:
    +            return "%d seconds" % (seconds)
    +        elif 60 <= seconds < 120:
    +            return "a minute"
    +        elif 120 <= seconds < 3600:
    +            return "%d minutes" % (seconds // 60)
    +        elif 3600 <= seconds < 3600*2:
    +            return "an hour"
    +        elif 3600 < seconds:
    +            return "%d hours" % (seconds // 3600)
    +    elif years == 0:
    +        if days == 1:
    +            return "a day"
    +        if not use_months:
    +            return "%d days" % days
    +        else:
    +            if not months:
    +                return "%d days" % days
    +            elif months == 1:
    +                return "a month"
    +            else:
    +                return "%d months" % months
    +    elif years == 1:
    +        if not months and not days:
    +            return "a year"
    +        elif not months:
    +            return "1 year, %d days" % days
    +        elif use_months:
    +            if months == 1:
    +                return "1 year, 1 month"
    +            else:
    +                return "1 year, %d months" % months
    +        else:
    +            return "1 year, %d days" % days
    +    else:
    +        return "%d years" % years
    +
    +
    +def naturaltime(value, future=False, months=True):
         """Given a datetime or a number of seconds, return a natural representation
         of that time in a resolution that makes sense.  This is more or less
    -    compatible with Django's ``naturaltime`` filter."""
    +    compatible with Django's ``naturaltime`` filter.  ``future`` is ignored for
    +    datetimes, where the tense is always figured out based on the current time.
    +    If an integer is passed, the return value will be past tense by default,
    +    unless ``future`` is set to True."""
         now = _now()
         date, delta = date_and_delta(value)
         if date is None:
             return value
    -
    -    future = date > now
    +    # determine tense by value only if datetime/timedelta were passed
    +    if isinstance(value, (datetime, timedelta)):
    +        future = date > now
         ago = 'from now' if future else 'ago'
    -    if delta.days == 0 and delta.seconds == 0:
    +    delta = naturaldelta(delta)
    +
    +    if delta == "a moment":
             return "now"
    -    elif abs(delta.days) == 1:
    -        return 'a day %s' % ago
    -    elif abs(delta.days) == 365:
    -        return 'a year %s' % ago
    -    elif abs(delta.days) > 365:
    -        years = abs(delta.days) // 365
    -        days = abs(delta.days) % 365
    -        months = days // 31
    -        if months == 1 and years == 1:
    -            return '1 year, 1 month %s' % (month, ago)
    -        elif months > 1 and years == 1:
    -            return '1 year, %d months %s' % (months, ago)
    -        elif years == 1:
    -            return 'a year %s' % ago
    -        elif years > 1 and months == 1:
    -            return '%d years, 1 month %s' % (years, ago)
    -        elif years > 1 and months > 1:
    -            return '%d years, %d months %d' % (years, months, ago)
    -        else:
    -            return '%d years %d' % (ago)
    -    elif 365 < abs(delta.days) < 365*2:
    -        return '1 year, %d days %s' % (abs(delta.days) % 365, ago)
    -    elif abs(delta.days) > 365*2:
    -        return '%d years %s' % (abs(delta.days) // 365, ago)
    -    elif 1 < abs(delta.days) < 365:
    -        return '%d days %s' % ago
    -    elif abs(delta.seconds) == 1:
    -        return 'a second %s' % ago
    -    elif abs(delta.seconds) < 60:
    -        return '%d seconds %s' % (delta.seconds, ago)
    -    elif 60 <= abs(delta.seconds) < 120:
    -        return 'a minute %s' % ago
    -    elif 120 <= abs(delta.seconds) < 3600:
    -        return '%d minutes %s' % (delta.seconds // 60, ago)
    -    elif 3600 <= abs(delta.seconds) < 3600*2:
    -        return 'an hour %s' % ago
    -    elif 3600 < abs(delta.seconds):
    -        return '%d hours %s' % (delta.seconds // 3600, ago)
    +
    +    return "%s %s" % (delta, ago)
     def naturalday(value, format='%b %d'):
         """For date values that are tomorrow, today or yesterday compared to
    @@ -101,11 +133,9 @@ def naturalday(value, format='%b %d'):
         except AttributeError:
             # Passed value wasn't date-ish
             return value
    -    except ValueError:
    +    except (OverflowError, ValueError):
             # Date arguments out of range
             return value
    -    if not isinstance(value, (date, datetime)):
    -        return value
         delta = value - date.today()
         if delta.days == 0:
             return 'today'
    
  • @@ -1,2 +1,3 @@
     [upload_docs]
    +upload-dir = docs/_build/html
    
  • @@ -6,7 +6,7 @@
     from setuptools import setup, find_packages
     import sys, os
    -version = '0.1'
    +version = '0.2'
     # some trove classifiers:
    
  • @@ -13,8 +13,10 @@ class FilesizeTestCase(HumanizeTestCase):
         def test_naturalsize(self):
             tests = (300, 3000, 3000000, 3000000000, 3000000000000, (300, True),
                 (3000, True), (3000000, True), (300, False, True), (3000, False, True),
    -            (3000000, False, True))
    +            (3000000, False, True), (1024, False, True), (10**26 * 30, False, True),
    +            (10**26 * 30, True), 10**26 * 30)
             results = ('300 Bytes', '3.0 kB', '3.0 MB', '3.0 GB', '3.0 TB',
    -            '300 Bytes', '2.9 KiB', '2.9 MiB', '300B', '2.9K', '2.9M')
    +            '300 Bytes', '2.9 KiB', '2.9 MiB', '300B', '2.9K', '2.9M', '1.0K', '2481.5Y',
    +            '2481.5 YiB', '3000.0 YB')
             self.assertManyResults(filesize.naturalsize, tests, results)
    
  • @@ -30,10 +30,11 @@ def test_intword(self):
             test_list = ('100', '1000000', '1200000', '1290000', '1000000000',
                 '2000000000', '6000000000000', '1300000000000000',
                 '3500000000000000000000', '8100000000000000000000000000000000',
    -            None, ('1230000', '%0.2f'))
    +            None, ('1230000', '%0.2f'), 10**101)
             result_list = ('100', '1.0 million', '1.2 million', '1.3 million',
                '1.0 billion', '2.0 billion', '6.0 trillion', '1.3 quadrillion',
    -           '3.5 sextillion', '8.1 decillion', None, '1.23 million')
    +           '3.5 sextillion', '8.1 decillion', None, '1.23 million',
    +           '1'+'0'*101)
             self.assertManyResults(number.intword, test_list, result_list)
         def test_apnumber(self):
    
  • @@ -27,10 +27,99 @@ def test_date_and_delta(self):
                     dt, d = time.date_and_delta(arg)
                     self.assertEqualDatetime(dt, result[0])
                     self.assertEqualTimedelta(d, result[1])
    +        self.assertEqual(time.date_and_delta("NaN"), (None, "NaN"))
     class TimeTestCase(HumanizeTestCase):
         """Tests for the public interface of humanize.time"""
    +    def test_naturaldelta_nomonths(self):
    +        now = datetime.now()
    +        test_list = [
    +            timedelta(days=7),
    +            timedelta(days=31),
    +            timedelta(days=230),
    +            timedelta(days=400),
    +        ]
    +        result_list = [
    +            '7 days',
    +            '31 days',
    +            '230 days',
    +            '1 year, 35 days',
    +        ]
    +        with patch('humanize.time._now') as mocked:
    +            mocked.return_value = now
    +            nd_nomonths = lambda d: time.naturaldelta(d, months=False)
    +            self.assertManyResults(nd_nomonths, test_list, result_list)
    +
    +    def test_naturaldelta(self):
    +        now = datetime.now()
    +        test_list = [
    +            0,
    +            1,
    +            30,
    +            timedelta(minutes=1, seconds=30),
    +            timedelta(minutes=2),
    +            timedelta(hours=1, minutes=30, seconds=30),
    +            timedelta(hours=23, minutes=50, seconds=50),
    +            timedelta(days=1),
    +            timedelta(days=500),
    +            timedelta(days=365*2 + 35),
    +            timedelta(seconds=1),
    +            timedelta(seconds=30),
    +            timedelta(minutes=1, seconds=30),
    +            timedelta(minutes=2),
    +            timedelta(hours=1, minutes=30, seconds=30),
    +            timedelta(hours=23, minutes=50, seconds=50),
    +            timedelta(days=1),
    +            timedelta(days=500),
    +            timedelta(days=365*2 + 35),
    +            # regression tests for bugs in post-release humanize
    +            timedelta(days=10000),
    +            timedelta(days=365+35),
    +            30,
    +            timedelta(days=365*2 + 65),
    +            timedelta(days=365 + 4),
    +            timedelta(days=35),
    +            timedelta(days=65),
    +            timedelta(days=9),
    +            timedelta(days=365),
    +            "NaN",
    +        ]
    +        result_list = [
    +            'a moment',
    +            'a second',
    +            '30 seconds',
    +            'a minute',
    +            '2 minutes',
    +            'an hour',
    +            '23 hours',
    +            'a day',
    +            '1 year, 4 months',
    +            '2 years',
    +            'a second',
    +            '30 seconds',
    +            'a minute',
    +            '2 minutes',
    +            'an hour',
    +            '23 hours',
    +            'a day',
    +            '1 year, 4 months',
    +            '2 years',
    +            '27 years',
    +            '1 year, 1 month',
    +            '30 seconds',
    +            '2 years',
    +            '1 year, 4 days',
    +            'a month',
    +            '2 months',
    +            '9 days',
    +            'a year',
    +            "NaN",
    +        ]
    +        with patch('humanize.time._now') as mocked:
    +            mocked.return_value = now
    +            self.assertManyResults(time.naturaldelta, test_list, result_list)
    +
         def test_naturaltime(self):
             now = datetime.now()
             test_list = [
    @@ -53,6 +142,13 @@ def test_naturaltime(self):
                 now + timedelta(days=1),
                 now + timedelta(days=500),
                 now + timedelta(days=365*2 + 35),
    +            # regression tests for bugs in post-release humanize
    +            now + timedelta(days=10000),
    +            now - timedelta(days=365+35),
    +            30,
    +            now - timedelta(days=365*2 + 65),
    +            now - timedelta(days=365 + 4),
    +            "NaN",
             ]
             result_list = [
                 'now',
    @@ -64,7 +160,7 @@ def test_naturaltime(self):
                 '23 hours ago',
                 'a day ago',
                 '1 year, 4 months ago',
    -            '2 years, 1 month ago',
    +            '2 years ago',
                 'a second from now',
                 '30 seconds from now',
                 'a minute from now',
    @@ -73,19 +169,39 @@ def test_naturaltime(self):
                 '23 hours from now',
                 'a day from now',
                 '1 year, 4 months from now',
    -            '2 years, 1 month from now',
    +            '2 years from now',
    +            '27 years from now',
    +            '1 year, 1 month ago',
    +            '30 seconds ago',
    +            '2 years ago',
    +            '1 year, 4 days ago',
    +            "NaN",
             ]
             with patch('humanize.time._now') as mocked:
                 mocked.return_value = now
                 self.assertManyResults(time.naturaltime, test_list, result_list)
         def test_naturalday(self):
    +        class fakedate(object):
    +            def __init__(self, year, month, day):
    +                self.year, self.month, self.day = year, month, day
             tomorrow = today + one_day
             yesterday = today - one_day
    -        someday = today - timedelta(10)
    +        if today.month != 3:
    +            someday = date(today.year, 3, 5)
    +            someday_result = 'Mar 05'
    +        else:
    +            someday = date(today.year, 9, 5)
    +            someday_result = 'Sep 09'
    +        valerrtest = fakedate(290149024, 2, 2)
    +        overflowtest = fakedate(120390192341, 2, 2)
             test_list = (today, tomorrow, yesterday, someday, '02/26/1984',
    -            (date(1982, 6, 27), '%Y.%M.%D'), None, "Not a date at all.")
    -        result_list = ('today', 'tomorrow', 'yesterday', 'Sep 29', '02/26/1984',
    -            date(1982, 6, 27).strftime('%Y.%M.%D'), None, "Not a date at all.")
    +            (date(1982, 6, 27), '%Y.%M.%D'), None, "Not a date at all.",
    +            valerrtest, overflowtest
    +        )
    +        result_list = ('today', 'tomorrow', 'yesterday', someday_result, '02/26/1984',
    +            date(1982, 6, 27).strftime('%Y.%M.%D'), None, "Not a date at all.",
    +            valerrtest, overflowtest
    +        )
             self.assertManyResults(time.naturalday, test_list, result_list)
    

b commit #65 to dotfiles

Jan. 17, 2012, 3:38 p.m.

g commit 0d7dd139 to micromongo

Jan. 15, 2012, 4:46 p.m.

fix the setup.py file so it'l work when pymongo is not installed, version bump

  • @@ -6,7 +6,7 @@
     from models import *
     from spec import Field
    -VERSION = (0, 1, 3)
    +VERSION = (0, 1, 4)
     __all__ = ['connect', 'clean_connection', 'Model', 'Field', 'VERSION']
    
  • @@ -5,8 +5,11 @@
     from setuptools import setup, find_packages
    -from micromongo import VERSION
    -version = '.'.join(map(str, VERSION))
    +try:
    +    from micromongo import VERSION
    +    version = '.'.join(map(str, VERSION))
    +except ImportError:
    +    version = '0.1.4'
     # some trove classifiers:
    

t @simonw in linux, you do it with #python! https://t.co/yUZr3A0T

b commit #189 to johnny-cache

Jan. 13, 2012, 4:32 a.m.

fix tests for Django 1.4a where settings will no longer report a DATABASE_ENGINE setting; if it's there, still check against it to prevent invalid test errors, but otherwise check all databases and fail if any don't support multithreading

johnny/tests/cache.py

b commit #188 to johnny-cache

Jan. 13, 2012, 4:17 a.m.

Merged in tobias.mcnulty/johnny-cache (pull request #7)

docs/queryset_cache.rst
johnny/cache.py
johnny/settings.py
johnny/tests/cache.py

b commit #187 to johnny-cache

Jan. 12, 2012, 4:45 p.m.

add more docs on multi-db setup

docs/queryset_cache.rst

b commit #186 to johnny-cache

Jan. 12, 2012, 4:37 p.m.

use "default" as default cache key rather than "ALL"

docs/queryset_cache.rst
johnny/tests/cache.py

b commit #181 to johnny-cache

Jan. 12, 2012, 3:47 p.m.

disable multi-db savepoint tests for 1.1.3 (which does not have multi-db support) instead of confusingly failing

johnny/tests/cache.py

bpullrequest_comment_created event

Jan. 12, 2012, 3:33 p.m.
1854

b commit #180 to johnny-cache

Jan. 12, 2012, 3:20 p.m.

alter pull req #8 a bit to fit in with the enable/disable convenience functions

docs/index.rst
docs/queryset_cache.rst

bpullrequest_fulfilled event

Jan. 12, 2012, 2:59 p.m.
None

b commit #179 to johnny-cache

Jan. 12, 2012, 2:59 p.m.

Merged in akaihola/johnny-cache (pull request #8)

docs/index.rst
docs/queryset_cache.rst

b commit #177 to johnny-cache

Jan. 12, 2012, 2:58 p.m.

add an `enable` and `disable` function to make doing this from scripts, et al easier; related to pull req #8

johnny/cache.py

b commit #178 to johnny-cache

Jan. 12, 2012, 9:49 a.m.

Documentation: Added a warning and instructions about Johnny outside Django's request-response loop A common gotcha only mentioned in some closed Bitbucket issues is that Johnny is not enabled in e.g. scripts, management commands, asynchronous workers and the shell. I gathered and added to the documentation some information about this from the following issues in Bitbucket: https://bitbucket.org/jmoiron/johnny-cache/issue/39 https://bitbucket.org/jmoiron/johnny-cache/issue/49 https://bitbucket.org/jmoiron/johnny-cache/issue/50

docs/index.rst
docs/queryset_cache.rst

b commit #185 to johnny-cache

Jan. 10, 2012, 5:55 p.m.

b commit #184 to johnny-cache

Jan. 10, 2012, 5:52 p.m.

add unit test

johnny/tests/cache.py

b commit #183 to johnny-cache

Jan. 10, 2012, 5:52 p.m.

use the right db cache key for query keys

johnny/cache.py

b commit #182 to johnny-cache

Jan. 10, 2012, 4:58 p.m.

add ability to override cache keys used for databases

docs/queryset_cache.rst
johnny/cache.py
johnny/settings.py

t @jorgebastida nice project, wrote one similar a while back, maybe you can get some ideas from it: https://t.co/YeGZwt02

g commit 38c8f2ee to jmoiron.net

Jan. 9, 2012, 2:22 p.m.

blog-edit form sorta working though integration of comment summary is copy pasta and shitty (i'll have to rethink how I build these things as the actions do involve some admin scaffold junk)

  • @@ -5,6 +5,12 @@ body {
       padding-bottom: 1em;
     }
    +.input-boolean {
    +  .input-append;
    +  .add-on { .border-radius(3px); }
    +  .active { background: lighten(@green, 30); border-color: @green; }
    +}
    +
     .container-fluid {
       min-width: @siteWidth;
     }
    @@ -21,6 +27,11 @@ input, textarea, select {
       h2 { margin-bottom: 0.25em; }
     }
    +label.required {
    +  color: #222;
    +  font-weight: bold;
    +}
    +
     #body { margin-top: 50px; }
     #header { }
     #sidebar { border-right: 1px solid #eee; }
    @@ -33,3 +44,24 @@ input, textarea, select {
     .content .pagination {
       margin-top: 0;
     }
    +
    +textarea {
    +  &.maximum {
    +    width: 100%;
    +    height: 300px;
    +  }
    +  &.medium {
    +    width: 70%;
    +    height: 100px;
    +  }
    +}
    +
    +.label {
    +  &.notice { text-shadow: @blue 1px 1px 1px; }
    +  &.important { text-shadow: @red 1px 1px 1px; }
    +  &.new { text-shadow: @green 1px 1px 1px; }
    +  &.big {
    +    font-size: 12px;
    +    padding: 3px 5px;
    +  }
    +}
    
  • @@ -7,10 +7,7 @@
     #}
     {%- macro label(field) -%}
    -  <label for="{{field.id}}">{{ field.label.text }}
    -  {% if field.flags.required %}
    -    <abbr title="This field is required">*</abbr>
    -  {% endif %}</label>
    +  <label for="{{field.id}}" {% if field.flags.required %}class="required"{% endif %}>{{ field.label.text }}</label>
     {%- endmacro -%}
     {%- macro description(field) -%}
    @@ -29,7 +26,7 @@
       {% endif %}
     {%- endmacro -%}
    -{%- macro boolean_field(field) -%}
    +{%- macro inline_boolean_field(field) -%}
       <div class="input">
         <label>{{ field() }}
           <span>{{ field.label.text }}</span>
    @@ -39,12 +36,21 @@
       </div>
     {%- endmacro -%}
    -{%- macro form_field(field) -%}
    +{%- macro boolean_field(field) -%}
    +  {{ label(field) }}
    +  <div class="input input-boolean" id="{{ field.id }}-div">
    +    <label class="add-on active">{{ field() }}</label>
    +  </div>
    +{%- endmacro -%}
    +
    +{%- macro form_field(field, class=none, inline_booleans=false) -%}
       <div class="clearfix">
       {% if field.type == "HiddenField" %}
         {{ field() }}
       {% else %}
    -    {% if field.type == "BooleanField" %}
    +    {% if field.type == "BooleanField" and inline_booleans %}
    +      {{ inline_boolean_field(field) }}
    +    {% elif field.type == "BooleanField" %}
           {{ boolean_field(field) }}
         {% else %}
           {{ label(field) }}
    @@ -52,7 +58,7 @@
             {% if field.type == "RadioField" %}
               {{ field(class="radio-group") }}
             {% else %}
    -          {{ field() }}
    +          {{ field(class=class) }}
             {% endif %}
             {{ description(field) }}
             {{ errors(field) }}
    
  • @@ -42,12 +42,15 @@ def post_list(count=20):
     @post.register("edit")
     def post_edit(id):
    -    post = Post.find_one({"id": int(id)})
    +    module = post
    +    post_obj = Post.find_one({"id": int(id)})
    +    post_obj.load_comments()
         if not post:
             abort(404)
    -    import ipdb; ipdb.set_trace();
    -    form = PostForm(MultiDict(post.items()))
    -    return render_template("blog/admin/post_edit.html", **locals())
    +    form = PostForm(MultiDict(post_obj.items()))
    +    ctx = locals()
    +    ctx['post'] = post_obj
    +    return render_template("blog/admin/post_edit.html", **ctx)
     admin.add_module(post)
    
  • @@ -7,10 +7,13 @@
     class PostForm(Form):
         title = TextField('Title', [validators.required])
    +    slug = TextField('Slug', [validators.required])
         is_published = BooleanField('Publish?')
    +    enable_comments = BooleanField('Enable Comments?')
         timestamp = TextField('Timestamp')
    -    body = TextAreaField('Body', [validators.required])
    +    body = TextAreaField('Body', [validators.required], id="body-field")
         #tags = SelectMultipleField('Tags')
         summary = TextAreaField('Summary')
    +    # TODO: date, tags, comments
    
  • @@ -27,6 +27,7 @@ class Post(Model):
             "body": Field(required=True, default=""),
             "tags": Field(required=True, default=[]),
             "comments": Field(required=True, default=[]),
    +        "enable_comments": Field(default=True, type=bool),
             "is_published": Field(required=True, default=False, type=bool),
             "summary": Field(),
             "id": Field(required=True, type=int),
    
  • @@ -1,7 +1,71 @@
    +{% import "admin/forms.html" as forms without context %}
    +{% set manager = module.manager %}
    +{% macro post_link(post) -%}
    +  <a href="{{ url_for("blog.detail", slug=post.slug) }}">{{ post.title }}</a>
    +{%- endmacro %}
    -{% import "admin/forms.html" as forms without context %}
    +<div class="page-header">
    +  <h2>Editing {{ post_link(post) }} <small>id {{ post.id }}</small></h2>
    +</div>
    +<form id="post-add" method="POST" action="{{ url_for("admin.add", manager=manager.name, module=module.name) }}">
    +  <fieldset>
    +    <legend>Title</legend>
    +    {{ forms.form_field(form.title, class="xlarge") }}
    +    {{ forms.form_field(form.slug, class="xlarge") }}
    +  <fieldset>
    +    <legend>Content</legend>
    +    {{ forms.form_field(form.body, class="maximum") }}
    +    {{ forms.form_field(form.summary, class="medium") }}
    +  </fieldset>
    +  <fieldset>
    +    <legend>Tags</legend>
    +    <div class="input">
    +    {% for tag in post.tags %}
    +      <span class="label notice big">{{ tag }}</span>
    +    {% endfor %}
    +    </div>
    +  </fieldset>
    +  </fieldset>
    +  <fieldset>
    +    <legend>Options</legend>
    +    {{ forms.form_field(form.is_published) }}
    +    {{ forms.form_field(form.enable_comments) }}
    +  <fieldset>
    +</form>
    -<form>
    -{{ forms.form_fields(form._fields.values()) }}
    +<div class="page-header">
    +  <h3>{{ post.comments|length }} Comments on {{ post_link(post) }}</h3>
    +</div>
    +<form method="POST" action="{{ url_for("admin.delete", manager="comments", module="comment") }}">
    +<fieldset>
    +<table class="zebra-striped bordered-table condensed-table">
    +  <thead>
    +    <th><span class="label important">x</span></th>
    +    <th>name</th>
    +    <th>url</th>
    +    <th>ip</th>
    +    <th>date</th>
    +    <th>spam</th>
    +  </thead>
    +  <tbody>
    +    {% for comment in post.comments %}
    +    <tr>
    +      <td><input type="checkbox" name="{{ comment.id }}"></td>
    +      <td><a href="mailto:{{comment.email}}">{{ comment.name }}</a></td>
    +      <td><a href="{{ comment.url }}">{{ comment.url|truncate(25) }}</a></td>
    +      <td>{{ comment.ip_address }}</td>
    +      <td>{{ comment.timestamp|pdt }}</td>
    +      <td>
    +        {% if comment.needs_moderation %}<span class="label important">x</span>
    +        {% else %}<span class="label success">&#x2713;</span>{% endif %}
    +      </td>
    +    </tr>
    +    {% endfor %}
    +  </tbody>
    +</table>
    +<div class="pull-right">
    +  <button type="submit" class="btn danger">- Delete</button>
    +</div>
    +</fieldset>
     </form>
    
  • @@ -4,16 +4,16 @@
     <h2>Latest comments <small>most recent first</small></h2>
     {% endblock %}
    -{% block table_header %}
    +{%- macro table_header() -%}
       <th><span class="label important">x</span></th>
       <th>name</th>
       <th>url</th>
       <th>ip</th>
       <th>date</th>
       <th>spam</th>
    -{% endblock %}
    +{%- endmacro -%}
    -{% block table_body %}
    +{%- macro table_body(comments) -%}
       {% for comment in comments %}
         <tr>
           <td><input type="checkbox" name="{{ comment.id }}"></td>
    @@ -27,5 +27,13 @@
           </td>
         </tr>
       {% endfor %}
    +{%- endmacro -%}
    +
    +{% block table_header %}
    +  {{ table_header() }}
    +{% endblock %}
    +
    +{% block table_body %}
    +  {{ table_body(comments) }}
     {% endblock %}
    

l Linux Mint 12: Oneiric Revisited

Jan. 8, 2012, 9:30 p.m.

After my disastrous experience with Oneiric left me with an ugly and half-working xfce/compiz amalgam, I promised myself that when the next version of Linux Mint shipped with the erstwhile Gnome2 fork "Mate", I'd give it a go.

g commit e9c98c6d to jmoiron.net

Jan. 8, 2012, 11:35 p.m.

start edit/add forms, fleshing out more forms library in the admin, automatically redirect the manager index to the module index (list) if the manager has only one module

  • @@ -12,6 +12,10 @@ body {
       clear: both;
     }
    +input, textarea, select {
    +  color: #404040;
    +}
    +
     .admin-list, .admin-summary {
       margin-top: 1em;
       h2 { margin-bottom: 0.25em; }
    
  • @@ -0,0 +1 @@
    +
    
  • @@ -75,3 +75,9 @@
       </fieldset>
     {%- endmacro -%}
    +{# delete  |  save #}
    +{%- macro edit_action_buttons() -%}
    +
    +{%- endmacro -%}
    +
    +{# save and add another, save and edit, save #}
    
  • @@ -1,6 +1,6 @@
     {% extends "admin/base.html" %}
     {% block content %}
    -  {{ module.list()|safe }}
    +  {{ content|safe }}
       <div class="clearfix"></div>
     {% endblock %}
    
  • @@ -26,7 +26,13 @@ def manager(manager):
             manager_obj = admin_manager.blueprint_map[manager].admin_manager
         except (KeyError, AttributeError):
             abort(404)
    -    return render_template("admin/manager_index.html",
    +
    +    if not len(manager_obj.modules):
    +        abort(404)
    +    if len(manager_obj.modules) == 1:
    +        return redirect(url_for("admin.list", manager=manager, module=manager_obj.modules[0].name))
    +
    +    return render_template("admin/manager/index.html",
             admin_manager=admin_manager,
             manager=manager_obj)
    @@ -38,9 +44,13 @@ def list(manager, module):
             module_obj = manager_obj.modmap[module]
         except (KeyError, AttributeError):
             abort(404)
    -    return render_template("admin/module_index.html",
    +
    +    content = module_obj.list()
    +    return render_template("admin/module/index.html",
             admin_manager=admin_manager,
    -        manager=manager_obj, module=module_obj)
    +        manager=manager_obj,
    +        module=module_obj,
    +        content=content)
     @admin.route("/<manager>/<module>/add/", methods=("GET", "POST"))
     @login_required
    @@ -57,7 +67,7 @@ def edit(manager, module, id):
             abort(404)
         content = module_obj.edit(id)
    -    return render_template("admin/module_edit.html",
    +    return render_template("admin/module/edit.html",
             admin_manager=admin_manager,
             manager=manager_obj,
             module=module_obj,
    
  • @@ -5,10 +5,13 @@
     from flask import *
     from flaskext.wtf import *
    +from werkzeug import MultiDict
    +
     from jmoiron.utils import Page
     from jmoiron.admin.models import Manager, Module
     from models import *
    +from forms import *
     admin = Manager(blueprint)
    @@ -42,6 +45,8 @@ def post_edit(id):
         post = Post.find_one({"id": int(id)})
         if not post:
             abort(404)
    +    import ipdb; ipdb.set_trace();
    +    form = PostForm(MultiDict(post.items()))
         return render_template("blog/admin/post_edit.html", **locals())
     admin.add_module(post)
    
  • @@ -10,7 +10,7 @@ class PostForm(Form):
         is_published = BooleanField('Publish?')
         timestamp = TextField('Timestamp')
         body = TextAreaField('Body', [validators.required])
    -    tags = SelectMultipleField('Tags')
    +    #tags = SelectMultipleField('Tags')
         summary = TextAreaField('Summary')
    
  • @@ -1,7 +1,7 @@
    -{% extends "admin/module_edit.html" %}
    -{% block content %}
    -  {{ content|safe }}
    -{% endblock %}
    +{% import "admin/forms.html" as forms without context %}
    +<form>
    +{{ forms.form_fields(form._fields.values()) }}
    +</form>