Skip to content

Commit

Permalink
various changes to editing interface
Browse files Browse the repository at this point in the history
  • Loading branch information
bendavis78 committed Apr 25, 2012
1 parent 53ecc11 commit 2543da6
Show file tree
Hide file tree
Showing 9 changed files with 236 additions and 107 deletions.
14 changes: 0 additions & 14 deletions impositions/admin.py
@@ -1,17 +1,3 @@
from django.contrib import admin
import models

class TemplateRegionInline(admin.StackedInline):
model = models.TemplateRegion

class TemplateAdmin(admin.ModelAdmin):
inlines = [TemplateRegionInline]

class CompositionRegionInline(admin.StackedInline):
model = models.CompositionRegion

class CompositionAdmin(admin.ModelAdmin):
inlines = [CompositionRegionInline]

admin.site.register(models.Template, TemplateAdmin)
admin.site.register(models.Composition, CompositionAdmin)
12 changes: 5 additions & 7 deletions impositions/backends/cairo.py
@@ -1,7 +1,6 @@
from __future__ import absolute_import
import os
import re
import StringIO
import cairo
import poppler
import pango
Expand All @@ -21,12 +20,11 @@ def __init__(self, *args, **kwargs):
self.page = None
self.pdf = None

def setup_template(self, template):
def setup_template(self, source_path):
if self.cr and self.page and self.pdf:
return

# Get source document
source_path = template.file.path
self.document = poppler.document_new_from_file('file://{}'.format(source_path), None)
self.page = self.document.get_page(0)

Expand Down Expand Up @@ -101,7 +99,7 @@ def render_image_region(self, region):

def render(self, comp, fmt):
self.validate(comp)
self.setup_template(comp.template)
self.setup_template(comp.template.file.path)

for region in comp.regions.all():
self.cr.save()
Expand All @@ -122,17 +120,17 @@ def render(self, comp, fmt):

return self.output

def get_template_thumbnail(self, template):
def get_template_thumbnail(self, file_path):
# Check for rendered thumb in media
dir = os.path.join('impositions', 'templates')
full_dir = os.path.join(settings.MEDIA_ROOT, dir)
filename = '{}.png'.format(os.path.basename(template.file.name))
filename = '{}.png'.format(os.path.basename(file_path))
path = os.path.join(full_dir, filename)
if not os.path.exists(path):
if not os.path.exists(full_dir):
os.makedirs(full_dir)
file = open(path, 'w')
self.setup_template(template)
self.setup_template(file_path)
self.pdf.write_to_png(file)
file.close()
return os.path.join(dir, filename)
3 changes: 1 addition & 2 deletions impositions/data_loaders.py
Expand Up @@ -5,10 +5,9 @@ class BaseDataLoader(object):
model = None
fields = []

def __init__(self, request=None):
def __init__(self):
if not self.model:
raise ValueError("model is required for data loaders")
self.request = request
self.instance = None

def set_instance(self, instance):
Expand Down
55 changes: 52 additions & 3 deletions impositions/forms.py
Expand Up @@ -43,9 +43,9 @@ def __init__(self, *args, **kwargs):
self.fields['default_image'].initial = default_image
self.fields['default_image'].widget = forms.Select(choices=image_choices)

to_choices = lambda s: [(v.strip(),v.strip()) for v in s.split(',')]
font_choices = to_choices(template.fonts)
color_choices = to_choices(template.color_palette)
to_choices = lambda l: [(v.strip(),v.strip()) for v in l]
font_choices = to_choices(template.get_fonts())
color_choices = to_choices(template.get_color_palette())
self.fields['allowed_fonts'].widget = CSVSelectMultiple(choices=font_choices)
self.fields['allowed_colors'].widget = CSVSelectMultiple(choices=color_choices)
self.fields['allowed_font_sizes'].help_text = 'Specify font sizes points, separated by comma'
Expand Down Expand Up @@ -86,3 +86,52 @@ def _get_empty_form(self, **kwargs):

class CompositionForm(forms.ModelForm):
model = models.Composition

class CompRegionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.composition_instance = kwargs.pop('composition_instance', None)

super(CompRegionForm, self).__init__(*args, **kwargs)

# don't deal w/ empty form
if self.prefix.endswith('__prefix__'):
return

region = self.instance.template_region

fonts = font_sizes = colors = None
if region:
# get allowed values
fonts = region.get_allowed_fonts() or region.template.get_fonts() \
or utils.DEFAULT_FONTS
font_sizes = region.get_allowed_font_sizes() or utils.DEFAULT_FONTS
colors = region.get_allowed_colors() or region.template.get_color_palette() \
or utils.DEFAULT_COLORS

to_choices = lambda l: [(v,v) for v in l]
self.fields['font'] = forms.ChoiceField(choices=to_choices(fonts))
self.fields['font_size'] = forms.ChoiceField(choices=to_choices(font_sizes))
self.fields['fg_color'] = forms.ChoiceField(choices=to_choices(colors))

model = models.CompositionRegion

class BaseCompRegionFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance')
self.max_num = instance.template.regions.count()
super(BaseCompRegionFormSet, self).__init__(*args, **kwargs)

def _construct_form(self, index, **kwargs):
kwargs['composition_instance'] = self.instance
return super(BaseCompRegionFormSet, self)._construct_form(index, **kwargs)

def _get_empty_form(self, **kwargs):
kwargs['composition_instance'] = self.instance
return super(BaseCompRegionFormSet, self)._get_empty_form(**kwargs)
empty_form = property(_get_empty_form)

CompositionRegionFormSet = inlineformset_factory(models.Composition,
models.CompositionRegion,
form=CompRegionForm,
formset=BaseCompRegionFormSet,
extra=0)
69 changes: 69 additions & 0 deletions impositions/helpers.py
@@ -0,0 +1,69 @@
import django
from django.forms.models import inlineformset_factory

class InlineFormSetMixin(object):
"""
Allows addition of a single inline formset to a ModelForm
"""
formset_class = None
formset_model = None

def __init__(self, *args, **kwargs):
if not self.formset_class:
self.formset_class = inlineformset_factory(self.model,
self.formset_model)
super(InlineFormSetMixin, self).__init__(*args, **kwargs)

def get_formset_class(self):
return self.formset_class

def get_formset(self, formset_class):
return formset_class(**self.get_formset_kwargs())

def get_formset_kwargs(self):
kwargs = {'instance': self.object}
if django.VERSION >= (1,4):
kwargs['initial'] = self.get_initial()
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs

def get_formset_initial(self):
"""
Returns a list of initial-data dicts to use for the formet's initial data
"""
return []

def get(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
formset_class = self.get_formset_class()
form = self.get_form(form_class)
formset = self.get_formset(formset_class)
context = self.get_context_data(form=form, formset=formset)
return self.render_to_response(context)

def post(self, request, *args, **kwargs):
self.object = self.get_object()
form_class = self.get_form_class()
formset_class = self.get_formset_class()
form = self.get_form(form_class)
formset = self.get_formset(formset_class)
if form.is_valid() and formset.is_valid():
return self.form_valid(form, formset)
else:
return self.form_invalid(form, formset)

def form_valid(self, form, formset):
self.object = form.save()
formset.instance = self.object
formset.save()
return super(InlineFormSetMixin, self).form_valid(form)

def form_invalid(self, form, formset):
return self.render_to_response(self.get_context_data(form=form, formset=formset))


76 changes: 64 additions & 12 deletions impositions/models.py
@@ -1,6 +1,7 @@
import os
from decimal import Decimal
from django.db import models
from django.core.exceptions import ValidationError
from django.core.files.storage import FileSystemStorage
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
Expand Down Expand Up @@ -48,13 +49,40 @@ class Template(models.Model):
def __unicode__(self):
return self.name

def get_color_palette(self):
if self.color_palette:
colors = self.color_palette.split(',')
return [c.strip() for c in colors]
return []

def clean(self):
from impositions import utils
# make sure color values are valid
if self.color_palette:
palette = []
for c in self.color_palette.split(','):
try:
palette.append(utils.parse_color(c, True))
except ValueError, e:
raise ValidationError(str(e))
self.color_palette = ','.join(palette)

def get_fonts(self):
if self.fonts:
fonts = self.fonts.split(',')
return [f.strip() for f in fonts]
return []

def get_thumbnail(self):
from impositions.utils import get_rendering_backend
Backend = get_rendering_backend()
backend = Backend()
template = self
return backend.get_template_thumbnail(template)
return backend.get_template_thumbnail(self.file.path)

@models.permalink
def get_absolute_url(self):
return ('impositions-template-edit', [self.pk])


class TemplateRegion(models.Model):
template = models.ForeignKey(Template, related_name='regions')
Expand All @@ -77,6 +105,9 @@ class TemplateRegion(models.Model):
default_text = models.TextField('Default Text', blank=True)
default_image = models.CharField('Default Image', max_length=255, blank=True)

class Meta:
ordering = ['top', 'left']

@property
def size(self):
return self.width, self.height
Expand All @@ -86,12 +117,23 @@ def box(self):
return (self.top, self.left,
self.top + self.height,
self.left + self.width)
def get_allowed_colors(self):

def clean(self):
from impositions import utils
# make sure color values are valid
if self.allowed_colors:
colors = []
for c in self.allowed_colors.split(','):
try:
colors.append(utils.parse_color(c, True))
except ValueError, e:
raise ValidationError(str(e))
self.allowed_colors = ','.join(colors)

def get_allowed_colors(self):
if self.allowed_colors:
allowed = self.allowed_colors.split(',')
return [utils.parse_color(c) for c in allowed]
return [c.strip() for c in allowed]
return []

def get_allowed_fonts(self):
Expand All @@ -115,14 +157,22 @@ def __unicode__(self):
class Composition(models.Model):
template = models.ForeignKey(Template)
description = models.CharField(max_length=200)
# TODO: add format property so user can choose format (jpg, png, pdf)

def __unicode__(self):
return "[{0}] {1}".format(
self.template.__unicode__(),
self.description
)

def save(self):
if not self.pk:
# Automatically add regions for the newly created composition
super(Composition, self).save()
for region in self.template.regions.all():
CompositionRegion(composition=self, template_region=region).save()
else:
super(Composition, self).save()

def render(self, fmt, **kwargs):
from impositions.utils import get_rendering_backend
Backend = get_rendering_backend()
Expand All @@ -133,11 +183,10 @@ def get_context(self, request):
from impositions.utils import get_data_loader
context = {}
for source in self.data_sources.all():
context.update(source.get_context(request))
DataLoader = get_data_loader(self.loader.path)
DataLoader = get_data_loader(source.path)
data_loader = DataLoader(request)
data_loader.set_instance(self.content_object)
context.update(data_loader.get_context(self.loader.prefix))
context.update(data_loader.get_context(source.prefix))
return context

class CompositionDataSource(models.Model):
Expand All @@ -155,8 +204,8 @@ class CompositionRegion(models.Model):
font = models.CharField(max_length=50, blank=True)
font_size = models.DecimalField(max_digits=8, decimal_places=2,
null=True, blank=True)
bg_color = models.CharField(max_length=50, blank=True)
fg_color = models.CharField(max_length=50, blank=True)
bg_color = models.CharField('Background', max_length=50, blank=True)
fg_color = models.CharField('Color', max_length=50, blank=True)
border_color = models.CharField(max_length=50, blank=True)
border_size = models.IntegerField(null=True, blank=True)

Expand Down Expand Up @@ -189,8 +238,9 @@ def get_font_size(self):
allowed = self.template_region.get_allowed_font_sizes()
return len(allowed) > 0 and allowed[0] or None

def get_content(self, context):
def get_content(self):
from django import template
context = self.get_context()
type = self.template_region.content_type
if type == 'image':
if self.image:
Expand All @@ -199,6 +249,8 @@ def get_content(self, context):
return context.get(self.template_region.default_image, None)
return None
elif type == 'text':
if self.text:
return self.text
tpl = template.Template(self.template_region.default_text)
default = tpl.render(template.Context(context)).strip()
return self.text.strip() or default
3 changes: 2 additions & 1 deletion impositions/urls.py
Expand Up @@ -4,5 +4,6 @@
url('^templates/$', 'template_list', name='impositions-template-list'),
url('^templates/create/$', 'template_create', name='impositions-template-create'),
url('^templates/(?P<pk>\d+)/$', 'template_edit', name='impositions-template-edit'),
url('^comps/(?P<id>\d+)/$', 'comp_edit', name='impositions-comp-edit'),
url('^templates/(?P<pk>\d+)/delete/$', 'template_delete', name='impositions-template-delete'),
url('^comps/(?P<pk>\d+)/$', 'comp_edit', name='impositions-comp-edit'),
)

0 comments on commit 2543da6

Please sign in to comment.