Commits

Matt Bone committed 4d47d93

simple refactor. doubling and growing pages are about the same. need to look less lame.

  • Participants
  • Parent commits 72765ea

Comments (0)

Files changed (8)

File growthrates/inputs/urls.py

 
 urlpatterns = patterns('growthrates.inputs.views',
     url(r'^$', 'index', name='index'),
-    url(r'^doubling$', 'doubling', name='doubling'),
-    url(r'^growing$', 'growing', name='growing'),
     url(r'^growing/at/(?P<rate>\d+)/percent/per/(?P<period>(year)|(month))$', 'growing_at', name='growing_at'),
     url(r'^growing/at/(?P<rate>\d+\.\d+)/percent/per/(?P<period>(year)|(month))$', 'growing_at', name='growing_at'),
     url(r'^doubling/every/(?P<rate>\d+)/(?P<period>(years)|(months))$', 'doubling_at', name='doubling_at'),
     url(r'^doubling/every/(?P<rate>\d+\.\d+)/(?P<period>(years)|(months))$', 'doubling_at', name='doubling_at'),
+    url(r'^about$', 'about', name='about'),
 )

File growthrates/inputs/views.py

 import json
 import math
 
-class GrowingForm(forms.Form):
+class RateForm(forms.Form):
     rate = forms.FloatField(required=True)
     period = forms.ChoiceField((('year', 'year'), ('month', 'month')))
+    type = forms.ChoiceField((('growing', 'growing'), ('doubling', 'doubling')))
 
 
-class DoublingForm(forms.Form):
-    rate = forms.FloatField(required=True)
-    period = forms.ChoiceField((('years', 'years'), ('months', 'months')))
+class Rate(object):
+    doubling_rate = None
+    growth_rate = None
+    period = None
 
+    @classmethod
+    def from_doubling_rate(cls, doubling_rate, period):
+        rate_obj = cls()
+        rate_obj.doubling_rate = doubling_rate
+        rate_obj.growth_rate = math.expm1(math.log(2)/doubling_rate)
+        rate_obj.set_period(period)
 
-def index(request):
-    return render_to_response("index.html")
+        return rate_obj
+
+
+    @classmethod
+    def from_growth_rate(cls, growth_rate, period):
+        rate_obj = cls()
+        rate_obj.growth_rate = growth_rate
+        rate_obj.doubling_rate = math.log(2)/math.log1p(growth_rate)
+        rate_obj.period = period
+
+        return rate_obj
+
+
+    def set_period(self, period):
+        if period.lower().startswith("month"):
+            self.period = "Month"
+        elif period.lower().startswith("year"):
+            self.period = "Year"
+
+
+    def get_data_array(self, initial_value=100, num_points=25):
+        data_array = []
+        curr = initial_value
+        data_array.append([0, initial_value])
+        for i in range(1, num_points):
+            curr = curr + self.growth_rate*curr
+            data_array.append([i, curr])
+        return data_array
 
 
 @csrf_exempt
-def growing(request):
+def index(request):
+    growing_error = False
+    doubling_error = False
     if request.method == 'POST':
-        form = GrowingForm(request.POST)
+        form = RateForm(request.POST)
         if form.is_valid():
-            return HttpResponseRedirect(reverse("growing_at",
-                                                args=(floatformat(form.cleaned_data["rate"], "-3"),
-                                                      form.cleaned_data["period"])))
+            if form.cleaned_data["type"] == "growing":
+                return HttpResponseRedirect(reverse("growing_at",
+                                                    args=(floatformat(form.cleaned_data["rate"], "-2"),
+                                                          form.cleaned_data["period"])))
+            elif form.cleaned_data["type"] == "doubling":
+                return HttpResponseRedirect(reverse("doubling_at",
+                                                    args=(floatformat(form.cleaned_data["rate"], "-2"),
+                                                    form.cleaned_data["period"]+"s")))
         else:
             print form.errors
+            type = request.POST.get("type", "growing")
+            if type == "growing":
+                growing_error = True
+            else:
+                doubling_error = True
 
-    return HttpResponseRedirect(reverse("index"))
+    return render_to_response("index.html", {"growing_error": growing_error,
+                                             "doubling_error": doubling_error})
 
 
-@csrf_exempt
-def doubling(request):
-    if request.method == 'POST':
-        form = DoublingForm(request.POST)
-        if form.is_valid():
-            return HttpResponseRedirect(reverse("doubling_at",
-                                                args=(floatformat(form.cleaned_data["rate"], "-3"),
-                                                      form.cleaned_data["period"])))
-        else:
-            print form.errors
-
-    return HttpResponseRedirect(reverse("index"))
+def about(request):
+    return render_to_response("about.html")
 
 
 def growing_at(request, rate, period):
+    rate_obj = Rate.from_growth_rate(float(rate)/100.0, period)
 
-    rate_f = float(rate)/100.0
-    doubling_rate = math.log(2)/math.log1p(rate_f)
-
-    initial_value = 100
-    num_points = 25
-
-    data_array = []
-    curr = initial_value
-    data_array.append([0, initial_value])
-    for i in range(1, num_points):
-        curr = curr + rate_f*curr
-        data_array.append([i, curr])
-
-
-    return render_to_response("growing.html", {'rate':rate,
-                                               'period': period,
-                                               'data_array': data_array,
-                                               'json_data_array': json.dumps(data_array),
-                                               'doubling_rate': floatformat(doubling_rate, "-3")})
+    return render_to_response("growing.html", {'rate': floatformat(rate_obj.growth_rate*100.0, "-2"),
+                                               'period': rate_obj.period,
+                                               'data_array': rate_obj.get_data_array(),
+                                               'json_data_array': json.dumps(rate_obj.get_data_array()),
+                                               'doubling_rate': floatformat(rate_obj.doubling_rate, "-2")})
 
 
 def doubling_at(request, rate, period):
-    return render_to_response("doubling.html", {'rate':rate,
-                                                'period': period})
+    rate_obj = Rate.from_doubling_rate(float(rate), period)
+
+    return render_to_response("doubling.html", {'rate': floatformat(rate_obj.doubling_rate, "-2"),
+                                                'period': rate_obj.period,
+                                                'data_array': rate_obj.get_data_array(),
+                                                'json_data_array': json.dumps(rate_obj.get_data_array()),
+                                                'growth_rate': floatformat(rate_obj.growth_rate*100.0, "-2")})

File growthrates/jinja2_for_django.py

 from django.template import TemplateDoesNotExist
 from django.core import urlresolvers
 from django.conf import settings
+from django.template.defaultfilters import floatformat
 import jinja2
 
 class Template(jinja2.Template):
     # These are available to all templates.
     env.globals['url_for'] = urlresolvers.reverse
     env.globals['MEDIA_URL'] = settings.MEDIA_URL
+    env.globals['floatformat'] = floatformat
     #env.globals['STATIC_URL'] = settings.STATIC_URL
 
     def load_template(self, template_name, template_dirs=None):
-        print "LOADING %s" % template_name
         try:
             template = self.env.get_template(template_name)
         except jinja2.TemplateNotFound:

File growthrates/templates/about.html

+{% extends "base.html" %}
+
+{% block content %}
+    <h1>About</h1>
+
+    <p>Coming soon...</p>
+
+{% endblock %}

File growthrates/templates/base.html

         html, body {
             background-color: #eee;
         }
+
         body {
-            padding-top: 40px; /* 40px to make the container go all the way to the bottom of the topbar */
+            padding-top: 20px;
         }
+
         .container > footer p {
             text-align: center; /* center align it with the container */
         }
+
         .container {
             width: 820px; /* downsize our container to make the content feel a bit tighter and more cohesive. NOTE: this removes two full columns from the grid, meaning you only go to 14 columns and not 16. */
         }
 
-            /* The white background content wrapper */
+        /* The white background content wrapper */
         .container > .content {
             background-color: #fff;
             padding: 20px;
             box-shadow: 0 1px 2px rgba(0,0,0,.15);
         }
 
-            /* Page header tweaks */
-        .page-header {
-            background-color: #f5f5f5;
-            padding: 20px 20px 10px;
-            margin: -20px -20px 20px;
+        .rate-input p {
+            font-size: 16px;
         }
 
-            /* Styles you shouldn't keep as they are for displaying this base example only */
-        .content .span10,
-        .content .span4 {
-            min-height: 500px;
-        }
-            /* Give a quick and non-cross-browser friendly divider */
-        .content .span4 {
-            margin-left: 0;
-            padding-left: 19px;
-            border-left: 1px solid #eee;
+        .rate-input select {
+            vertical-align: middle;
         }
 
-        .topbar .btn {
-            border: 0;
+        .rate-input input {
+            vertical-align: middle;
         }
-
     </style>
-
-
-
-    {#
-    <!-- Le fav and touch icons -->
-    <link rel="shortcut icon" href="images/favicon.ico">
-    <link rel="apple-touch-icon" href="images/apple-touch-icon.png">
-    <link rel="apple-touch-icon" sizes="72x72" href="images/apple-touch-icon-72x72.png">
-
-    <link rel="apple-touch-icon" sizes="114x114" href="images/apple-touch-icon-114x114.png">
-    #}
 </head>
 
 <body>
 
 
     <footer>
-        <p>by <a href="http://thatmattbone.com">Matt Bone</a></p>
+        <p><a href="{{ url_for("about") }}">about</a> | <a href="http://bitbucket.org/thatmattbone/growthrates">get the code</a> | by <a href="http://thatmattbone.com">Matt Bone</a></p>
     </footer>
 
 </div> <!-- /container -->

File growthrates/templates/doubling.html

 {% extends "base.html" %}
 
 {% block content %}
-<h1>Doubling every {{ rate }} {{ period }}</h1>
+    <h1>Doubling Every {{ rate }} {{ period }}s</h1>
 
+    <p>Is the same as <a href="{{ url_for("growing_at", args=(growth_rate, period.lower())) }}">growing at {{ growth_rate }}% per {{ period.lower() }}.</a></p>
+
+    <h2>If you had $100 doubling every {{ rate }} {{ period.lower() }}s</h2>
+
+    <div id="doubling-graph" style="width:600px;height:300px;"></div>
+
+    <h2 style="padding-top: 25px">Or in table form...</h2>
+    <table class="span5 zebra-striped">
+        <thead>
+        <th>{{ period }}</th>
+        <th>Dollars</th>
+        </thead>
+        {% for n, m in data_array %}
+            <tr>
+                <td>{{ n }}</td>
+                <td>${{ floatformat(m, "-2") }}</td>
+            </tr>
+        {% endfor %}
+    </table>
+
+    <script type="text/javascript">
+        $(function () {
+            $.plot($("#doubling-graph"), [{{ json_data_array }}]);
+        });
+    </script>
 {% endblock %}

File growthrates/templates/growing.html

     {% for n, m in data_array %}
     <tr>
         <td>{{ n }}</td>
-        <td>{{ m }}</td>
+        <td>${{ floatformat(m, "-2") }}</td>
     </tr>
     {% endfor %}
     </table>

File growthrates/templates/index.html

 {% block content %}
 <h1>Growth Rates</h1>
 
-    <form action="{{ url_for("growing") }}" method="POST">
+    {% if errors %}
+        {{  errors }}
+    {% endif %}
 
-        <p>Growing at
-            <input type="text" name="rate" class="span1" placeholder="7"/>% per
-            <select name="period" class="span2">
-                <option value="year">year</option>
-                <option value="month">month</option>
-            </select>
-            <input class="btn primary" type="submit" value="Go">
-        </p>
-    </form>
-    <form action="{{ url_for("doubling") }}" method="POST">
-        <p>Doubling every
-            <input type="text" name="rate" class="span1" placeholder="10"/>
-            <select name="period" class="span2">
-                <option value="years">years</option>
-                <option value="months">months</option>
-            </select>
-            <input class="btn primary" type="submit" value="Go">
-        </p>
-    </form>
+    <p>
+    Whether reading an article or listening to a political speech, sometimes it can be difficult to understand what it
+    means for something to be growing at a particular rate.  This site aims help in the visualization and quantification
+    of these types of measurements.
+    </p>
+
+    <div class="rate-input">
+        <form action="" method="POST" id="growing-form">
+
+            <h2>I know something is growing at...</h2>
+            {% if growing_error %}
+                <div class="alert-message block-message error">
+                    <p style="font-size:12px; font-weight: bold">Oops! Please enter a growth rate in the field below.</p>
+                    <p style="font-size:12px;">We've filled in 7% for because it's an interesting growth rate.</p>
+                </div>
+            {% endif %}
+            <p style="padding-top: 5px">
+                <input type="text" name="rate" class="span1" {% if growing_error %}value="7"{% else %}placeholder="7.25"{% endif %}/>% per
+                <select name="period" class="span2">
+                    <option value="year">year</option>
+                    <option value="month">month</option>
+                </select>
+                <input type="hidden" name="type" value="growing"/>
+                <input class="btn primary" type="submit" value="Show Me">
+            </p>
+        </form>
+    </div>
+
+    <div class="rate-input">
+        <form action="" method="POST" id="doubling-form">
+            <h2>or I know something is doubling every...</h2>
+            {% if doubling_error %}
+                <div class="alert-message block-message error">
+                    <p style="font-size:12px; font-weight: bold">Oops! Please enter a doubling rate in the field below.</p>
+                    <p style="font-size:12px;">We've filled in 10 years for you because it's an interesting doubling rate.</p>
+                </div>
+            {% endif %}
+            <p style="padding-top: 5px">
+                <input type="text" name="rate" class="span1" {% if doubling_error %}value="10"{% else %}placeholder="9.75"{% endif %}/>
+                <select name="period" class="span2">
+                    <option value="year">years</option>
+                    <option value="month">months</option>
+                </select>
+                <input type="hidden" name="type" value="doubling"/>
+                <input class="btn primary" type="submit" value="Show Me">
+            </p>
+        </form>
+    </div>
+
 
 {% endblock %}