diff options
| -rw-r--r-- | equations/models.py | 119 | ||||
| -rw-r--r-- | equations/signals.py | 65 | ||||
| -rw-r--r-- | equations/templates/equations/equation_detail.html | 27 | ||||
| -rw-r--r-- | equations/templates/equations/equation_list.html | 24 | ||||
| -rw-r--r-- | equations/views.py | 31 | ||||
| -rw-r--r-- | settings.py.example | 87 | ||||
| -rw-r--r-- | templates/base.html | 2 | ||||
| -rw-r--r-- | templates/go.html | 6 | ||||
| -rw-r--r-- | urls.py | 31 | 
9 files changed, 381 insertions, 11 deletions
diff --git a/equations/models.py b/equations/models.py index 71a8362..6791eb2 100644 --- a/equations/models.py +++ b/equations/models.py @@ -1,3 +1,120 @@  from django.db import models +from django.utils.translation import gettext_lazy as _ +import django.contrib.auth.models as auth +from django.conf import settings +from django.dispatch import dispatcher +from django.db.models import signals +from signals import update_render -# Create your models here. +class Symbol(models.Model): +    name = models.CharField(_("Name"), maxlength=256) +    latex = models.TextField(_("Raw LaTeX"), unique=True) +    unicode = models.TextField(_("Unicode Representation"), blank=True) +    renderdir = "symbolrenders/" +    render = models.ImageField(_("Rendered Image"),  +        upload_to=renderdir, editable=False) + +    class Meta: +        get_latest_by = 'name' +    class Admin: +        ordering = ['name'] + +    def __str__(self): +        return self.name +    def get_absolute_url(self): +        return "/symbol/%s/" % self.id +    def get_admin_url(self): +        return "/admin/symbols/%s/" % self.id +    #def save(self): +    #    super(Symbol, self) +    #    self.update_generic_variable() +    def save(self): +        super(Symbol, self).save() +        self.render = self.renderdir + "%s.png" % self.id +        super(Symbol, self).save() + +def update_generic_variable(sender, instance, signal, *args, **kwargs): +    """Checks if there is a generic variable associated with this +        symbol; create one if there isn't""" +    for v in Variable.objects.filter(isgeneric=True): +        if v.latex == instance.latex: +            return +    genericv = Variable(name="Generic %s" % instance.name, +                        latex=instance.latex, +                        unicode=instance.unicode, +                        symbol=instance, +                        isgeneric=True) +    genericv.save() + +dispatcher.connect(update_render, signal=signals.pre_save, sender=Symbol) +dispatcher.connect(update_generic_variable, signal=signals.post_save,  +    sender=Symbol) + +class Variable(models.Model): +    name = models.CharField(_("Name"), maxlength=256) +    latex = models.TextField(_("Raw LaTeX"), unique=True) +    unicode = models.TextField(_("Unicode Representation"), blank=True) +    description = models.TextField(_("Description"), blank=True) +    renderdir = "variablerenders/" +    render = models.ImageField(_("Rendered Image"),  +        upload_to=renderdir, editable=False) +    reference = models.URLField(_("Reference URL"), blank=True) +    symbol = models.ForeignKey(Symbol, verbose_name=_("Symbol")) +    isgeneric = models.BooleanField(_("Is Generic?"), default=False) + +    class Meta: +        get_latest_by = 'name' +    class Admin: +        ordering = ['name'] + +    def __str__(self): +        return self.name +    def get_absolute_url(self): +        return "/variable/%s/" % self.id +    def get_admin_url(self): +        return "/admin/variables/%s/" % self.id +    def save(self): +        super(Variable, self).save() +        self.render = self.renderdir + "%s.png" % self.id +        super(Variable, self).save() + +dispatcher.connect(update_render, signal=signals.pre_save, sender=Variable) + +class Equation(models.Model): +    name = models.CharField(_("Name"), maxlength=256) +    latex = models.TextField(_("Raw LaTeX"), unique=True) +    unicode = models.TextField(_("Unicode Representation"), blank=True) +    description = models.TextField(_("Description"), blank=True) +    created = models.DateField(_("Created"), auto_now_add=True) +    updated = models.DateField(_("Last Updated"), auto_now_add=True) +    owner = models.ForeignKey(auth.User, verbose_name=_("Owner")) +    variables = models.ManyToManyField(Variable, verbose_name="Variables", +        editable=True) +    renderdir = "equationrenders/" +    render = models.ImageField(_("Rendered Image"),  +        upload_to=renderdir, editable=False) +    reference = models.URLField(_("Reference URL"), blank=True) + +    class Meta: +        get_latest_by = 'created' +    class Admin: +        ordering = ['created'] + +    def __str__(self): +        return self.name +    def get_absolute_url(self): +        return "/equation/%s/" % self.id +    def get_admin_url(self): +        return "/admin/equations/%s/" % self.id +    def update_variables(self): +        """Updates the variables field with all Variable objects found +           in the LaTeX representation.""" +        pass # TODO: write +    def save(self): +        self.update_variables() +        super(Equation, self).save() +        self.render = self.renderdir + "%s.png" % self.id +        super(Equation, self).save() + + +dispatcher.connect(update_render, signal=signals.post_save, sender=Equation) diff --git a/equations/signals.py b/equations/signals.py new file mode 100644 index 0000000..0fecc34 --- /dev/null +++ b/equations/signals.py @@ -0,0 +1,65 @@ +from django.conf import settings + +def update_render(sender, instance, signal, *args, **kwargs): +    """Renders an object's .png representation using LaTeX and puts it  +       in the right directory. Requires latex, dvipng, etc""" +    import os, shutil, tempfile, subprocess +    def call_command_in_dir(app, args, targetdir): +        cwd = os.getcwd() +        try: +            os.chdir(targetdir) +            p = subprocess.Popen(app + ' ' + ' '.join(args), shell=True) +            sts = os.waitpid(p.pid, 0) +            # FIXME -- should we raise an exception of status is non-zero? +        finally: +            # Restore working directory +            os.chdir(cwd) + +    rawlatex = instance.latex     +    dstdir = settings.MEDIA_ROOT + instance.renderdir +    dstfile = "%s.png" % instance.id +    dst = "%s" % instance.id + +    prologue = "" +    latex_template = r''' +    \documentclass[12pt]{article} +    \pagestyle{empty} +    %(prologue)s +    \begin{document} +    $$%(raw)s$$ +    \end{document} +    ''' +    max_pages = 1 +    MAX_RUN_TIME = 5 # seconds +    latex = "latex" +    dvipng = "dvipng" +    latex_args = ("--interaction=nonstopmode", "%s.tex") +    dvipng_args = ("-q", "-bgTransparent", "-Ttight", "--noghostscript",  +        "-l%s" % max_pages, "%s.dvi") +    tex = latex_template % { 'raw': rawlatex, 'prologue': prologue } + +    tmpdir = tempfile.mkdtemp() +    try: +        data = open("%s/%s.tex" % (tmpdir, dst), "w") +        data.write(tex) +        data.close() +        args = list(latex_args) +        args[-1] = args[-1] % dst +        res = call_command_in_dir(latex, args, tmpdir) +        if not res is None: +            # FIXME need to return some sort of error +            return [] +        args = list(dvipng_args) +        args[-1] = args[-1] % dst +        res = call_command_in_dir(dvipng, args, tmpdir) +        if not res is None: +            # FIXME need to return some sort of error +            return [] + +        if os.access("%s/%s1.png" % (tmpdir, dst), os.R_OK): +            shutil.copyfile("%s/%s1.png" % (tmpdir, dst), +                dstdir + dstfile) +    finally: +        # FIXME do some tidy up here +        instance.render = instance.renderdir + dstfile +    shutil.rmtree(tmpdir) diff --git a/equations/templates/equations/equation_detail.html b/equations/templates/equations/equation_detail.html new file mode 100644 index 0000000..b4c3911 --- /dev/null +++ b/equations/templates/equations/equation_detail.html @@ -0,0 +1,27 @@ +{% extends "base.html" %} + +{% block title %}{{ object.name }}{% endblock %} + +{% block content %} +<div style="width: 100px; text-align:center;"> +<img src="{{ object.get_render_url }}" style="border: none;" /></div> +<table width="100%"> +{% if object.description %} +  <tr><td><b>Description:</b></td><td>{{ object.description }}</td></tr> +{% endif %} +{% if object.variables %} +  <tr><td><b>Variables:</b></td><td> +    {% for variable in object.variables %} +    <a href="{{ variable.get_absolute_url }}">{{ variable.name }}</a><br /> +    {% endfor %} +{% endif %} +  <tr><td><b>Raw LaTeX:</b></td><td>{{ object.latex }}</td></tr> +{% if object.unicode %} +  <tr><td><b>Unicode:</b></td><td>{{ object.unicode }}</td></tr> +{% endif %} +  <tr><td></td><td><br /> +    This equation was created on <i>{{ object.created }}</i>;  +    it was last updated on <i>{{ object.updated }}</i>. It's owner is +    <i>{{ object.owner }}</i>.</td></tr> +</table> +{% endblock %} diff --git a/equations/templates/equations/equation_list.html b/equations/templates/equations/equation_list.html new file mode 100644 index 0000000..c7a9632 --- /dev/null +++ b/equations/templates/equations/equation_list.html @@ -0,0 +1,24 @@ +{% extends "base.html" %} + +{% block title %}Check out all these equations!{% endblock %} + +{% block content %} +{% if object_list %} +<table width="!00%"> +  {% for item in object_list %} +    <tr style="height:50px;"><td style="width:45%;"> +    <a href="{{ item.get_absolute_url }}" class="imglink"> +    <img style="border: none;" src="{{ item.get_render_url }}" /></a> +    </td><td style="width: 55%"><a href="{{ item.get_absolute_url }}"> +    {{ item.name }}</a></td></tr> +  {% endfor %} +</table> +{% if not is_paginated %}<br />{% if not has_previous %} +<b><a href="./?page={{ previous }}" style="float:left;"><PREV</a></b> +{% endif %}{% if has_next %} +<b><a href="./?page={{ next }}" style="float:right;">NEXT></a></b> +{% endif %}{% endif %} +{% else %} +Fuck, where are they? +{% endif %} +{% endblock %} diff --git a/equations/views.py b/equations/views.py index 60f00ef..8e2f6fa 100644 --- a/equations/views.py +++ b/equations/views.py @@ -1 +1,30 @@ -# Create your views here. +from django.core import serializers +from models import Equation, Variable, Symbol +from django.http import HttpResponse + +def all_vars(request): +    data = serializers.serialize("json", Variable.objects.all()) +    return HttpResponse(data, mimetype="text/javascript") + +def all_symbs(request): +    data = serializers.serialize("json", Symbol.objects.all()) +    return HttpResponse(data, mimetype="text/javascript") + +def equs_by_vars(request, whichvars): +    vars = whichvars.split(',') +    if len(vars) < 1: +        return HttpResponse('[]', mimetype="text/javascript") +    #if vars[-1] == '/': +    #    vars = vars[:-1] +    returnables = Equation.objects.filter(variables=vars[0]) +    if len(vars) > 1: +        for r in returnables: +            for i in vars[1:]: +                if not i in r.variables: +                    returnables = returnables.exclude(id=r.id) +                    if len(returnables) < 1: +                        break +     +    data = serializers.serialize("json", returnables) +    return HttpResponse(data, mimetype="text/javascript") + diff --git a/settings.py.example b/settings.py.example new file mode 100644 index 0000000..432bc77 --- /dev/null +++ b/settings.py.example @@ -0,0 +1,87 @@ +# Django settings for equator project. + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( +    # ('Your Name', 'your_email@domain.com'), +) + +MANAGERS = ADMINS + +DATABASE_ENGINE = 'sqlite3'           # 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'. +DATABASE_NAME = 'equator/eq.db'             # Or path to database file if using sqlite3. +DATABASE_USER = ''             # Not used with sqlite3. +DATABASE_PASSWORD = ''         # Not used with sqlite3. +DATABASE_HOST = ''             # Set to empty string for localhost. Not used with sqlite3. +DATABASE_PORT = ''             # Set to empty string for default. Not used with sqlite3. + +# Local time zone for this installation. All choices can be found here: +# http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = 'America/Boston' + +# Language code for this installation. All choices can be found here: +# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes +# http://blogs.law.harvard.edu/tech/stories/storyReader$15 +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 + +# Absolute path to the directory that holds media. +# Example: "/home/media/media.lawrence.com/" +MEDIA_ROOT = 'equator/static/' + +# URL that handles the media served from MEDIA_ROOT. +# Example: "http://media.lawrence.com" +MEDIA_URL = '/static/' + +# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a +# trailing slash. +# Examples: "http://foo.com/media/", "/media/". +ADMIN_MEDIA_PREFIX = '/static/admin-media/' + +# Make this unique, and don't share it with anybody. +SECRET_KEY = 'changeme' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( +    'django.template.loaders.filesystem.load_template_source', +    'django.template.loaders.app_directories.load_template_source', +#     'django.template.loaders.eggs.load_template_source', +) + +MIDDLEWARE_CLASSES = ( +    'django.middleware.common.CommonMiddleware', +    'django.contrib.sessions.middleware.SessionMiddleware', +    'django.contrib.auth.middleware.AuthenticationMiddleware', +    'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', +    'django.contrib.csrf.middleware.CsrfMiddleware', +    'django.middleware.doc.XViewMiddleware', +) + +ROOT_URLCONF = 'equator.urls' + +TEMPLATE_DIRS = ( +    'equator/templates/', +    # 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. +) + +INSTALLED_APPS = ( +    'django.contrib.auth', +    'django.contrib.contenttypes', +    'django.contrib.sessions', +    'django.contrib.sites', +    'django.contrib.admin', +    'django.contrib.flatpages', +    'django.contrib.markup', +    'django.contrib.comments', +    'equator.equations', +) diff --git a/templates/base.html b/templates/base.html index c2e023b..82579c5 100644 --- a/templates/base.html +++ b/templates/base.html @@ -9,7 +9,7 @@  </head><body>  <div class="main">  <div style="width: 100%; text-align: center;"> -<h1>{% block title %}The Equator% endblock %}</h1> +<h1>{% block title %}The Equator{% endblock %}</h1>  </div>  {% block content %}  no content? diff --git a/templates/go.html b/templates/go.html index cb1e855..5569cf2 100644 --- a/templates/go.html +++ b/templates/go.html @@ -1,7 +1,7 @@  {% extends "base.html" %}  {% block javascript %} -<script type="text/javascript" src="The%20Equator_files/prototype.js"> +<script type="text/javascript" src="/static/prototype.js">  </script>  <script type="text/javascript">  var variables = [] @@ -9,7 +9,7 @@ var equations = []  function get_variables() {      $('status_bin').innerHTML= "Fetching..."      var initialVariablesRequest = new Ajax.Request( -        "/variable/json/all/", +        "/json/all_vars/",          { method: 'get',             onSuccess: handle_variables });  } @@ -44,7 +44,7 @@ function toggle_variable(vnum) {          }      }      var freshEquationsRequest = new Ajax.Request( -        "/equation/json/byvariables/" + whichvars, +        "/json/equs_by_vars/" + whichvars,          { method: 'get',             onSuccess: handle_equations});  } @@ -1,9 +1,30 @@  from django.conf.urls.defaults import * +from django.conf import settings +from equations.models import Equation, Symbol, Variable  urlpatterns = patterns('', -    # Example: -    # (r'^equator/', include('equator.foo.urls')), - -    # Uncomment this for admin: -#     (r'^admin/', include('django.contrib.admin.urls')), +    (r'^go/', 'django.views.generic.simple.direct_to_template', +        {'template': 'go.html'}), +    (r'^admin/', include('django.contrib.admin.urls')), +    (r'^static/(?P<path>.*)$', 'django.views.static.serve', +        {'document_root': settings.MEDIA_ROOT}), +) +urlpatterns += patterns('django.views.generic.list_detail', +    (r'^equation/$', 'object_list', dict(queryset=Equation.objects.all(), +        paginate_by=20, allow_empty=True)), +    (r'^equation/(?P<object_id>\d+)/$', 'object_detail', +        dict(queryset=Equation.objects.all() )), +    (r'^symbol/$', 'object_list', dict(queryset=Symbol.objects.all(), +        paginate_by=20, allow_empty=True)), +    (r'^symbol/(?P<object_id>\d+)/$', 'object_detail', +        dict(queryset=Symbol.objects.all() )), +    (r'^variable/$', 'object_list', dict(queryset=Variable.objects.all(), +        paginate_by=20, allow_empty=True)), +    (r'^variable/(?P<object_id>\d+)/$', 'object_detail', +        dict(queryset=Variable.objects.all() )), +) +urlpatterns += patterns('equator.equations.views', +    (r'^json/equs_by_vars/(?P<whichvars>[\d\,]*)/?$', 'equs_by_vars'), +    (r'^json/all_vars/$', 'all_vars'), +    (r'^json/all_symbs/$', 'all_symbs'),  )  | 
