Back to documentation

Stack based calculator

This page is written with "normal" django templates, not the pure python hypergen templates.

Add number to stack

Perform operation

Stack


Show sourcestemplates/djangotemplates/base.html
{% load hypergen %}{% load static %}<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    {# Due to technical reasons both header and footer media are needed #}
    {% hypergen_media_header %}
    <link rel="stylesheet" type="text/css" href="https://unpkg.com/simpledotcss/simple.min.css"></link>
    <link rel="stylesheet" type="text/css" href="{% static 'website/website.css' %}"></link>
  </head>
<body>
  <p><a href="{% url 'website:documentation' %}">Back to documentation</a></p>
  
  <h1>Stack based calculator</h1>
  <p>This page is written with "normal" django templates, not the pure python hypergen templates.</p>

  {# This matches the target_id keyword argument to the @actions #}
  <div id="content">
    {% block content %}
    {% endblock %}
  </div>
  
  <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/default.min.css"></link>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
  <script src="{% static 'website/website.js' %}" defer></script>

  {{sources|safe}}
  
  {# Due to technical reasons both header and footer media are needed #}
  {% hypergen_media_footer %}
</body>
</html>
templates/djangotemplates/content.html
{% extends "djangotemplates/base.html"%}
{% load hypergen %}{% csrf_token %}

{% block content %}
  <h2>Add number to stack</h2>
  <input id="number" type="number" autofocus>
  {# The {% callback %} template tag works exactly like it's python cousin, except it requires an id and event. #}
  {# Also notice that you can reference other elements with "#id[.type]" magic strings. #}
  <button {% callback "djangotemplates:push" "#number.float" id="push" event="onclick" %}>Push</button>

  <h2>Perform operation</h2>
  <button {% callback "djangotemplates:add" id="add" event="onclick" %}>+</button>
  <button {% callback "djangotemplates:subtract" id="subtract" event="onclick" %}>-</button>
  <button {% callback "djangotemplates:multiply" id="multiply" event="onclick" %}>*</button>
  <button {% callback "djangotemplates:divide" id="divide" event="onclick" %}>/</button>
  <button {% callback "djangotemplates:reset" id="reset" event="onclick" %}>C</button>

  <h2>Stack</h2>
  <ol>
  {% for item in stack reversed %}
    <li>{{item}}</li>
  {% endfor %}
  </ol>
{% endblock %}
urls.py
from djangotemplates import views
from hypergen.hypergen import autourls
try:
    from django.conf.urls import url
except ImportError:
    from django.urls import re_path as url

app_name = 'djangotemplates'

urlpatterns = [
    url('^$', views.djangotemplates, name="djangotemplates"),] + autourls(views, app_name)
views.py
import operator

from hypergen.imports import *
from hypergen.context import context as c
from hypergen.templatetags.hypergen import render_to_hypergen

from django.shortcuts import render

from website.templates2 import show_sources

# appstate lives in Django's session and are automatically saved at the end of each request.
def init_appstate():
    return []

init_appstate.namespace = "djangotemplates"

# djangotemplates is a vanilla Django view, with it's route defined in urls.py and not by autourls.
# It's decorated with @liveview to enable liveview capabilities.
@liveview(perm=NO_PERM_REQUIRED, autourl=False, appstate=init_appstate)
def djangotemplates(request):
    return render(request, "djangotemplates/content.html", context=dict(stack=context.hypergen.appstate,
        sources=hypergen(show_sources, __file__)))

def render_content():
    # render_to_hypergen() works exactly as Djangos render_to_string except for two things:
    #     1. It writes the HTML directly to the page.
    #     2. It supports a "block" keyword argument so that only the content of that block is rendered.
    render_to_hypergen("djangotemplates/content.html", context=dict(stack=context.hypergen.appstate,
        sources=hypergen(show_sources, __file__)), block="content")

###  ACTIONS ###
# @actions works exactly like vanilla hypergen actions, so the hypergen template language is enabled.
# Here we choose to use render_context to partially render a Django html template.

@action(perm=NO_PERM_REQUIRED, target_id="content", appstate=init_appstate)
def push(request, number):
    if number is not None:
        assert type(number) is float
        context.hypergen.appstate.append(number)

    render_content()

@action(perm=NO_PERM_REQUIRED, target_id="content", appstate=init_appstate)
def reset(request):
    context.hypergen.appstate = []
    render_content()

@action(perm=NO_PERM_REQUIRED, target_id="content", appstate=init_appstate)
def add(request, *args):
    apply_operation(operator.add)

@action(perm=NO_PERM_REQUIRED, target_id="content", appstate=init_appstate)
def subtract(request, *args):
    apply_operation(operator.sub)

@action(perm=NO_PERM_REQUIRED, target_id="content", appstate=init_appstate)
def multiply(request, *args):
    apply_operation(operator.mul)

@action(perm=NO_PERM_REQUIRED, target_id="content", appstate=init_appstate)
def divide(request, *args):
    if len(context.hypergen.appstate) and context.hypergen.appstate[-1] == 0:
        command("alert", "Can't divide by zero")
        return

    apply_operation(operator.truediv)

def apply_operation(op):
    if len(context.hypergen.appstate) < 2:
        command("alert", "Stack has too few elements")
        return

    b, a = context.hypergen.appstate.pop(), context.hypergen.appstate.pop()
    context.hypergen.appstate.append(op(a, b))

    render_content()