Changeset - 7596922f4d68
[Not reviewed]
0 0 10
Luke Hatcher - 12 years ago 2012-07-12 04:38:39
lukeman@gmail.com
add proposals app from pycon
10 files changed with 697 insertions and 0 deletions:
0 comments (0 inline, 0 general)
symposion/proposals/__init__.py
Show inline comments
 
new file 100644
symposion/proposals/actions.py
Show inline comments
 
new file 100644
 
import csv
 

	
 
from django.http import HttpResponse
 

	
 

	
 
def export_as_csv_action(description="Export selected objects as CSV file",
 
                         fields=None, exclude=None, header=True):
 
    """
 
    This function returns an export csv action
 
    'fields' and 'exclude' work like in django ModelForm
 
    'header' is whether or not to output the column names as the first row
 
    """
 
    def export_as_csv(modeladmin, request, queryset):
 
        """
 
        Generic csv export admin action.
 
        based on http://djangosnippets.org/snippets/1697/
 
        """
 
        opts = modeladmin.model._meta
 
        if fields:
 
            fieldset = set(fields)
 
            field_names = fieldset
 
        elif exclude:
 
            excludeset = set(exclude)
 
            field_names = field_names - excludeset
 

	
 
        response = HttpResponse(mimetype='text/csv')
 
        response['Content-Disposition'] = 'attachment; filename=%s.csv' % unicode(opts).replace('.', '_')
 

	
 
        writer = csv.writer(response)
 
        if header:
 
            writer.writerow(list(field_names))
 
        for obj in queryset:
 
            writer.writerow([unicode(getattr(obj, field)).encode("utf-8", "replace") for field in field_names])
 
        return response
 
    export_as_csv.short_description = description
 
    return export_as_csv
symposion/proposals/admin.py
Show inline comments
 
new file 100644
 
from django.contrib import admin
 

	
 
# from symposion.proposals.actions import export_as_csv_action
 
from symposion.proposals.models import ProposalSection, ProposalKind
 

	
 

	
 
# admin.site.register(Proposal,
 
#     list_display = [
 
#         "id",
 
#         "title",
 
#         "speaker",
 
#         "speaker_email",
 
#         "kind",
 
#         "audience_level",
 
#         "cancelled",
 
#     ],
 
#     list_filter = [
 
#         "kind__name",
 
#         "result__accepted",
 
#     ],
 
#     actions = [export_as_csv_action("CSV Export", fields=[
 
#         "id",
 
#         "title",
 
#         "speaker",
 
#         "speaker_email",
 
#         "kind",
 
#     ])]
 
# )
 

	
 

	
 
admin.site.register(ProposalSection)
 
admin.site.register(ProposalKind)
symposion/proposals/forms.py
Show inline comments
 
new file 100644
 
from django import forms
 
from django.db.models import Q
 

	
 
from symposion.proposals.models import SupportingDocument
 
# from markitup.widgets import MarkItUpWidget
 

	
 

	
 
# @@@ generic proposal form
 

	
 

	
 
class AddSpeakerForm(forms.Form):
 
    
 
    email = forms.EmailField(
 
        label="Email address of new speaker (use their email address, not yours)"
 
    )
 
    
 
    def __init__(self, *args, **kwargs):
 
        self.proposal = kwargs.pop("proposal")
 
        super(AddSpeakerForm, self).__init__(*args, **kwargs)
 
    
 
    def clean_email(self):
 
        value = self.cleaned_data["email"]
 
        exists = self.proposal.additional_speakers.filter(
 
            Q(user=None, invite_email=value) |
 
            Q(user__email=value)
 
        ).exists()
 
        if exists:
 
            raise forms.ValidationError(
 
                "This email address has already been invited to your talk proposal"
 
            )
 
        return value
 

	
 

	
 
class SupportingDocumentCreateForm(forms.ModelForm):
 
    
 
    class Meta:
 
        model = SupportingDocument
 
        fields = [
 
            "file",
 
            "description",
 
        ]
symposion/proposals/managers.py
Show inline comments
 
new file 100644
 
from django.db import models
 
from django.db.models.query import QuerySet
 

	
 

	
 
class CachingM2MQuerySet(QuerySet):
 
    
 
    def __init__(self, *args, **kwargs):
 
        super(CachingM2MQuerySet, self).__init__(*args, **kwargs)
 
        self.cached_m2m_field = kwargs["m2m_field"]
 
    
 
    def iterator(self):
 
        parent_iter = super(CachingM2MQuerySet, self).iterator()
 
        m2m_model = getattr(self.model, self.cached_m2m_field).through
 
        
 
        for obj in parent_iter:
 
            if obj.id in cached_objects:
 
                setattr(obj, "_cached_m2m_%s" % self.cached_m2m_field)
 
            yield obj
 

	
 

	
 
class ProposalManager(models.Manager):
 
    def cache_m2m(self, m2m_field):
 
        return CachingM2MQuerySet(self.model, using=self._db, m2m_field=m2m_field)
 
        AdditionalSpeaker = queryset.model.additional_speakers.through
 
        additional_speakers = collections.defaultdict(set)
 
        for additional_speaker in AdditionalSpeaker._default_manager.filter(proposal__in=queryset).select_related("speaker__user"):
 
            additional_speakers[additional_speaker.proposal_id].add(additional_speaker.speaker)
...
 
\ No newline at end of file
symposion/proposals/models.py
Show inline comments
 
new file 100644
 
import datetime
 
import os
 
import uuid
 

	
 
from django.core.urlresolvers import reverse
 
from django.db import models
 
from django.db.models import Q
 

	
 
from django.contrib.auth.models import User
 

	
 
from markitup.fields import MarkupField
 

	
 
from model_utils.managers import InheritanceManager
 

	
 
from symposion.conference.models import Section
 

	
 

	
 
class ProposalSection(models.Model):
 
    """
 
    configuration of proposal submissions for a specific Section.
 
    
 
    a section is available for proposals iff:
 
      * it is after start (if there is one) and
 
      * it is before end (if there is one) and
 
      * closed is NULL or False
 
    """
 
    
 
    section = models.OneToOneField(Section)
 
    
 
    start = models.DateTimeField(null=True, blank=True)
 
    end = models.DateTimeField(null=True, blank=True)
 
    closed = models.NullBooleanField()
 
    published = models.NullBooleanField()
 
    
 
    @classmethod
 
    def available(cls):
 
        now = datetime.datetime.now()
 
        return cls._default_manager.filter(
 
            Q(start__lt=now) | Q(start=None),
 
            Q(end__gt=now) | Q(end=None),
 
            Q(closed=False) | Q(closed=None),
 
        )
 
    
 
    def __unicode__(self):
 
        return self.section.name
 

	
 

	
 
class ProposalKind(models.Model):
 
    """
 
    e.g. talk vs panel vs tutorial vs poster
 
    
 
    Note that if you have different deadlines, reviewers, etc. you'll want
 
    to distinguish the section as well as the kind.
 
    """
 
    
 
    section = models.ForeignKey(Section, related_name="proposal_kinds")
 
    
 
    name = models.CharField("name", max_length=100)
 
    slug = models.SlugField()
 
    
 
    def __unicode__(self):
 
        return self.name
 

	
 

	
 
class ProposalBase(models.Model):
 
    
 
    objects = InheritanceManager()
 
    
 
    kind = models.ForeignKey(ProposalKind)
 
    
 
    title = models.CharField(max_length=100)
 
    description = models.TextField(
 
        max_length=400,  # @@@ need to enforce 400 in UI
 
        help_text="If your talk is accepted this will be made public and printed in the program. Should be one paragraph, maximum 400 characters."
 
    )
 
    abstract = MarkupField(
 
        help_text="Detailed description and outline. Will be made public if your talk is accepted. Edit using <a href='http://warpedvisions.org/projects/markdown-cheat-sheet/' target='_blank'>Markdown</a>."
 
    )
 
    additional_notes = MarkupField(
 
        blank=True,
 
        help_text="Anything else you'd like the program committee to know when making their selection: your past speaking experience, open source community experience, etc. Edit using <a href='http://warpedvisions.org/projects/markdown-cheat-sheet/' target='_blank'>Markdown</a>."
 
    )
 
    submitted = models.DateTimeField(
 
        default=datetime.datetime.now,
 
        editable=False,
 
    )
 
    speaker = models.ForeignKey("speakers.Speaker", related_name="proposals")
 
    additional_speakers = models.ManyToManyField("speakers.Speaker", through="AdditionalSpeaker", blank=True)
 
    cancelled = models.BooleanField(default=False)
 
    
 
    def can_edit(self):
 
        return True
 
    
 
    @property
 
    def speaker_email(self):
 
        return self.speaker.email
 

	
 
    @property
 
    def number(self):
 
        return str(self.pk).zfill(3)
 
    
 
    def speakers(self):
 
        yield self.speaker
 
        for speaker in self.additional_speakers.exclude(additionalspeaker__status=AdditionalSpeaker.SPEAKING_STATUS_DECLINED):
 
            yield speaker
 

	
 

	
 
class AdditionalSpeaker(models.Model):
 
    
 
    SPEAKING_STATUS_PENDING = 1
 
    SPEAKING_STATUS_ACCEPTED = 2
 
    SPEAKING_STATUS_DECLINED = 3
 
    
 
    SPEAKING_STATUS = [
 
        (SPEAKING_STATUS_PENDING, "Pending"),
 
        (SPEAKING_STATUS_ACCEPTED, "Accepted"),
 
        (SPEAKING_STATUS_DECLINED, "Declined"),
 
    ]
 
    
 
    speaker = models.ForeignKey("speakers.Speaker")
 
    proposalbase = models.ForeignKey(ProposalBase)
 
    status = models.IntegerField(choices=SPEAKING_STATUS, default=SPEAKING_STATUS_PENDING)
 
    
 
    class Meta:
 
        db_table = "proposals_proposalbase_additional_speakers"
 
        unique_together = ("speaker", "proposalbase")
 

	
 

	
 
def uuid_filename(instance, filename):
 
    ext = filename.split(".")[-1]
 
    filename = "%s.%s" % (uuid.uuid4(), ext)
 
    return os.path.join("document", filename)
 

	
 

	
 
class SupportingDocument(models.Model):
 
    
 
    proposal = models.ForeignKey(ProposalBase, related_name="supporting_documents")
 
    
 
    uploaded_by = models.ForeignKey(User)
 
    created_at = models.DateTimeField(default=datetime.datetime.now)
 
    
 
    file = models.FileField(upload_to=uuid_filename)
 
    description = models.CharField(max_length=140)
 

	
 
    def download_url(self):
 
        return reverse("proposal_document_download", args=[self.pk, os.path.basename(self.file.name).lower()])
symposion/proposals/templatetags/__init__.py
Show inline comments
 
new file 100644
symposion/proposals/templatetags/proposal_tags.py
Show inline comments
 
new file 100644
 
from django import template
 

	
 
from symposion.proposals.models import AdditionalSpeaker
 

	
 

	
 
register = template.Library()
 

	
 

	
 
class AssociatedProposalsNode(template.Node):
 
    
 
    @classmethod
 
    def handle_token(cls, parser, token):
 
        bits = token.split_contents()
 
        if len(bits) == 3 and bits[1] == "as":
 
            return cls(bits[2])
 
        else:
 
            raise template.TemplateSyntaxError("%r takes 'as var'" % bits[0])
 
    
 
    def __init__(self, context_var):
 
        self.context_var = context_var
 
    
 
    def render(self, context):
 
        request = context["request"]
 
        if request.user.speaker_profile:
 
            pending = AdditionalSpeaker.SPEAKING_STATUS_ACCEPTED
 
            speaker = request.user.speaker_profile
 
            queryset = AdditionalSpeaker.objects.filter(speaker=speaker, status=pending)
 
            context[self.context_var] = [item.proposalbase for item in queryset]
 
        else:
 
            context[self.context_var] = None
 
        return u""
 

	
 

	
 
class PendingProposalsNode(template.Node):
 
    
 
    @classmethod
 
    def handle_token(cls, parser, token):
 
        bits = token.split_contents()
 
        if len(bits) == 3 and bits[1] == "as":
 
            return cls(bits[2])
 
        else:
 
            raise template.TemplateSyntaxError("%r takes 'as var'" % bits[0])
 
    
 
    def __init__(self, context_var):
 
        self.context_var = context_var
 
    
 
    def render(self, context):
 
        request = context["request"]
 
        if request.user.speaker_profile:
 
            pending = AdditionalSpeaker.SPEAKING_STATUS_PENDING
 
            speaker = request.user.speaker_profile
 
            queryset = AdditionalSpeaker.objects.filter(speaker=speaker, status=pending)
 
            context[self.context_var] = [item.proposalbase for item in queryset]
 
        else:
 
            context[self.context_var] = None
 
        return u""
 

	
 

	
 
@register.tag
 
def pending_proposals(parser, token):
 
    """
 
    {% pending_proposals as pending_proposals %}
 
    """
 
    return PendingProposalsNode.handle_token(parser, token)
 

	
 

	
 
@register.tag
 
def associated_proposals(parser, token):
 
    """
 
    {% associated_proposals as associated_proposals %}
 
    """
 
    return AssociatedProposalsNode.handle_token(parser, token)
 

	
symposion/proposals/urls.py
Show inline comments
 
new file 100644
 
from django.conf.urls.defaults import *
 

	
 

	
 
urlpatterns = patterns("symposion.proposals.views",
 
    url(r"^submit/$", "proposal_submit", name="proposal_submit"),
 
    url(r"^submit/(\w+)/$", "proposal_submit_kind", name="proposal_submit_kind"),
 
    url(r"^(\d+)/$", "proposal_detail", name="proposal_detail"),
 
    url(r"^(\d+)/edit/$", "proposal_edit", name="proposal_edit"),
 
    url(r"^(\d+)/speakers/$", "proposal_speaker_manage", name="proposal_speaker_manage"),
 
    url(r"^(\d+)/cancel/$", "proposal_cancel", name="proposal_cancel"),
 
    url(r"^(\d+)/leave/$", "proposal_leave", name="proposal_leave"),
 
    url(r"^(\d+)/join/$", "proposal_pending_join", name="proposal_pending_join"),
 
    url(r"^(\d+)/decline/$", "proposal_pending_decline", name="proposal_pending_decline"),
 
    
 
    url(r"^(\d+)/document/create/$", "document_create", name="proposal_document_create"),
 
    url(r"^document/(\d+)/delete/$", "document_delete", name="proposal_document_delete"),
 
    url(r"^document/(\d+)/([^/]+)$", "document_download", name="proposal_document_download"),
 
)
symposion/proposals/views.py
Show inline comments
 
new file 100644
 
import random
 
import sys
 

	
 
from django.conf import settings
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.db.models import Q
 
from django.http import Http404, HttpResponse, HttpResponseForbidden
 
from django.shortcuts import render, redirect, get_object_or_404
 
from django.utils.hashcompat import sha_constructor
 
from django.views import static
 

	
 
from django.contrib import messages
 
from django.contrib.auth.decorators import login_required
 

	
 
from account.models import EmailAddress
 
from symposion.proposals.models import ProposalBase, ProposalSection, ProposalKind
 
from symposion.proposals.models import SupportingDocument, AdditionalSpeaker
 
from symposion.speakers.models import Speaker
 
from symposion.utils.mail import send_email
 

	
 
from symposion.proposals.forms import AddSpeakerForm, SupportingDocumentCreateForm
 

	
 

	
 
def get_form(name):
 
    dot = name.rindex('.')
 
    mod_name, form_name = name[:dot], name[dot + 1:]
 
    __import__(mod_name)
 
    return getattr(sys.modules[mod_name], form_name)
 

	
 

	
 
def proposal_submit(request):
 
    if not request.user.is_authenticated():
 
        return redirect("home")  # @@@ unauth'd speaker info page?
 
    else:
 
        try:
 
            request.user.speaker_profile
 
        except ObjectDoesNotExist:
 
            return redirect("dashboard")
 
    
 
    kinds = []
 
    for proposal_section in ProposalSection.available():
 
        for kind in proposal_section.section.proposal_kinds.all():
 
            kinds.append(kind)
 
    
 
    return render(request, "proposals/proposal_submit.html", {
 
        "kinds": kinds,
 
    })
 

	
 

	
 
def proposal_submit_kind(request, kind_slug):
 
    
 
    kind = get_object_or_404(ProposalKind, slug=kind_slug)
 
    
 
    if not request.user.is_authenticated():
 
        return redirect("home")  # @@@ unauth'd speaker info page?
 
    else:
 
        try:
 
            speaker_profile = request.user.speaker_profile
 
        except ObjectDoesNotExist:
 
            return redirect("dashboard")
 
    
 
    form_class = get_form(settings.PROPOSAL_FORMS[kind_slug])
 
    
 
    if request.method == "POST":
 
        form = form_class(request.POST)
 
        if form.is_valid():
 
            proposal = form.save(commit=False)
 
            proposal.kind = kind
 
            proposal.speaker = speaker_profile
 
            proposal.save()
 
            form.save_m2m()
 
            messages.success(request, "Proposal submitted.")
 
            if "add-speakers" in request.POST:
 
                return redirect("proposal_speaker_manage", proposal.pk)
 
            return redirect("dashboard")
 
    else:
 
        form = form_class()
 
    
 
    return render(request, "proposals/proposal_submit_kind.html", {
 
        "kind": kind,
 
        "form": form,
 
    })
 

	
 

	
 
@login_required
 
def proposal_speaker_manage(request, pk):
 
    queryset = ProposalBase.objects.select_related("speaker")
 
    proposal = get_object_or_404(queryset, pk=pk)
 
    proposal = ProposalBase.objects.get_subclass(pk=proposal.pk)
 
    
 
    if proposal.speaker != request.user.speaker_profile:
 
        raise Http404()
 
    
 
    if request.method == "POST":
 
        add_speaker_form = AddSpeakerForm(request.POST, proposal=proposal)
 
        if add_speaker_form.is_valid():
 
            message_ctx = {
 
                "proposal": proposal,
 
            }
 
            
 
            def create_speaker_token(email_address):
 
                # create token and look for an existing speaker to prevent
 
                # duplicate tokens and confusing the pending speaker
 
                try:
 
                    pending = Speaker.objects.get(
 
                        Q(user=None, invite_email=email_address)
 
                    )
 
                except Speaker.DoesNotExist:
 
                    salt = sha_constructor(str(random.random())).hexdigest()[:5]
 
                    token = sha_constructor(salt + email_address).hexdigest()
 
                    pending = Speaker.objects.create(
 
                        invite_email=email_address,
 
                        invite_token=token,
 
                    )
 
                else:
 
                    token = pending.invite_token
 
                return pending, token
 
            email_address = add_speaker_form.cleaned_data["email"]
 
            # check if email is on the site now
 
            users = EmailAddress.objects.get_users_for(email_address)
 
            if users:
 
                # should only be one since we enforce unique email
 
                user = users[0]
 
                message_ctx["user"] = user
 
                # look for speaker profile
 
                try:
 
                    speaker = user.speaker_profile
 
                except ObjectDoesNotExist:
 
                    speaker, token = create_speaker_token(email_address)
 
                    message_ctx["token"] = token
 
                    # fire off email to user to create profile
 
                    send_email(
 
                        [email_address], "speaker_no_profile",
 
                        context = message_ctx
 
                    )
 
                else:
 
                    # fire off email to user letting them they are loved.
 
                    send_email(
 
                        [email_address], "speaker_addition",
 
                        context = message_ctx
 
                    )
 
            else:
 
                speaker, token = create_speaker_token(email_address)
 
                message_ctx["token"] = token
 
                # fire off email letting user know about site and to create
 
                # account and speaker profile
 
                send_email(
 
                    [email_address], "speaker_invite",
 
                    context = message_ctx
 
                )
 
            invitation, created = AdditionalSpeaker.objects.get_or_create(proposalbase=proposal.proposalbase_ptr, speaker=speaker)
 
            messages.success(request, "Speaker invited to proposal.")
 
            return redirect("proposal_speaker_manage", proposal.pk)
 
    else:
 
        add_speaker_form = AddSpeakerForm(proposal=proposal)
 
    ctx = {
 
        "proposal": proposal,
 
        "speakers": proposal.speakers(),
 
        "add_speaker_form": add_speaker_form,
 
    }
 
    return render(request, "proposals/proposal_speaker_manage.html", ctx)
 

	
 

	
 
@login_required
 
def proposal_edit(request, pk):
 
    queryset = ProposalBase.objects.select_related("speaker")
 
    proposal = get_object_or_404(queryset, pk=pk)
 
    proposal = ProposalBase.objects.get_subclass(pk=proposal.pk)
 

	
 
    if request.user != proposal.speaker.user:
 
        raise Http404()
 
    
 
    if not proposal.can_edit():
 
        ctx = {
 
            "title": "Proposal editing closed",
 
            "body": "Proposal editing is closed for this session type."
 
        }
 
        return render(request, "proposals/proposal_error.html", ctx)
 
    
 
    form_class = get_form(settings.PROPOSAL_FORMS[proposal.kind.slug])
 

	
 
    if request.method == "POST":
 
        form = form_class(request.POST, instance=proposal)
 
        if form.is_valid():
 
            form.save()
 
            messages.success(request, "Proposal updated.")
 
            return redirect("proposal_detail", proposal.pk)
 
    else:
 
        form = form_class(instance=proposal)
 
    
 
    return render(request, "proposals/proposal_edit.html", {
 
        "proposal": proposal,
 
        "form": form,
 
    })
 

	
 

	
 
@login_required
 
def proposal_detail(request, pk):
 
    queryset = ProposalBase.objects.select_related("speaker", "speaker__user")
 
    proposal = get_object_or_404(queryset, pk=pk)
 
    proposal = ProposalBase.objects.get_subclass(pk=proposal.pk)
 
    
 
    if request.user not in [p.user for p in proposal.speakers()]:
 
        raise Http404()
 
    
 
    return render(request, "proposals/proposal_detail.html", {
 
        "proposal": proposal,
 
    })
 

	
 

	
 
@login_required
 
def proposal_cancel(request, pk):
 
    queryset = ProposalBase.objects.select_related("speaker")
 
    proposal = get_object_or_404(queryset, pk=pk)
 
    proposal = ProposalBase.objects.get_subclass(pk=proposal.pk)
 
    
 
    if proposal.speaker.user != request.user:
 
        return HttpResponseForbidden()
 

	
 
    if request.method == "POST":
 
        proposal.cancelled = True
 
        proposal.save()
 
        # @@@ fire off email to submitter and other speakers
 
        messages.success(request, "%s has been cancelled" % proposal.title)
 
        return redirect("dashboard")
 
    
 
    return render(request, "proposals/proposal_cancel.html", {
 
        "proposal": proposal,
 
    })
 

	
 

	
 
@login_required
 
def proposal_leave(request, pk):
 
    queryset = ProposalBase.objects.select_related("speaker")
 
    proposal = get_object_or_404(queryset, pk=pk)
 
    proposal = ProposalBase.objects.get_subclass(pk=proposal.pk)
 

	
 
    try:
 
        speaker = proposal.additional_speakers.get(user=request.user)
 
    except ObjectDoesNotExist:
 
        return HttpResponseForbidden()
 
    if request.method == "POST":
 
        proposal.additional_speakers.remove(speaker)
 
        # @@@ fire off email to submitter and other speakers
 
        messages.success(request, "You are no longer speaking on %s" % proposal.title)
 
        return redirect("speaker_dashboard")
 
    ctx = {
 
        "proposal": proposal,
 
    }
 
    return render(request, "proposals/proposal_leave.html", ctx)
 

	
 

	
 
@login_required
 
def proposal_pending_join(request, pk):
 
    proposal = get_object_or_404(ProposalBase, pk=pk)
 
    speaking = get_object_or_404(AdditionalSpeaker, speaker=request.user.speaker_profile, proposalbase=proposal)
 
    if speaking.status == AdditionalSpeaker.SPEAKING_STATUS_PENDING:
 
        speaking.status = AdditionalSpeaker.SPEAKING_STATUS_ACCEPTED
 
        speaking.save()
 
        messages.success(request, "You have accepted the invitation to join %s" % proposal.title)
 
        return redirect("dashboard")
 
    else:
 
        return redirect("dashboard")
 

	
 

	
 
@login_required
 
def proposal_pending_decline(request, pk):
 
    proposal = get_object_or_404(ProposalBase, pk=pk)
 
    speaking = get_object_or_404(AdditionalSpeaker, speaker=request.user.speaker_profile, proposalbase=proposal)
 
    if speaking.status == AdditionalSpeaker.SPEAKING_STATUS_PENDING:
 
        speaking.status = AdditionalSpeaker.SPEAKING_STATUS_DECLINED
 
        speaking.save()
 
        messages.success(request, "You have declined to speak on %s" % proposal.title)
 
        return redirect("dashboard")
 
    else:
 
        return redirect("dashboard")
 

	
 

	
 
@login_required
 
def document_create(request, proposal_pk):
 
    queryset = ProposalBase.objects.select_related("speaker")
 
    proposal = get_object_or_404(queryset, pk=proposal_pk)
 
    proposal = ProposalBase.objects.get_subclass(pk=proposal.pk)
 
    
 
    if request.method == "POST":
 
        form = SupportingDocumentCreateForm(request.POST, request.FILES)
 
        if form.is_valid():
 
            document = form.save(commit=False)
 
            document.proposal = proposal
 
            document.uploaded_by = request.user
 
            document.save()
 
            return redirect("proposal_detail", proposal.pk)
 
    else:
 
        form = SupportingDocumentCreateForm()
 
        
 
    return render(request, "proposals/document_create.html", {
 
        "proposal": proposal,
 
        "form": form,
 
    })
 

	
 

	
 
@login_required
 
def document_download(request, pk, *args):
 
    document = get_object_or_404(SupportingDocument, pk=pk)
 
    if settings.USE_X_ACCEL_REDIRECT:
 
        response = HttpResponse()
 
        response["X-Accel-Redirect"] = document.file.url
 
        # delete content-type to allow Gondor to determine the filetype and
 
        # we definitely don't want Django's crappy default :-)
 
        del response["content-type"]
 
    else:
 
        response = static.serve(request, document.file.name, document_root=settings.MEDIA_ROOT)
 
    return response
 

	
 

	
 
@login_required
 
def document_delete(request, pk):
 
    document = get_object_or_404(SupportingDocument, pk=pk, uploaded_by=request.user)
 
    proposal_pk = document.proposal.pk
 
    
 
    if request.method == "POST":
 
        document.delete()
 
    
 
    return redirect("proposal_detail", proposal_pk)
0 comments (0 inline, 0 general)