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'), ) |