Changeset - 252697b842c0
[Not reviewed]
! ! !
Joel Addison - 3 years ago 2020-11-22 13:58:14
joel@addison.net.au
Update to Django 2.2

Upgrade site and modules to Django 2.2. Remove and replace obsolete
functionality with current equivalents. Update requirements to latest
versions where possible. Remove unused dependencies.
78 files changed with 646 insertions and 364 deletions:
0 comments (0 inline, 0 general)
constraints.txt
Show inline comments
 
django<1.12,>=1.11
 
pysaml2==4.8.0
 
django<3.0,>=2.2
 
pysaml2>=5.3.0
docker/Dockerfile
Show inline comments
 
FROM python:3.6-stretch as symposion_base
 
FROM python:3.8-buster as symposion_base
 

	
 
RUN set -ex \
 
    && apt-get update
 
RUN set -ex \
 
    && buildDeps=' \
 
        libffi-dev \
 
        libfreetype6-dev \
 
        libjpeg-dev \
 
        libwebp-dev \
 
        libpng-dev \
 
        liblcms2-dev \
 
        zlib1g-dev \
...
 
@@ -16,24 +16,25 @@ RUN set -ex \
 
        inkscape \
 
    ' \
 
    && apt-get install -y git xmlsec1 \
 
    && apt-get install -y $buildDeps --no-install-recommends \
 
    && rm -rf /var/lib/apt/lists/*
 

	
 
RUN set -ex \
 
    && pip install uwsgi
 

	
 
COPY constraints.txt requirements.txt /reqs/
 

	
 
RUN set -ex \
 
    && pip install -U pip \
 
    && pip install --no-cache-dir -r /reqs/requirements.txt -c /reqs/constraints.txt \
 
    && apt-get purge -y --auto-remove $buildDeps \
 
    && rm -rf /usr/src/python ~/.cache
 

	
 
COPY . /app/symposion_app
 

	
 
WORKDIR /app/symposion_app
 
RUN set -x \
 
    && pip install -r vendored_requirements.txt -c /reqs/constraints.txt
 
RUN set -x \
 
    && DJANGO_SECRET_KEY=1234 STRIPE_PUBLIC_KEY=1234 STRIPE_SECRET_KEY=1234 \
 
       DATABASE_URL="sqlite:////dev/null" python manage.py compilescss
docker/laptop-mode-env
Show inline comments
 
DJANGO_SECRET_KEY=5CEA51A5-A613-4AEF-A9FB-D0A57D77C13B
 
STRIPE_PUBLIC_KEY=5CEA51A5-A613-4AEF-A9FB-D0A57D77C13B
 
STRIPE_SECRET_KEY=5CEA51A5-A613-4AEF-A9FB-D0A57D77C13B
 
GCS_BUCKET=5CEA51A5-A613-4AEF-A9FB-D0A57D77C13B
 
GOOGLE_APPLICATION_CREDENTIALS=/dev/null
 
DATABASE_URL=sqlite:////tmp/symposion.sqlite
 
SYMPOSION_DEV_MODE=LAPTOP
 
SYMPOSION_APP_DEBUG=1
...
 
\ No newline at end of file
 
SYMPOSION_APP_DEBUG=1
pinaxcon/devmode_settings.py
Show inline comments
 
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 
AUTHENTICATION_BACKENDS = [
 
    'symposion.teams.backends.TeamPermissionsBackend',
 
    'django.contrib.auth.backends.ModelBackend',
 
]
 
LOGIN_URL='/accounts/login'
 

	
 
ROOT_URLCONF = "pinaxcon.devmode_urls"
 

	
 
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
 

	
 
INTERNAL_IPS = ['*']
pinaxcon/devmode_urls.py
Show inline comments
 
from django.conf.urls import include, url
 
from django.contrib.auth.views import login, logout
 
    
 
from django.contrib.auth.views import LoginView, LogoutView
 
from django.urls import include, path
 

	
 
from pinaxcon import urls
 

	
 
urlpatterns = [
 
    url(r'^accounts/logout', logout, {'template_name': 'admin/logout.html'}),
 
    url(r'^accounts/login', login, {'template_name': 'admin/login.html'}),
 
    path('accounts/logout', LogoutView.as_view(template_name='admin/logout.html')),
 
    path('accounts/login', LoginView.as_view(template_name='admin/login.html')),
 
]
 

	
 
urlpatterns += urls.urlpatterns
pinaxcon/monkey_patch.py
Show inline comments
 
from functools import wraps
 

	
 

	
 
class MonkeyPatchMiddleware(object):
 
    ''' Ensures that our monkey patching only gets called after it is safe to do so.'''
 

	
 
    def process_request(self, request):
 
    def __init__(self, get_response):
 
        self.get_response = get_response
 

	
 
    def __call__(self, request):
 
        do_monkey_patch()
 
        response = self.get_response(request)
 
        return response
 

	
 

	
 
def do_monkey_patch():
 
    patch_stripe_payment_form()
 

	
 
    # Remove this function from existence
 
    global do_monkey_patch
 
    do_monkey_patch = lambda: None  # noqa: E731
 

	
 

	
 
def patch_stripe_payment_form():  # noqa: C901
 

	
...
 
@@ -44,25 +49,25 @@ def patch_stripe_payment_form():  # noqa: C901
 
            k[initial] = {}
 
        initial = k[initial]
 

	
 
        # Find request context maybe?
 
        frame = inspect.currentframe()
 
        attendee_profile = None
 
        if frame:
 
            context = frame.f_back.f_locals
 
            for name, value in (context.items() or {}):
 
                if not isinstance(value, HttpRequest):
 
                    continue
 
                user = value.user
 
                if not user.is_authenticated():
 
                if not user.is_authenticated:
 
                    break
 
                try:
 
                    attendee_profile = models.AttendeeProfile.objects.get(
 
                        attendee__user=user
 
                    )
 
                except models.AttendeeProfile.DoesNotExist:
 
                    # Profile is still none.
 
                    pass
 
                break
 

	
 
        if attendee_profile:
 
            for us, stripe in mappings:
pinaxcon/raffle/models.py
Show inline comments
 
from django.conf import settings
 
from django.db import models
 

	
 
from pinaxcon.raffle.mixins import PrizeMixin, RaffleMixin
 

	
 

	
 
class Raffle(RaffleMixin, models.Model):
 
    """
 
    Stores a single Raffle object, related to one or many
 
    :model:`pinaxcon_registrasion.Product`, which  is usually a raffle ticket,
 
    but can be set to tickets or other products for door prizes.
 
    """
 
    description = models.CharField(max_length=255)
...
 
@@ -17,70 +18,100 @@ class Raffle(RaffleMixin, models.Model):
 
        return self.description
 

	
 

	
 
class Prize(PrizeMixin, models.Model):
 
    """
 
    Stores a Prize for a given :model:`pinaxcon_raffle.Raffle`.
 

	
 
    Once `winning_ticket` has been set to a :model:`pinaxcon_raffle.DrawnTicket`
 
    object, no further changes are permitted unless the object is explicitely
 
    unlocked.
 
    """
 
    description = models.CharField(max_length=255)
 
    raffle = models.ForeignKey('pinaxcon_raffle.Raffle', related_name='prizes')
 
    raffle = models.ForeignKey(
 
        'pinaxcon_raffle.Raffle',
 
        related_name='prizes',
 
        on_delete=models.CASCADE,
 
    )
 
    order = models.PositiveIntegerField()
 
    winning_ticket = models.OneToOneField(
 
        'pinaxcon_raffle.DrawnTicket', null=True,
 
        blank=True, related_name='+', on_delete=models.PROTECT
 
        'pinaxcon_raffle.DrawnTicket',
 
        null=True,
 
        blank=True,
 
        related_name='+',
 
        on_delete=models.PROTECT,
 
    )
 

	
 
    class Meta:
 
        unique_together = ('raffle', 'order')
 

	
 
    def __str__(self):
 
        return f"{self.order}. Prize: {self.description}"
 

	
 

	
 
class PrizeAudit(models.Model):
 
    """
 
    Stores an audit event for changes to a particular :model:`pinaxcon_raffle.Prize`.
 
    """
 
    reason = models.CharField(max_length=255)
 
    prize = models.ForeignKey('pinaxcon_raffle.Prize', related_name='audit_events')
 
    prize = models.ForeignKey(
 
        'pinaxcon_raffle.Prize',
 
        related_name='audit_events',
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    user = models.ForeignKey('auth.User')
 
    user = models.ForeignKey(
 
        settings.AUTH_USER_MODEL,
 
        on_delete=models.CASCADE,
 
    )
 
    timestamp = models.DateTimeField(auto_now_add=True)
 

	
 
    class Meta:
 
        ordering = ('-timestamp',)
 

	
 
    def __str__(self):
 
        return self.reason
 

	
 

	
 
class Draw(models.Model):
 
    """
 
    Stores a draw for a given :model:`pinaxcon_raffle.Raffle`, along with audit fields
 
    for the creating :model:`auth.User` and the creation timestamp.
 
    """
 
    raffle = models.ForeignKey('pinaxcon_raffle.Raffle', related_name='draws')
 
    drawn_by = models.ForeignKey('auth.User')
 
    raffle = models.ForeignKey(
 
        'pinaxcon_raffle.Raffle',
 
        related_name='draws',
 
        on_delete=models.CASCADE,
 
    )
 
    drawn_by = models.ForeignKey(
 
        settings.AUTH_USER_MODEL,
 
        on_delete=models.CASCADE,
 
    )
 
    drawn_time = models.DateTimeField(auto_now_add=True)
 

	
 
    def __str__(self):
 
        return f"{self.raffle}: {self.drawn_time}"
 

	
 

	
 
class DrawnTicket(models.Model):
 
    """
 
    Stores the result of a ticket draw, along with the corresponding
 
    :model:`pinaxcon_raffle.Draw`, :model:`pinaxcon_raffle.Prize` and the
 
    :model:`registrasion.commerce.LineItem` from which it was generated.
 
    """
 
    ticket = models.CharField(max_length=255)
 

	
 
    draw = models.ForeignKey('pinaxcon_raffle.Draw')
 
    prize = models.ForeignKey('pinaxcon_raffle.Prize')
 
    lineitem = models.ForeignKey('registrasion.LineItem')
 
    draw = models.ForeignKey(
 
        'pinaxcon_raffle.Draw',
 
        on_delete=models.CASCADE,
 
    )
 
    prize = models.ForeignKey(
 
        'pinaxcon_raffle.Prize',
 
        on_delete=models.CASCADE,
 
    )
 
    lineitem = models.ForeignKey(
 
        'registrasion.LineItem',
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    def __str__(self):
 
        return f"{self.ticket}: {self.draw.raffle}"
...
 
\ No newline at end of file
 
        return f"{self.ticket}: {self.draw.raffle}"
pinaxcon/registrasion/management/commands/dummy_presentations.py
Show inline comments
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.core.management.base import BaseCommand
 

	
 
from symposion.conference.models import Section, current_conference
 

	
 
from symposion.speakers.models import Speaker
 
from symposion.schedule.models import Presentation
 
from symposion.proposals.models import ProposalKind
 
from pinaxcon.proposals.models import TalkProposal
 

	
 
User = get_user_model()
 

	
 

	
 
class Command(BaseCommand):
 

	
 
    help = "Creates a bunch of dummy presentations to play around with."
 

	
 
    def handle(self, *args, **options):
 
        conf = current_conference()
 
        section = Section.objects.filter(conference=conf, slug="main").all().first()
 

	
 
        user = User.objects.first()
 
        speaker = Speaker.objects.first()
 
        if not speaker:
pinaxcon/settings.py
Show inline comments
 
from decimal import Decimal
 
import os
 
import sys
 

	
 
import django
 
import dj_database_url
 
import saml2
 
import saml2.saml
 

	
 
from datetime import date, datetime, timedelta
 
import pytz
 

	
 
from dataclasses import dataclass
 

	
 
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
 
PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__))
 
DJANGO_ROOT = os.path.abspath(os.path.dirname(django.__file__))
 
BASE_DIR = PACKAGE_ROOT
 
sys.path.append(os.path.join(PROJECT_ROOT, 'vendor'))
 

	
 

	
 
### USER SETTINGS
 
DEV_MODE = os.environ.get("SYMPOSION_DEV_MODE", None)
...
 
@@ -115,25 +116,25 @@ if DEV_MODE and DEV_MODE == "LAPTOP":
 
    }
 
else:
 
    CACHES = {
 
        'default': {
 
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
 
            'LOCATION': 'unique-snowflake',
 
        }
 
    }
 

	
 

	
 
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '*']
 

	
 
TIME_ZONE = "Australia/Brisbane"
 
TIME_ZONE = "Australia/Melbourne"
 
DATE_FORMAT = "j F Y"
 
LANGUAGE_CODE = "en-au"
 

	
 
SITE_ID = int(os.environ.get("SITE_ID", 1))
 
USE_I18N = True
 
USE_L10N = True
 
USE_TZ = True
 

	
 
MEDIA_ROOT = os.environ.get("MEDIA_ROOT", os.path.join(PACKAGE_ROOT, "site_media", "media"))
 
MEDIA_URL = "/site_media/media/"
 

	
 
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static', 'build')
...
 
@@ -167,72 +168,74 @@ TEMPLATES = [
 
                "django.template.context_processors.static",
 
                "django.template.context_processors.tz",
 
                "django.template.context_processors.request",
 
                "django.contrib.messages.context_processors.messages",
 
                "pinax_theme_bootstrap.context_processors.theme",
 
                "symposion.reviews.context_processors.reviews",
 
                "django_settings_export.settings_export",
 
            ],
 
        },
 
    },
 
]
 

	
 
MIDDLEWARE_CLASSES = [
 
MIDDLEWARE = [
 
    "whitenoise.middleware.WhiteNoiseMiddleware",
 
    "django.contrib.sessions.middleware.SessionMiddleware",
 
    "django.middleware.common.CommonMiddleware",
 
    "django.middleware.csrf.CsrfViewMiddleware",
 
    "django.contrib.auth.middleware.AuthenticationMiddleware",
 
    "django.contrib.auth.middleware.SessionAuthenticationMiddleware",
 
    "django.contrib.messages.middleware.MessageMiddleware",
 
    "debug_toolbar.middleware.DebugToolbarMiddleware",
 
    "reversion.middleware.RevisionMiddleware",
 
    "waffle.middleware.WaffleMiddleware",
 
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
 
    "django.contrib.flatpages.middleware.FlatpageFallbackMiddleware",
 
    'pinaxcon.monkey_patch.MonkeyPatchMiddleware',
 
]
 

	
 
if DEV_MODE and DEV_MODE == "LAPTOP":
 
    ROOT_URLCONF = "pinaxcon.devmode_urls"
 
else:
 
    ROOT_URLCONF = "pinaxcon.urls"
 

	
 
# Python dotted path to the WSGI application used by Django's runserver.
 
WSGI_APPLICATION = "pinaxcon.wsgi.application"
 

	
 
INSTALLED_APPS = [
 
    "whitenoise.runserver_nostatic",
 
    "django.contrib.admin",
 
    "django.contrib.auth",
 
    "django.contrib.contenttypes",
 
    "django.contrib.flatpages",
 
    "django.contrib.messages",
 
    "django.contrib.sessions",
 
    "django.contrib.sites",
 
    "django.contrib.staticfiles",
 
    "django.contrib.humanize",
 
    "debug_toolbar",
 

	
 
    'djangosaml2',
 

	
 
    # theme
 
    "bootstrapform",
 
    "pinax_theme_bootstrap",
 
    "sass_processor",
 
    "capture_tag",
 

	
 
    # external
 
    "easy_thumbnails",
 
    "taggit",
 
    "reversion",
 
    "sitetree",
 
    "django_jsonfield_backport",
 
    "pinax.eventlog",
 

	
 
    # symposion
 
    "symposion",
 
    "symposion.conference",
 
    "symposion.proposals",
 
    "symposion.reviews",
 
    "symposion.schedule",
 
    "symposion.speakers",
 
    "symposion.teams",
 

	
 
    # Registrasion
...
 
@@ -274,27 +277,30 @@ DEBUG_TOOLBAR_PANELS = [
 
    'debug_toolbar.panels.request.RequestPanel',
 
    'debug_toolbar.panels.sql.SQLPanel',
 
    'debug_toolbar.panels.staticfiles.StaticFilesPanel',
 
    'debug_toolbar.panels.cache.CachePanel',
 
    'debug_toolbar.panels.signals.SignalsPanel',
 
    'debug_toolbar.panels.logging.LoggingPanel',
 
    'debug_toolbar.panels.templates.TemplatesPanel',
 
    'debug_toolbar.panels.redirects.RedirectsPanel',
 
]
 

	
 
DEBUG_TOOLBAR_CONFIG = {
 
    'INTERCEPT_REDIRECTS': False,
 
    'SHOW_TOOLBAR_CALLBACK': lambda x: DEBUG,
 
}
 

	
 
INTERNAL_IPS = [
 
    '127.0.0.1',
 
]
 

	
 
from debug_toolbar.panels.logging import collector
 
LOGGING = {
 
    'version': 1,
 
    'disable_existing_loggers': False,
 
    'formatters': {
 
        'verbose': {
 
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
 
        },
 
        'simple': {
 
            'format': '%(asctime)s %(levelname)s $(module)s %(message)s'
 
        },
 
    },
...
 
@@ -493,27 +499,28 @@ class SpeakersDinnerCat(Category):
 
        cls.tickets.append(t)
 
        return t
 

	
 

	
 
class PenguinDinnerCat(Category):
 
    @classmethod
 
    def create(cls, name: str, price: Decimal, description: str, reservation: timedelta) -> PenguinDinnerTicket:
 
        t = PenguinDinnerTicket(name, price, description, reservation, cls)
 
        cls.tickets.append(t)
 
        return t
 

	
 

	
 
LCA_START = datetime(2021, 1, 23)
 
LCA_END = datetime(2021, 1, 25)
 
EARLY_BIRD_DEADLINE = datetime(2020, 12, 1)
 
_TZINFO = pytz.timezone(TIME_ZONE)
 
LCA_START = datetime(2021, 1, 23, tzinfo=_TZINFO)
 
LCA_END = datetime(2021, 1, 25, tzinfo=_TZINFO)
 
EARLY_BIRD_DEADLINE = datetime(2020, 12, 1, tzinfo=_TZINFO)
 
PENGUIN_DINNER_TICKET_DATE = date(2021, 1, 23)
 
SPEAKER_DINNER_TICKET_DATE = date(2021, 1, 25)
 
PDNS_TICKET_DATE = date(2021, 1, 24)
 

	
 
TSHIRT_PRICE = Decimal("25.00")
 

	
 
CONTRIBUTOR = Ticket("Contributor", Decimal("1999.00"), Decimal("1849.00"))
 
PROFESSIONAL = Ticket("Professional", Decimal("1099.00"), Decimal("949.00"))
 
HOBBYIST = Ticket("Hobbyist", Decimal("549.00"), Decimal("399.00"))
 
STUDENT = Ticket("Student", Decimal("199.00"), None)
 

	
 
MINICONF_MT = Ticket("Monday and Tuesday Only", Decimal("198.00"), None)
pinaxcon/templates/403_csrf.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% load i18n %}
 

	
 
{% block body_class %}template-blogpage{% endblock %}
 

	
 
{% block head_title %}{{ page.title }}{% endblock %}
 

	
 
{% block body %}
 
  {% block content %}
 
    <div class="l-content-page">
 
      <div class="l-content-page--richtext">
 
        <h2>{{ title }} <span>(403)</span></h2>
pinaxcon/templates/404.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% load i18n %}
 

	
 
{% block body_class %}template-blogpage{% endblock %}
 

	
 
{% block head_title %}{{ page.title }}{% endblock %}
 

	
 
{% block body %}
 
  {% block content %}
 
    <div class="l-content-page">
 
      <div class="l-content-page--richtext ooops-hack">
 
        <h2>Ooops</h2>
pinaxcon/templates/dashboard.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load i18n %}
 

	
 
{% load review_tags %}
 
{% load teams_tags %}
 
{% load registrasion_tags %}
 
{% load lca2018_tags %}
 
{% load staticfiles %}
 

	
 

	
 
{% block head_title %}Dashboard{% endblock %}
 
{% block page_title %}User Dashboard{% endblock %}
 

	
 
{% block alert %}
 
{% endblock %}
 

	
 
{% block content %}
 

	
 
{% available_categories as categories %}
 
{% if categories %}
pinaxcon/templates/raffle.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load registrasion_tags %}
 
{% load lca2018_tags %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% block head_title %}Raffle Tickets{% endblock %}
 
{% block page_title %}Raffle Tickets{% endblock %}
 

	
 
{% block content %}
 
<p>
 
  All of the raffles you are entered into and the tickets you have for each of them.
 
  If available, you will be able to purchase additional raffle tickets from the <a href="{% url "dashboard" %}">Dashboard</a>.
 
</p>
 

	
 
<h2>Your Raffle Tickets</h2>
 
{% for raffle in raffles %}
pinaxcon/templates/raffle_draw.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load registrasion_tags %}
 
{% load lca2018_tags %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% block head_title %}Raffle Winners{% endblock %}
 
{% block page_title %}Raffle Winners{% endblock %}
 

	
 
{% block content %}
 
{% for raffle in raffles %}
 
{% if raffle.hidden %}
 
{% else %}
 
<h2 class="mt-3"><a href="{% url "raffle-draw" raffle.id %}">{{ raffle }}</a></h2>
 

	
 
<dl class="row my-4">
 
  {% for prize in raffle.prizes.all %}
pinaxcon/templates/registrasion/base.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load lca2018_tags %}
 
{% load i18n %}
 

	
 
{% block head_title %}{% block page_title %}{% endblock %}{% endblock %}
 

	
 
{% block content_base %}
 
{% block content %}
 
  <div class="jumbotron rego-content">
 
    {% block proposals_body %}
 
    {% endblock %}
 
  </div>
 
{% endblock content %}
pinaxcon/templates/registrasion/invoice.html
Show inline comments
 
{% extends "registrasion/base.html" %}
 
{% load registrasion_tags %}
 
{% load lca2018_tags %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% block head_title %}Tax Invoice/Statement #{{ invoice.id }}{% endblock %}
 
{% block page_title %}{% conference_name %}{% endblock %}
 

	
 
{% block proposals_body %}
 
{% include "registrasion/_invoice_details.html" %}
 
<div class="d-print-none mb-4 pb-4">
 
  {% if invoice.is_unpaid %}
 
  <p>
 
    <strong>NOTICE:</strong> The above invoice is automatically generated, and
 
    will be voided if you amend your selections before payment, or if discounts
 
    or products contained in the invoice become unavailable. The products and
pinaxcon/templates/site_base.html
Show inline comments
 
{% load staticfiles %}
 
{% load static %}
 
{% load i18n %}
 
{% load sitetree %}
 
{% load sass_tags %}
 
{% load capture_tags %}
 

	
 
{% capture as head_title silent %}{% block head_title_base %}{% if SITE_NAME %}{{ SITE_NAME }} | {% endif %}{% block head_title %}{% endblock %}{% endblock %}{% endcapture %}
 

	
 
<!DOCTYPE html>
 

	
 
<html lang="en">
 
<head>
 
  <meta charset="utf-8">
pinaxcon/templates/symposion/dashboard/_categories.html
Show inline comments
 
{% load i18n %}
 
{% load proposal_tags %}
 
{% load review_tags %}
 
{% load teams_tags %}
 
{% load registrasion_tags %}
 
{% load lca2018_tags %}
 
{% load lca2019_tags %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load waffle_tags %}
 

	
 
{% if user.is_staff %}
 
<div class="mb-4">
 
  <div class="row">
 
    <div class="col-12">
 
      <h2>{% trans "Administration" %}</h2>
 
      <p>The following administrative tools are available to you:
 
        <ul class="list-unstyled">
 
          <li><a href="{% url "reports_list" %}">Reports</a></li>
 
        </ul>
 
      </p>
pinaxcon/templates/symposion/proposals/base.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 

	
 
{% block body_outer %}
 
  {% block proposals_body %}
 
  {% endblock %}
 
{% endblock %}
 

	
pinaxcon/templates/symposion/reviews/base.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% load i18n %}
 

	
 
{% block body_class %}reviews{% endblock %}
 
{% block main_class %}container-fluid{% endblock %}
 

	
 
{% block body_outer %}
 
<div class="container-fluid">
 
    <div class="row">
 
        <div class="col-md-2 col-sm-4">
 
            {% block sidebar %}
 
            {% for section in review_sections %}
pinaxcon/templates/symposion/reviews/review_list.html
Show inline comments
...
 
@@ -4,19 +4,21 @@
 

	
 
{% block body_class %}{{ block.super }}
 
	{% if reviewed == "all_reviews" %}
 
		review-list
 
	{% endif %}
 
{% endblock %}
 

	
 
{% block body %}
 
	<h3>{{ section }}</h3>
 
	{% if reviewed == 'all_reviews' %}
 
    	<h4>All proposals</h4>
 
    {% elif reviewed == 'user_reviewed' %}
 
    	<h4>Proposals you have reviewed</h4>
 
		<h4>Proposals you have reviewed</h4>
 
	{% elif reviewed == 'user_not_reviewed' %}
 
		<h4>Proposals you have not reviewed</h4>
 
    {% else %}
 
    	<h4>Proposals you have not yet reviewed</h4>
 
    	<h4>Proposals reviewed by selected reviewer</h4>
 
    {% endif %}
 

	
 
    {% include "symposion/reviews/_review_table.html" %}
 
{% endblock %}
pinaxcon/templates/symposion/reviews/review_review.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% load bootstrap %}
 

	
 
{% block body_class %}review{% endblock %}
 

	
 
{% block body %}
 
<div class="l-content-page">
 
<div class="l-content-page--richtext">
 
<div class="rich-text">
 
    <h1>Proposal Review</h1>
 

	
 
    <div class="proposal">
pinaxcon/templates/symposion/schedule/presentation_detail.html
Show inline comments
 
{% extends "site_base.html" %}
 

	
 
{% load lca2018_tags %}
 
{% load lca2019_tags %}
 
{% load sitetree %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load thumbnail %}
 

	
 
{% block head_title %}Presentation: {{ presentation.title }}{% endblock %}
 
{% block page_title %}{{ presentation.title }}{% endblock %}
 
{% block page_lead %}
 
{% if presentation.slot %}
 
{{ presentation.slot.rooms.0 }} | {{ presentation.slot.day.date|date:"D d M" }} | {{ presentation.slot.start }}&ndash;{{ presentation.slot.end }}
 
{% else %}
 
<em>Not currently scheduled.</em>
 
{% endif %}
 
{% endblock %}
 

	
pinaxcon/templates/symposion/schedule/session_detail.html
Show inline comments
 
{% extends "site_base.html" %}
 

	
 
{% load lca2018_tags %}
 
{% load sitetree %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load thumbnail %}
 
{% load i18n %}
 

	
 

	
 
{% block head_title %}Session: {{ session }}{% endblock %}
 

	
 
{% block breadcrumbs %}{% sitetree_breadcrumbs from "main" %}{% endblock %}
 

	
 

	
 
{% block header_title %}Session: {{ session }}{% endblock %}
 

	
 
{% block header_paragraph %}
pinaxcon/templates/symposion/schedule/session_list.html
Show inline comments
 
{% extends "site_base.html" %}
 

	
 
{% load lca2018_tags %}
 
{% load sitetree %}
 
{% load staticfiles %}
 
{% load static %}
 
{% load thumbnail %}
 
{% load i18n %}
 

	
 

	
 
{% block head_title %}Sessions{% endblock %}
 

	
 
{% block breadcrumbs %}{% sitetree_breadcrumbs from "main" %}{% endblock %}
 

	
 
{% block header_title %}Sessions{% endblock %}
 

	
 
{% block header_paragraph %}
 

	
pinaxcon/templatetags/lca2018_tags.py
Show inline comments
...
 
@@ -6,25 +6,25 @@ from django.conf import settings
 
from django.contrib.staticfiles.templatetags import staticfiles
 
from easy_thumbnails.files import get_thumbnailer
 
from registrasion.templatetags import registrasion_tags
 
from symposion.conference import models as conference_models
 
from symposion.schedule.models import Track
 

	
 
CONFERENCE_ID = settings.CONFERENCE_ID
 
GST_RATE = settings.GST_RATE
 

	
 
register = template.Library()
 

	
 

	
 
@register.assignment_tag()
 
@register.simple_tag()
 
def classname(ob):
 
    return ob.__class__.__name__
 

	
 

	
 
@register.simple_tag(takes_context=True)
 
def can_manage(context, proposal):
 
    return proposal_permission(context, "manage", proposal)
 

	
 

	
 
@register.simple_tag(takes_context=True)
 
def can_review(context, proposal):
 
    return proposal_permission(context, "review", proposal)
pinaxcon/urls.py
Show inline comments
 
import debug_toolbar
 
from django.conf import settings
 
from django.conf.urls import include, url
 
from django.conf.urls.static import static
 
from django.views.generic import RedirectView
 
from django.views.generic import TemplateView
 
from django.urls import include, path
 
from django.contrib.flatpages.views import flatpage
 

	
 
from django.contrib import admin
 

	
 
import symposion.views
 

	
 

	
 
urlpatterns = [
 
    url(r'^saml2/', include('djangosaml2.urls')),
 
    url(r"^admin/", include(admin.site.urls)),
 
    path('saml2/', include('djangosaml2.urls')),
 
    path('admin/', admin.site.urls),
 

	
 
    url(r"^speaker/", include("symposion.speakers.urls")),
 
    url(r"^proposals/", include("symposion.proposals.urls")),
 
    url(r"^reviews/", include("symposion.reviews.urls")),
 
    url(r"^schedule/", include("symposion.schedule.urls")),
 
    url(r"^conference/", include("symposion.conference.urls")),
 
    path("speaker/", include("symposion.speakers.urls")),
 
    path("proposals/", include("symposion.proposals.urls")),
 
    path("reviews/", include("symposion.reviews.urls")),
 
    path("schedule/", include("symposion.schedule.urls")),
 
    path("conference/", include("symposion.conference.urls")),
 

	
 
    url(r"^teams/", include("symposion.teams.urls")),
 
    url(r'^raffle/', include("pinaxcon.raffle.urls")),
 
    path("teams/", include("symposion.teams.urls")),
 
    path('raffle/', include("pinaxcon.raffle.urls")),
 

	
 
    # Required by registrasion
 
    url(r'^tickets/payments/', include('registripe.urls')),
 
    url(r'^tickets/', include('registrasion.urls')),
 
    url(r'^nested_admin/', include('nested_admin.urls')),
 
    url(r'^checkin/', include('regidesk.urls')),
 
    url(r'^pages/', include('django.contrib.flatpages.urls')),
 

	
 
    url(r'^dashboard/', symposion.views.dashboard, name="dashboard"),
 
    url(r'^boardingpass', RedirectView.as_view(pattern_name="regidesk:boardingpass")),
 
]
 
    path('tickets/payments/', include('registripe.urls')),
 
    path('tickets/', include('registrasion.urls')),
 
    path('nested_admin/', include('nested_admin.urls')),
 
    path('checkin/', include('regidesk.urls')),
 
    path('pages/', include('django.contrib.flatpages.urls')),
 

	
 
    path('dashboard/', symposion.views.dashboard, name="dashboard"),
 
    path('boardingpass', RedirectView.as_view(pattern_name="regidesk:boardingpass")),
 

	
 
if settings.DEBUG:
 
    import debug_toolbar
 
    urlpatterns.insert(0, url(r'^__debug__/', include(debug_toolbar.urls)))
 
    # Debug Toolbar. Always include to ensure tests work.
 
    path('__debug__/', include(debug_toolbar.urls)),
 
]
 

	
 
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)#
...
 
\ No newline at end of file
 
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
pinaxcon/wsgi.py
Show inline comments
 
import os
 

	
 
os.environ.setdefault("DEBUG", "0")
 
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pinaxcon.settings")
 

	
 
from django.core.wsgi import get_wsgi_application  # noqa
 

	
 
from dj_static import Cling, MediaCling  # noqa
 

	
 
application = Cling(MediaCling(get_wsgi_application()))
 
application = get_wsgi_application()
requirements.txt
Show inline comments
 
asn1crypto==0.24.0
 
bleach==2.1.3
 
cachetools==2.1.0
 
cairocffi==0.8.1
 
CairoSVG==2.1.2
 
certifi==2018.4.16
 
cffi==1.11.5
 
chardet==3.0.4
 
coverage==4.0.3
 
cryptography==2.3
 
cssselect2==0.2.1
 
dataclasses==0.6
 
decorator==4.3.0
 
defusedxml==0.5.0
 
dj-database-url==0.4.2
 
dj-static==0.0.6
 
Django==1.11.25
 
django-appconf==1.0.1
 
Django>=2.2
 
pinax-theme-bootstrap==8.0.1
 
pinax-eventlog[django-lts]==5.1.0
 
django-formset-js==0.5.0
 
whitenoise==5.2.0
 
dj-database-url==0.5.0
 
pylibmc==1.6.1
 
django-debug-toolbar==3.1.1
 
django-bootstrap-form==3.4
 
django-settings-export~=1.2.1
 
django-capture-tag==1.0
 
django-compressor==2.3
 
django-countries==5.3.1
 
django-crispy-forms==1.7.2
 
django-debug-toolbar==1.9.1
 
django-formset-js==0.5.0
 
django-gapc-storage==0.5.1
 
django-ical==1.4
 
django-jquery-js==3.1.1
 
django-model-utils==3.1.2
 
django-nested-admin==2.2.6
 
django-nose==1.4.5
 
django-reversion==1.10.1
 
django-sass-processor==0.7.3
 
django-settings-export==1.2.1
 
django-sitetree==1.10.0
 
django-taggit==0.18.0
 
django-timezone-field==2.1
 
django-waffle==0.14.0
 
djangosaml2==0.17.2
 
easy-thumbnails==2.5
 
future==0.16.0
 
google-api-python-client==1.7.0
 
google-auth==1.5.1
 
google-auth-httplib2==0.0.3
 
html5lib==1.0.1
 
httplib2==0.11.3
 
icalendar==4.0.2
 
idna==2.7
 
jsonfield==2.0.2
 
libsass==0.19.3
 
lxml==4.0.0
 
mysqlclient==1.3.13
 
nose==1.3.7
 
oauth2client==4.1.2
 
Paste==2.0.3
 
Pillow==5.2.0
 
pinax-eventlog==1.1.1
 
pinax-stripe==3.2.1
 
pinax-theme-bootstrap==7.10.2
 
pyasn1==0.4.4
 
pyasn1-modules==0.2.2
 
pycparser==2.18
 
pycryptodomex==3.6.4
 
pylibmc==1.5.1
 
pyOpenSSL==18.0.0
 
pypng==0.0.18
 
PyQRCode==1.2.1
 
pysaml2==4.8.0
 
python-dateutil==2.7.3
 
pytz==2018.4
 
rcssmin==1.0.6
 
repoze.who==2.3
 
requests==2.19.1
 
rjsmin==1.1.0
 
rsa==3.4.2
 
six==1.11.0
 
sqlparse==0.2.4
 
static3==0.7.0
 
stripe==1.38.0
 
tinycss2==0.6.1
 
uritemplate==3.0.0
 
urllib3==1.23
 
uWSGI==2.0.17.1
 
webencodings==0.5.1
 
WebOb==1.8.2
 
zope.interface==4.5.0
 
djangosaml2==0.50.0
 
django-gapc-storage==0.5.2
 
django-waffle==2.0.0
 

	
 
# database
 
mysqlclient==2.0.1
 

	
 
# For testing
 
django-nose==1.4.7
 
coverage==5.3
 
factory_boy==3.1.0
 

	
 
# Symposion reqs
 
django-appconf==1.0.4
 
django-model-utils==4.0.0
 
django-reversion==3.0.8
 
django-sitetree==1.16.0
 
django-taggit==1.3.0
 
django-timezone-field==4.0
 
easy-thumbnails==2.7.0
 
bleach==3.2.1
 
pytz>=2020.1
 
django-ical==1.7.1
 

	
 
# Registrasion reqs
 
django-nested-admin==3.3.2
 
CairoSVG==2.4.2
 

	
 
# Registripe
 
django-countries>=6.1.3
 
pinax-stripe==4.4.0
 
requests==2.24.0
 
stripe==2.55.0
 

	
 
# SASS Compiler and template tags
 
libsass==0.20.1
 
django-sass-processor==0.8.2
 
django-compressor==2.4
 

	
 
django-crispy-forms==1.9.2
vendor/regidesk/regidesk/forms.py
Show inline comments
 
import copy
 
from regidesk import models
 

	
 
from django import forms
 
from django.core.urlresolvers import reverse
 
import functools
 

	
 
from django import forms
 
from django.core.urlresolvers import reverse
 
from django.core.exceptions import ValidationError
 
from django.db.models import F, Q
 
from django.forms import widgets
 
from django.urls import reverse
 
from django.utils import timezone
 

	
 
from django_countries import countries
 
from django_countries.fields import LazyTypedChoiceField
 
from django_countries.widgets import CountrySelectWidget
 

	
vendor/regidesk/regidesk/management/commands/print_badges.py
Show inline comments
 
#!/usr/bin/env python
 

	
 
from django.core.management.base import BaseCommand, CommandError
 

	
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from registrasion.views import _convert_img as convert_img
 
from registrasion.views import render_badge_svg
 

	
 
User = get_user_model()
 

	
 

	
 
class Command(BaseCommand):
 

	
 
    def handle(self, *args, **options):
 

	
 
        users = User.objects.filter(
 
            checkin__checked_in_bool=True).filter(
 
            checkin__badge_printed=False
 
        )
 

	
 
        for user in users:
 

	
 
            try:
vendor/regidesk/regidesk/models.py
Show inline comments
...
 
@@ -3,32 +3,34 @@ import base64
 
from datetime import datetime
 
from decimal import Decimal
 
from io import BytesIO
 

	
 
from django.core.exceptions import ValidationError
 
from django.utils import timezone
 

	
 
from django.db import models
 
from django.db.models import Q, F
 
from django.db.models import Case, When, Value
 
from django.db.models import Count
 
from django.db.models.signals import post_save
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
import pyqrcode
 

	
 
from symposion import constants
 
from symposion.text_parser import parse
 
from registrasion.models import commerce
 
from registrasion.util import generate_access_code as generate_code
 

	
 
User = get_user_model()
 

	
 

	
 
class BoardingPassTemplate(models.Model):
 

	
 
    label = models.CharField(max_length=100, verbose_name="Label")
 
    from_address = models.EmailField(verbose_name="From address")
 
    subject = models.CharField(max_length=100, verbose_name="Subject")
 
    body = models.TextField(verbose_name="Body")
 
    html_body = models.TextField(verbose_name="HTML Body",null=True)
 

	
 
    class Meta:
 
        verbose_name = ("Boarding Pass template")
 
        verbose_name_plural = ("Boarding Pass templates")
...
 
@@ -51,25 +53,25 @@ class BoardingPass(models.Model):
 
            ("send_boarding_pass", "Can send boarding passes"),
 
        )
 

	
 
    def __unicode__(self):
 
        return self.checkin.attendee.attendeeprofilebase.attendeeprofile.name + ' ' + self.timestamp.strftime('%Y-%m-%d %H:%M:%S')
 

	
 
    @property
 
    def email_args(self):
 
        return (self.subject, self.body, self.from_address, self.user.email)
 

	
 
class CheckIn(models.Model):
 

	
 
    user = models.OneToOneField(User)
 
    user = models.OneToOneField(User, on_delete=models.CASCADE)
 
    boardingpass = models.OneToOneField(BoardingPass, null=True,
 
                                        blank=True, on_delete=models.SET_NULL)
 
    seen = models.DateTimeField(null=True,blank=True)
 
    checked_in = models.DateTimeField(null=True,blank=True)
 
    checkin_code = models.CharField(
 
        max_length=6,
 
        unique=True,
 
        db_index=True,
 
    )
 
    _checkin_code_png=models.TextField(max_length=512,null=True,blank=True)
 
    badge_printed = models.BooleanField(default=False)
 
    schwag_given = models.BooleanField(default=False)
vendor/regidesk/regidesk/templates/regidesk/base.html
Show inline comments
 
{% extends "site_base.html" %}
 
{% load staticfiles %}
 
{% load static %}
 

	
 
{% load i18n %}
 

	
 
{% block head_title %}Checkin{% endblock head_title %}
 

	
 
{% block content %}
 
{% block body %}
 
{% endblock %}
 
{% endblock %}
 

	
 
{% block extra_script %}
 
<link rel="stylesheet" type="text/css" href="//cdn.datatables.net/1.10.19/css/jquery.dataTables.min.css" />
...
 
@@ -30,13 +30,13 @@
 
    {
 
      extend: 'collection',
 
      text: 'Columns',
 
      buttons: [
 
        {
 
          extend: 'columnsToggle',
 
          columns: '.toggle'
 
        },
 
      ]
 
    }]
 
  });
 
</script>
 
{% endblock %}
...
 
\ No newline at end of file
 
{% endblock %}
vendor/regidesk/regidesk/views.py
Show inline comments
 
import base64
 
import logging
 
from datetime import datetime
 
from email.mime.image import MIMEImage
 

	
 
from django.core.exceptions import ValidationError
 
from django.core.mail import EmailMultiAlternatives
 
from django.conf import settings
 
from django.contrib import messages
 
from django.contrib.auth.decorators import permission_required, user_passes_test, login_required
 
from django.contrib.auth.mixins import PermissionRequiredMixin
 
from django.contrib.auth.models import User, Group
 
from django.contrib.auth.models import Group
 
from django.contrib.auth import get_user_model
 
from django.contrib.sites.models import Site
 
from django.db import transaction
 
from django.db.models import F, Q
 
from django.db.models import Count, Max, Sum
 
from django.http import Http404
 
from django.http import HttpResponse, HttpResponseBadRequest
 
from django.shortcuts import redirect, render
 
from django.template import Template, Context
 
from django.urls import reverse
 
from django.views.decorators.csrf import csrf_exempt
 
from django.views.generic import TemplateView
 

	
 
from registrasion import util
 
from registrasion.views import render_badge
 
from registrasion.models import commerce, people
 
from registrasion.templatetags.registrasion_tags import items_purchased, items_pending
 
from registrasion.templatetags.registrasion_tags import invoices, missing_categories
 
from symposion.conference.models import Conference
 

	
 
from regidesk import forms
 
from regidesk.models import BoardingPass, BoardingPassTemplate, CheckIn
 

	
 

	
 

	
 
User = get_user_model()
 
AttendeeProfile = util.get_object_from_name(settings.ATTENDEE_PROFILE_MODEL)
 

	
 

	
 
def _staff_only(user):
 
    ''' Returns true if the user is staff. '''
 
    return user.is_staff
 

	
 
@login_required
 
def boardingpass(request):
 

	
 
    user=request.user
 
    checkin = CheckIn.objects.get_or_create(user=user)[0]
 
    if not checkin.boardingpass:
 
        templates = BoardingPassTemplate.objects.all()
 
        if not templates:
vendor/regidesk/requirements.txt
Show inline comments
 
django-countries>=4.0
 
requests>=2.11.1
 
django-countries>=6.1.3
 
requests>=2.24.0
 
pypng
 
pyqrcode
vendor/registrasion/registrasion/admin.py
Show inline comments
...
 
@@ -35,30 +35,32 @@ class CategoryAdmin(admin.ModelAdmin):
 
class ProductAdmin(admin.ModelAdmin):
 
    model = inventory.Product
 
    list_display = ("name", "category", "description")
 
    list_filter = ("category", )
 

	
 

	
 
# Discounts
 

	
 
class DiscountForProductInline(admin.TabularInline):
 
    model = conditions.DiscountForProduct
 
    verbose_name = _("Product included in discount")
 
    verbose_name_plural = _("Products included in discount")
 
    sortable_options = []
 

	
 

	
 
class DiscountForCategoryInline(admin.TabularInline):
 
    model = conditions.DiscountForCategory
 
    verbose_name = _("Category included in discount")
 
    verbose_name_plural = _("Categories included in discount")
 
    sortable_options = []
 

	
 

	
 
@admin.register(conditions.TimeOrStockLimitDiscount)
 
class TimeOrStockLimitDiscountAdmin(admin.ModelAdmin, EffectsDisplayMixin):
 
    list_display = (
 
        "description",
 
        "start_time",
 
        "end_time",
 
        "limit",
 
        "effects",
 
    )
 
    ordering = ("start_time", "end_time", "limit")
...
 
@@ -128,25 +130,25 @@ class VoucherDiscountInline(nested_admin.NestedStackedInline):
 

	
 
class VoucherFlagInline(nested_admin.NestedStackedInline):
 
    model = conditions.VoucherFlag
 
    verbose_name = _("Product and category enabled by voucher")
 
    verbose_name_plural = _("Products and categories enabled by voucher")
 

	
 
    # TODO work out why we're allowed to add more than one?
 
    max_num = 1
 
    extra = 1
 

	
 

	
 
@admin.register(inventory.Voucher)
 
class VoucherAdmin(nested_admin.NestedAdmin):
 
class VoucherAdmin(nested_admin.NestedModelAdmin):
 

	
 
    def effects(self, obj):
 
        ''' List the effects of the voucher in the admin. '''
 
        out = []
 

	
 
        try:
 
            voucher_condition = obj.voucherflag.condition
 
        except ObjectDoesNotExist:
 
            voucher_condition = None
 

	
 
        try:
 
            discount_effects = obj.voucherdiscount.effects()
...
 
@@ -169,53 +171,53 @@ class VoucherAdmin(nested_admin.NestedAdmin):
 

	
 
    model = inventory.Voucher
 
    list_display = ("recipient", "code", "effects")
 
    inlines = [
 
        VoucherDiscountInline,
 
        VoucherFlagInline,
 
    ]
 

	
 

	
 
# Enabling conditions
 
@admin.register(conditions.ProductFlag)
 
class ProductFlagAdmin(
 
        nested_admin.NestedAdmin,
 
        nested_admin.NestedModelAdmin,
 
        EffectsDisplayMixin):
 

	
 
    def enablers(self, obj):
 
        return list(obj.enabling_products.all())
 

	
 
    model = conditions.ProductFlag
 
    fields = ("description", "enabling_products", "condition", "products",
 
              "categories"),
 

	
 
    list_display = ("description", "condition", "enablers", "effects")
 

	
 

	
 
# Enabling conditions
 
@admin.register(conditions.CategoryFlag)
 
class CategoryFlagAdmin(
 
        nested_admin.NestedAdmin,
 
        nested_admin.NestedModelAdmin,
 
        EffectsDisplayMixin):
 

	
 
    model = conditions.CategoryFlag
 
    fields = ("description", "enabling_category", "condition", "products",
 
              "categories"),
 

	
 
    list_display = ("description", "condition", "enabling_category", "effects")
 
    ordering = ("enabling_category",)
 

	
 

	
 
@admin.register(conditions.SpeakerFlag)
 
class SpeakerFlagAdmin(nested_admin.NestedAdmin, EffectsDisplayMixin):
 
class SpeakerFlagAdmin(nested_admin.NestedModelAdmin, EffectsDisplayMixin):
 

	
 
    model = conditions.SpeakerFlag
 
    fields = ("description", "is_presenter", "is_copresenter", "proposal_kind",
 
              "products", "categories")
 

	
 
    list_display = ("description", "is_presenter", "is_copresenter", "effects")
 

	
 
    ordering = ("-is_presenter", "-is_copresenter")
 

	
 

	
 
@admin.register(conditions.GroupMemberFlag)
 
class GroupMemberFlagAdmin(admin.ModelAdmin, EffectsDisplayMixin):
vendor/registrasion/registrasion/contrib/badger.py
Show inline comments
...
 
@@ -15,35 +15,38 @@ management command, and by the tickets/badger and tickets/badge API functions.
 
import sys
 
import os
 
import csv
 
from lxml import etree
 
import tempfile
 
from copy import deepcopy
 
import subprocess
 

	
 
import pdb
 

	
 
from django.core.management.base import BaseCommand
 

	
 
from django.contrib.auth.models import User, Group
 
from django.contrib.auth.models import Group
 
from django.contrib.auth import get_user_model
 
from django.db.utils import OperationalError, ProgrammingError
 
from pinaxcon.registrasion.models import AttendeeProfile
 
from registrasion.controllers.cart import CartController
 
from registrasion.controllers.invoice import InvoiceController
 
from registrasion.models import Voucher
 
from registrasion.models import Attendee
 
from registrasion.models import Product
 
from registrasion.models import Invoice
 
from symposion.speakers.models import Speaker
 

	
 
User = get_user_model()
 

	
 
# A few unicode encodings ...
 
GLYPH_PLUS = '+'
 
GLYPH_GLASS = u'\ue001'
 
GLYPH_DINNER = u'\ue179'
 
GLYPH_SPEAKER = u'\ue122'
 
GLYPH_SPRINTS = u'\ue254'
 
GLYPH_CROWN = u'\ue211'
 
GLYPH_SNOWMAN = u'\u2603'
 
GLYPH_STAR = u'\ue007'
 
GLYPH_FLASH = u'\ue162'
 
GLYPH_EDU = u'\ue233'
 

	
vendor/registrasion/registrasion/controllers/batch.py
Show inline comments
 
import contextlib
 
import functools
 

	
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 

	
 
User = get_user_model()
 

	
 

	
 
class BatchController(object):
 
    ''' Batches are sets of operations where certain queries for users may be
 
    repeated, but are also unlikely change within the boundaries of the batch.
 

	
 
    Batches are keyed per-user. You can mark the edge of the batch with the
 
    ``batch`` context manager. If you nest calls to ``batch``, only the
 
    outermost call will have the effect of ending the batch.
 

	
 
    Batches store results for functions wrapped with ``memoise``. These results
 
    for the user are flushed at the end of the batch.
vendor/registrasion/registrasion/controllers/item.py
Show inline comments
 
''' NEEDS TESTS '''
 

	
 
import operator
 
from functools import reduce
 

	
 
from registrasion.models import commerce
 
from registrasion.models import inventory
 

	
 
from collections import Iterable
 
from collections.abc import Iterable
 
from collections import namedtuple
 
from django.db.models import Case
 
from django.db.models import Q
 
from django.db.models import Sum
 
from django.db.models import When
 
from django.db.models import Value
 

	
 
_ProductAndQuantity = namedtuple("ProductAndQuantity", ["product", "quantity"])
 

	
 

	
 
class ProductAndQuantity(_ProductAndQuantity):
 
    ''' Class that holds a product and a quantity.
vendor/registrasion/registrasion/models/commerce.py
Show inline comments
 
from . import conditions
 
from . import inventory
 

	
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.core.exceptions import ValidationError
 
from django.db import models
 
from django.db.models import F, Q, Sum
 
from django.utils import timezone
 
from django.utils.encoding import python_2_unicode_compatible
 
from django.utils.translation import ugettext_lazy as _
 
from model_utils.managers import InheritanceManager
 

	
 
User = get_user_model()
 

	
 

	
 
# Commerce Models
 

	
 
@python_2_unicode_compatible
 
class Cart(models.Model):
 
    ''' Represents a set of product items that have been purchased, or are
 
    pending purchase. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        index_together = [
 
            ("status", "time_last_updated"),
...
 
@@ -29,25 +31,25 @@ class Cart(models.Model):
 
        return "%d rev #%d" % (self.id, self.revision)
 

	
 
    STATUS_ACTIVE = 1
 
    STATUS_PAID = 2
 
    STATUS_RELEASED = 3
 

	
 
    STATUS_TYPES = [
 
        (STATUS_ACTIVE, _("Active")),
 
        (STATUS_PAID, _("Paid")),
 
        (STATUS_RELEASED, _("Released")),
 
    ]
 

	
 
    user = models.ForeignKey(User)
 
    user = models.ForeignKey(User, on_delete=models.CASCADE)
 
    # ProductItems (foreign key)
 
    vouchers = models.ManyToManyField(inventory.Voucher, blank=True)
 
    time_last_updated = models.DateTimeField(
 
        db_index=True,
 
    )
 
    reservation_duration = models.DurationField()
 
    revision = models.PositiveIntegerField(default=1)
 
    status = models.IntegerField(
 
        choices=STATUS_TYPES,
 
        db_index=True,
 
        default=STATUS_ACTIVE,
 
    )
...
 
@@ -67,44 +69,45 @@ class Cart(models.Model):
 
@python_2_unicode_compatible
 
class ProductItem(models.Model):
 
    ''' Represents a product-quantity pair in a Cart. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        ordering = ("product", )
 

	
 
    def __str__(self):
 
        return "product: %s * %d in Cart: %s" % (
 
            self.product, self.quantity, self.cart)
 

	
 
    cart = models.ForeignKey(Cart)
 
    product = models.ForeignKey(inventory.Product)
 
    cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
 
    product = models.ForeignKey(inventory.Product, on_delete=models.CASCADE)
 
    quantity = models.PositiveIntegerField(db_index=True)
 

	
 

	
 
@python_2_unicode_compatible
 
class DiscountItem(models.Model):
 
    ''' Represents a discount-product-quantity relation in a Cart. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        ordering = ("product", )
 

	
 
    def __str__(self):
 
        return "%s: %s * %d in Cart: %s" % (
 
            self.discount, self.product, self.quantity, self.cart)
 

	
 
    cart = models.ForeignKey(Cart)
 
    product = models.ForeignKey(inventory.Product)
 
    discount = models.ForeignKey(conditions.DiscountBase)
 
    cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
 
    product = models.ForeignKey(inventory.Product, on_delete=models.CASCADE)
 
    discount = models.ForeignKey(conditions.DiscountBase,
 
            on_delete=models.CASCADE)
 
    quantity = models.PositiveIntegerField()
 

	
 

	
 
@python_2_unicode_compatible
 
class Invoice(models.Model):
 
    ''' An invoice. Invoices can be automatically generated when checking out
 
    a Cart, in which case, it is attached to a given revision of a Cart.
 

	
 
    Attributes:
 

	
 
        user (User): The owner of this invoice.
 

	
...
 
@@ -180,26 +183,26 @@ class Invoice(models.Model):
 
    def total_payments(self):
 
        ''' Returns the total amount paid towards this invoice. '''
 

	
 
        payments = PaymentBase.objects.filter(invoice=self)
 
        total_paid = payments.aggregate(Sum("amount"))["amount__sum"] or 0
 
        return total_paid
 

	
 
    def balance_due(self):
 
        ''' Returns the total balance remaining towards this invoice. '''
 
        return self.value - self.total_payments()
 

	
 
    # Invoice Number
 
    user = models.ForeignKey(User)
 
    cart = models.ForeignKey(Cart, null=True)
 
    user = models.ForeignKey(User, on_delete=models.CASCADE)
 
    cart = models.ForeignKey(Cart, null=True, on_delete=models.CASCADE)
 
    cart_revision = models.IntegerField(
 
        null=True,
 
        db_index=True,
 
    )
 
    # Line Items (foreign key)
 
    status = models.IntegerField(
 
        choices=STATUS_TYPES,
 
        db_index=True,
 
    )
 
    recipient = models.CharField(max_length=1024)
 
    issue_time = models.DateTimeField()
 
    due_time = models.DateTimeField()
...
 
@@ -233,29 +236,30 @@ class LineItem(models.Model):
 
        app_label = "registrasion"
 
        ordering = ("id", )
 

	
 
    def __str__(self):
 
        return "Line: %s * %d @ %s" % (
 
            self.description, self.quantity, self.price)
 

	
 
    @property
 
    def total_price(self):
 
        ''' price * quantity '''
 
        return self.price * self.quantity
 

	
 
    invoice = models.ForeignKey(Invoice)
 
    invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE)
 
    description = models.CharField(max_length=255)
 
    quantity = models.PositiveIntegerField()
 
    price = models.DecimalField(max_digits=8, decimal_places=2)
 
    product = models.ForeignKey(inventory.Product, null=True, blank=True)
 
    product = models.ForeignKey(inventory.Product, null=True, blank=True,
 
            on_delete=models.CASCADE)
 

	
 

	
 
@python_2_unicode_compatible
 
class PaymentBase(models.Model):
 
    ''' The base payment type for invoices. Payment apps should subclass this
 
    class to handle implementation-specific issues.
 

	
 
    Attributes:
 
        invoice (commerce.Invoice): The invoice that this payment applies to.
 

	
 
        time (datetime): The time that this payment was generated. Note that
 
            this will default to the current time when the model is created.
...
 
@@ -266,37 +270,37 @@ class PaymentBase(models.Model):
 
        amount (Decimal): The amount the payment is for.
 

	
 
    '''
 

	
 
    class Meta:
 
        ordering = ("time", )
 

	
 
    objects = InheritanceManager()
 

	
 
    def __str__(self):
 
        return "Payment: ref=%s amount=%s" % (self.reference, self.amount)
 

	
 
    invoice = models.ForeignKey(Invoice)
 
    invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE)
 
    time = models.DateTimeField(default=timezone.now)
 
    reference = models.CharField(max_length=255)
 
    amount = models.DecimalField(max_digits=8, decimal_places=2)
 

	
 

	
 
class ManualPayment(PaymentBase):
 
    ''' Payments that are manually entered by staff. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    entered_by = models.ForeignKey(User)
 
    entered_by = models.ForeignKey(User, on_delete=models.CASCADE)
 

	
 

	
 
class CreditNote(PaymentBase):
 
    ''' Credit notes represent money accounted for in the system that do not
 
    belong to specific invoices. They may be paid into other invoices, or
 
    cashed out as refunds.
 

	
 
    Each CreditNote may either be used to pay towards another Invoice in the
 
    system (by attaching a CreditNoteApplication), or may be marked as
 
    refunded (by attaching a CreditNoteRefund).'''
 

	
 
    class Meta:
...
 
@@ -355,25 +359,25 @@ class CreditNoteApplication(CleanOnSave, PaymentBase):
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    def clean(self):
 
        if not hasattr(self, "parent"):
 
            return
 
        if hasattr(self.parent, 'creditnoterefund'):
 
            raise ValidationError(
 
                "Cannot apply a refunded credit note to an invoice"
 
            )
 

	
 
    parent = models.OneToOneField(CreditNote)
 
    parent = models.OneToOneField(CreditNote, on_delete=models.CASCADE)
 

	
 

	
 
class CreditNoteRefund(CleanOnSave, models.Model):
 
    ''' Represents a refund of a credit note to an external payment.
 
    Credit notes may only be refunded in full. How those refunds are handled
 
    is left as an exercise to the payment app.
 

	
 
    Attributes:
 
        parent (commerce.CreditNote): The CreditNote that this refund
 
            corresponds to.
 

	
 
        time (datetime): The time that this refund was generated.
...
 
@@ -382,24 +386,24 @@ class CreditNoteRefund(CleanOnSave, models.Model):
 
            allow the user to identify the refund in their records.
 

	
 
    '''
 

	
 
    def clean(self):
 
        if not hasattr(self, "parent"):
 
            return
 
        if hasattr(self.parent, 'creditnoteapplication'):
 
            raise ValidationError(
 
                "Cannot refund a credit note that has been paid to an invoice"
 
            )
 

	
 
    parent = models.OneToOneField(CreditNote)
 
    parent = models.OneToOneField(CreditNote, on_delete=models.CASCADE)
 
    time = models.DateTimeField(default=timezone.now)
 
    reference = models.CharField(max_length=255)
 

	
 

	
 
class ManualCreditNoteRefund(CreditNoteRefund):
 
    ''' Credit notes that are entered by a staff member. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    entered_by = models.ForeignKey(User)
 
    entered_by = models.ForeignKey(User, on_delete=models.CASCADE)
vendor/registrasion/registrasion/models/conditions.py
Show inline comments
 
import itertools
 

	
 
from . import inventory
 

	
 
from django.contrib.auth.models import Group
 
from django.core.exceptions import ValidationError
 
from django.db import models
 
from django.db.models import F, Q
 
from django.utils.encoding import python_2_unicode_compatible
 
from django.utils.translation import ugettext_lazy as _
 
from model_utils.managers import InheritanceManager
 

	
 
from symposion import proposals
 

	
 

	
 
# Condition Types
 

	
 
class TimeOrStockLimitCondition(models.Model):
 
    ''' Attributes for a condition that is limited by timespan or a count of
 
    purchased or reserved items.
 

	
 
    Attributes:
 
        start_time (Optional[datetime]): When the condition should start being
 
            true.
 

	
 
        end_time (Optional[datetime]): When the condition should stop being
...
 
@@ -90,25 +88,25 @@ class SpeakerCondition(models.Model):
 

	
 
    is_presenter = models.BooleanField(
 
        blank=True,
 
        help_text=_("This condition is met if the user is the primary "
 
                    "presenter of a presentation."),
 
    )
 
    is_copresenter = models.BooleanField(
 
        blank=True,
 
        help_text=_("This condition is met if the user is a copresenter of a "
 
                    "presentation."),
 
    )
 
    proposal_kind = models.ManyToManyField(
 
        proposals.models.ProposalKind,
 
        "symposion_proposals.ProposalKind",
 
        help_text=_("The types of proposals that these users may be "
 
                    "presenters of."),
 
    )
 

	
 

	
 
class GroupMemberCondition(models.Model):
 
    ''' Conditions that are met if a user is a member (not declined or
 
    rejected) of a specific django auth group. '''
 

	
 
    class Meta:
 
        abstract = True
 

	
...
 
@@ -511,24 +509,25 @@ class CategoryFlag(FlagBase):
 
    class Meta:
 
        app_label = "registrasion"
 
        verbose_name = _("flag (dependency on product from category)")
 
        verbose_name_plural = _("flags (dependency on product from category)")
 

	
 
    def __str__(self):
 
        return "Enabled by product in category: " + str(self.enabling_category)
 

	
 
    enabling_category = models.ForeignKey(
 
        inventory.Category,
 
        help_text=_("If a product from this category is purchased, this "
 
                    "condition is met."),
 
        on_delete=models.CASCADE,
 
    )
 

	
 

	
 
@python_2_unicode_compatible
 
class VoucherFlag(VoucherCondition, FlagBase):
 
    ''' The condition is met because a Voucher is present. This is for e.g.
 
    enabling sponsor tickets. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 
        verbose_name = _("flag (dependency on voucher)")
 
        verbose_name_plural = _("flags (dependency on voucher)")
vendor/registrasion/registrasion/models/inventory.py
Show inline comments
...
 
@@ -147,25 +147,26 @@ class Product(models.Model):
 

	
 
    name = models.CharField(
 
        max_length=255,
 
        verbose_name=_("Name"),
 
    )
 
    description = models.TextField(
 
        verbose_name=_("Description"),
 
        null=True,
 
        blank=True,
 
    )
 
    category = models.ForeignKey(
 
        Category,
 
        verbose_name=_("Product category")
 
        verbose_name=_("Product category"),
 
        on_delete=models.CASCADE,
 
    )
 
    price = models.DecimalField(
 
        max_digits=8,
 
        decimal_places=2,
 
        verbose_name=_("Price"),
 
    )
 
    limit_per_user = models.PositiveIntegerField(
 
        null=True,
 
        blank=True,
 
        verbose_name=_("Limit per user"),
 
    )
 
    reservation_duration = models.DurationField(
vendor/registrasion/registrasion/models/people.py
Show inline comments
 
from registrasion import util
 

	
 
from django.conf import settings
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.db import models
 
from django.utils.encoding import python_2_unicode_compatible
 
from model_utils.managers import InheritanceManager
 

	
 
from registrasion.models.commerce import Invoice, ProductItem
 

	
 
User = get_user_model()
 

	
 

	
 
# User models
 

	
 
@python_2_unicode_compatible
 
class Attendee(models.Model):
 
    ''' Miscellaneous user-related data. '''
 

	
 
    class Meta:
 
        app_label = "registrasion"
 

	
 
    def __str__(self):
 
        return "%s" % self.user
 

	
vendor/registrasion/registrasion/reporting/reports.py
Show inline comments
 
import csv
 

	
 
from django.contrib.auth.decorators import user_passes_test
 
from django.shortcuts import render
 
from django.core.urlresolvers import reverse
 
from django.http import HttpResponse
 
from django.urls import reverse
 
from functools import wraps
 

	
 
from registrasion import views
 

	
 

	
 
''' A list of report views objects that can be used to load a list of
 
reports. '''
 
_all_report_views = []
 

	
 

	
 
class Report(object):
 

	
vendor/registrasion/registrasion/reporting/views.py
Show inline comments
 
from . import forms
 

	
 
import collections
 
import datetime
 
import itertools
 

	
 
from django.conf import settings
 
from django.contrib.auth.decorators import user_passes_test
 
from django.contrib.auth.models import User
 
from django.core.urlresolvers import reverse
 
from django.contrib.auth import get_user_model
 
from django.db import models
 
from django.db.models import F, Q, Subquery, OuterRef
 
from django.db.models import Count, Max, Sum
 
from django.db.models import Case, When, Value
 
from django.db.models.fields.related import RelatedField
 
from django.db.models.fields import CharField
 
from django.shortcuts import render
 
from django.urls import reverse
 

	
 
from registrasion.controllers.cart import CartController
 
from registrasion.controllers.item import ItemController
 
from registrasion.models import conditions
 
from registrasion.models import commerce
 
from registrasion.models import people
 
from registrasion import util
 
from registrasion import views
 

	
 
from symposion.schedule import models as schedule_models
 

	
 
from .reports import get_all_reports
 
from .reports import Links
 
from .reports import ListReport
 
from .reports import QuerysetReport
 
from .reports import report_view
 

	
 
import bleach
 

	
 

	
 
def CURRENCY():
 
    return models.DecimalField(decimal_places=2)
 

	
 

	
 
User = get_user_model()
 
AttendeeProfile = util.get_object_from_name(settings.ATTENDEE_PROFILE_MODEL)
 

	
 

	
 
@user_passes_test(views._staff_only)
 
def reports_list(request):
 
    ''' Lists all of the reports currently available. '''
 

	
 
    reports = []
 

	
 
    for report in get_all_reports():
 
        reports.append({
 
            "name": report.__name__,
vendor/registrasion/registrasion/templatetags/registrasion_tags.py
Show inline comments
 
from registrasion.models import commerce
 
from registrasion.controllers.category import CategoryController
 
from registrasion.controllers.item import ItemController
 

	
 
from django import template
 
from django.conf import settings
 
from django.db.models import Sum
 
try:
 
    from urllib import urlencode
 
except ImportError:
 
    from urllib.parse import urlencode
 
from urllib.parse import urlencode
 

	
 
from operator import attrgetter
 

	
 
register = template.Library()
 

	
 

	
 
def user_for_context(context):
 
    ''' Returns either context.user or context.request.user if the former is
 
    not defined. '''
 
    try:
 
        return context["user"]
 
    except KeyError:
 
        return context.request.user
 

	
 

	
 
@register.assignment_tag(takes_context=True)
 
@register.simple_tag(takes_context=True)
 
def available_categories(context):
 
    ''' Gets all of the currently available products.
 

	
 
    Returns:
 
        [models.inventory.Category, ...]: A list of all of the categories that
 
            have Products that the current user can reserve.
 

	
 
    '''
 
    return CategoryController.available_categories(user_for_context(context))
 

	
 

	
 
@register.assignment_tag(takes_context=True)
 
@register.simple_tag(takes_context=True)
 
def missing_categories(context):
 
    ''' Adds the categories that the user does not currently have. '''
 
    user = user_for_context(context)
 
    categories_available = set(CategoryController.available_categories(user))
 
    items = ItemController(user).items_pending_or_purchased()
 

	
 
    categories_held = set()
 

	
 
    for product, quantity in items:
 
        categories_held.add(product.category)
 

	
 
    missing = categories_available - categories_held
 

	
 
    return sorted(set(i for i in missing), key=attrgetter("order"))
 

	
 
@register.assignment_tag(takes_context=True)
 
@register.simple_tag(takes_context=True)
 
def available_credit(context):
 
    ''' Calculates the sum of unclaimed credit from this user's credit notes.
 

	
 
    Returns:
 
        Decimal: the sum of the values of unclaimed credit notes for the
 
            current user.
 

	
 
    '''
 

	
 
    notes = commerce.CreditNote.unclaimed().filter(
 
        invoice__user=user_for_context(context),
 
    )
 
    ret = notes.values("amount").aggregate(Sum("amount"))["amount__sum"] or 0
 
    return 0 - ret
 

	
 

	
 
@register.assignment_tag(takes_context=True)
 
@register.simple_tag(takes_context=True)
 
def invoices(context):
 
    '''
 

	
 
    Returns:
 
        [models.commerce.Invoice, ...]: All of the current user's invoices. '''
 
    return commerce.Invoice.objects.filter(user=user_for_context(context))
 

	
 

	
 
@register.assignment_tag(takes_context=True)
 
@register.simple_tag(takes_context=True)
 
def items_pending(context):
 
    ''' Gets all of the items that the user from this context has reserved.
 

	
 
    The user will be either `context.user`, and `context.request.user` if
 
    the former is not defined.
 
    '''
 

	
 
    return ItemController(user_for_context(context)).items_pending()
 

	
 

	
 
@register.assignment_tag(takes_context=True)
 
@register.simple_tag(takes_context=True)
 
def items_purchased(context, category=None):
 
    ''' Returns the items purchased for this user.
 

	
 
    The user will be either `context.user`, and `context.request.user` if
 
    the former is not defined.
 
    '''
 

	
 
    return ItemController(user_for_context(context)).items_purchased(
 
        category=category
 
    )
 

	
 

	
 
@register.assignment_tag(takes_context=True)
 
@register.simple_tag(takes_context=True)
 
def total_items_purchased(context, category=None):
 
    ''' Returns the number of items purchased for this user (sum of quantities).
 

	
 
    The user will be either `context.user`, and `context.request.user` if
 
    the former is not defined.
 
    '''
 

	
 
    return sum(i.quantity for i in items_purchased(context, category))
 

	
 

	
 
@register.assignment_tag(takes_context=True)
 
@register.simple_tag(takes_context=True)
 
def report_as_csv(context, section):
 

	
 
    old_query = context.request.META["QUERY_STRING"]
 
    query = dict([("section", section), ("content_type", "text/csv")])
 
    querystring = urlencode(query)
 

	
 
    if old_query:
 
        querystring = old_query + "&" + querystring
 

	
 
    return context.request.path + "?" + querystring
 

	
 

	
 
@register.assignment_tag(takes_context=True)
 
@register.simple_tag(takes_context=True)
 
def sold_out_and_unregistered(context):
 
    ''' If the current user is unregistered, returns True if there are no
 
    products in the TICKET_PRODUCT_CATEGORY that are available to that user.
 

	
 
    If there *are* products available, the return False.
 

	
 
    If the current user *is* registered, then return None (it's not a
 
    pertinent question for people who already have a ticket).
 

	
 
    '''
 

	
 
    user = user_for_context(context)
...
 
@@ -177,16 +174,16 @@ class IncludeNode(template.Node):
 
@register.tag
 
def include_if_exists(parser, token):
 
    """Usage: {% include_if_exists "head.html" %}
 

	
 
    This will fail silently if the template doesn't exist. If it does, it will
 
    be rendered with the current context.
 

	
 
    From: https://djangosnippets.org/snippets/2058/
 
    """
 
    try:
 
        tag_name, template_name = token.split_contents()
 
    except ValueError:
 
        raise (template.TemplateSyntaxError,
 
        raise template.TemplateSyntaxError(
 
            "%r tag requires a single argument" % token.contents.split()[0])
 

	
 
    return IncludeNode(template_name)
vendor/registrasion/registrasion/tests/test_batch.py
Show inline comments
...
 
@@ -118,20 +118,20 @@ class BatchTestCase(RegistrationCartTestCase):
 
            end_count = 0
 

	
 
            def end_batch(self):
 
                self.end_count += 1
 

	
 
        @BatchController.memoise
 
        def get_ender(user):
 
            return Ender()
 

	
 
        # end_batch should get called once on exiting the batch
 
        with BatchController.batch(self.USER_1):
 
            ender = get_ender(self.USER_1)
 
        self.assertEquals(1, ender.end_count)
 
        self.assertEqual(1, ender.end_count)
 

	
 
        # end_batch should get called once on exiting the batch
 
        # no matter how deep the object gets cached
 
        with BatchController.batch(self.USER_1):
 
            with BatchController.batch(self.USER_1):
 
                ender = get_ender(self.USER_1)
 
        self.assertEquals(1, ender.end_count)
 
        self.assertEqual(1, ender.end_count)
vendor/registrasion/registrasion/tests/test_cart.py
Show inline comments
 
import datetime
 
import pytz
 

	
 
from decimal import Decimal
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.core.exceptions import ValidationError
 
from django.core.management import call_command
 
from django.test import TestCase
 

	
 
from registrasion.models import commerce
 
from registrasion.models import conditions
 
from registrasion.models import inventory
 
from registrasion.models import people
 
from registrasion.controllers.batch import BatchController
 
from registrasion.controllers.product import ProductController
 

	
 
from registrasion.tests.controller_helpers import TestingCartController
 
from registrasion.tests.patches import MixInPatches
 

	
 
UTC = pytz.timezone('UTC')
 
User = get_user_model()
 

	
 

	
 
class RegistrationCartTestCase(MixInPatches, TestCase):
 

	
 
    def setUp(self):
 
        super(RegistrationCartTestCase, self).setUp()
 

	
 
    def tearDown(self):
 
        if True:
 
            # If you're seeing segfaults in tests, enable this.
 
            call_command(
 
                'flush',
...
 
@@ -76,25 +77,25 @@ class RegistrationCartTestCase(MixInPatches, TestCase):
 
                required=False,
 
            )
 
            cls.categories.append(cat)
 

	
 
        cls.CAT_1 = cls.categories[0]
 
        cls.CAT_2 = cls.categories[1]
 

	
 
        cls.products = []
 
        for i in range(4):
 
            prod = inventory.Product.objects.create(
 
                name="Product " + str(i + 1),
 
                description="This is a test product.",
 
                category=cls.categories[i / 2],  # 2 products per category
 
                category=cls.categories[i // 2],  # 2 products per category
 
                price=Decimal("10.00"),
 
                reservation_duration=cls.RESERVATION,
 
                limit_per_user=10,
 
                order=1,
 
            )
 
            cls.products.append(prod)
 

	
 
        cls.PROD_1 = cls.products[0]
 
        cls.PROD_2 = cls.products[1]
 
        cls.PROD_3 = cls.products[2]
 
        cls.PROD_4 = cls.products[3]
 

	
...
 
@@ -183,25 +184,25 @@ class BasicCartTests(RegistrationCartTestCase):
 
        current_cart = TestingCartController.for_user(self.USER_1)
 

	
 
        # Add a product twice
 
        current_cart.add_to_cart(self.PROD_1, 1)
 
        current_cart.add_to_cart(self.PROD_1, 1)
 

	
 
        # Count of products for a given user should be collapsed.
 
        items = commerce.ProductItem.objects.filter(
 
            cart=current_cart.cart,
 
            product=self.PROD_1)
 
        self.assertEqual(1, len(items))
 
        item = items[0]
 
        self.assertEquals(2, item.quantity)
 
        self.assertEqual(2, item.quantity)
 

	
 
    def test_set_quantity(self):
 
        current_cart = TestingCartController.for_user(self.USER_1)
 

	
 
        def get_item():
 
            return commerce.ProductItem.objects.get(
 
                cart=current_cart.cart,
 
                product=self.PROD_1)
 

	
 
        current_cart.set_quantity(self.PROD_1, 1)
 
        self.assertEqual(1, get_item().quantity)
 

	
vendor/registrasion/registrasion/tests/test_credit_note.py
Show inline comments
...
 
@@ -105,69 +105,69 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        invoice = self._invoice_containing_prod_1(1)
 

	
 
        to_pay = invoice.invoice.value
 
        invoice.pay("Reference", to_pay)
 
        self.assertTrue(invoice.invoice.is_paid)
 

	
 
        invoice.refund()
 

	
 
        # There should be one credit note generated out of the invoice.
 
        cn = self._credit_note_for_invoice(invoice.invoice)
 

	
 
        # That credit note should be in the unclaimed pile
 
        self.assertEquals(1, commerce.CreditNote.unclaimed().count())
 
        self.assertEqual(1, commerce.CreditNote.unclaimed().count())
 

	
 
        # Create a new (identical) cart with invoice
 
        cart = TestingCartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 

	
 
        invoice2 = TestingInvoiceController.for_cart(self.reget(cart.cart))
 

	
 
        cn.apply_to_invoice(invoice2.invoice)
 
        self.assertTrue(invoice2.invoice.is_paid)
 

	
 
        # That invoice should not show up as unclaimed any more
 
        self.assertEquals(0, commerce.CreditNote.unclaimed().count())
 
        self.assertEqual(0, commerce.CreditNote.unclaimed().count())
 

	
 
    def test_apply_credit_note_generates_new_credit_note_if_overpaying(self):
 

	
 
        # Create and refund an invoice, generating a credit note.
 
        invoice = self._invoice_containing_prod_1(2)
 

	
 
        invoice.pay("Reference", invoice.invoice.value)
 
        self.assertTrue(invoice.invoice.is_paid)
 

	
 
        invoice.refund()
 

	
 
        # There should be one credit note generated out of the invoice.
 
        cn = self._credit_note_for_invoice(invoice.invoice)  # noqa
 

	
 
        self.assertEquals(1, commerce.CreditNote.unclaimed().count())
 
        self.assertEqual(1, commerce.CreditNote.unclaimed().count())
 

	
 
        # Create a new invoice for a cart of half value of inv 1
 
        invoice2 = self._invoice_containing_prod_1(1)
 
        # Credit note is automatically applied by generating the new invoice
 
        self.assertTrue(invoice2.invoice.is_paid)
 

	
 
        # We generated a new credit note, and spent the old one,
 
        # unclaimed should still be 1.
 
        self.assertEquals(1, commerce.CreditNote.unclaimed().count())
 
        self.assertEqual(1, commerce.CreditNote.unclaimed().count())
 

	
 
        credit_note2 = commerce.CreditNote.objects.get(
 
            invoice=invoice2.invoice,
 
        )
 

	
 
        # The new credit note should be the residual of the cost of cart 1
 
        # minus the cost of cart 2.
 
        self.assertEquals(
 
        self.assertEqual(
 
            invoice.invoice.value - invoice2.invoice.value,
 
            credit_note2.value,
 
        )
 

	
 
    def test_cannot_apply_credit_note_on_invalid_invoices(self):
 

	
 
        # Disable auto-application of invoices.
 
        self._manual_invoice(1)
 

	
 
        # And now start the actual test.
 

	
 
        invoice = self._invoice_containing_prod_1(1)
...
 
@@ -201,66 +201,66 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        with self.assertRaises(ValidationError):
 
            cn.apply_to_invoice(invoice_2.invoice)
 

	
 
    def test_cannot_apply_a_refunded_credit_note(self):
 
        invoice = self._invoice_containing_prod_1(1)
 

	
 
        to_pay = invoice.invoice.value
 
        invoice.pay("Reference", to_pay)
 
        self.assertTrue(invoice.invoice.is_paid)
 

	
 
        invoice.refund()
 

	
 
        self.assertEquals(1, commerce.CreditNote.unclaimed().count())
 
        self.assertEqual(1, commerce.CreditNote.unclaimed().count())
 

	
 
        cn = self._credit_note_for_invoice(invoice.invoice)
 

	
 
        cn.refund()
 

	
 
        # Refunding a credit note should mark it as claimed
 
        self.assertEquals(0, commerce.CreditNote.unclaimed().count())
 
        self.assertEqual(0, commerce.CreditNote.unclaimed().count())
 

	
 
        # Create a new cart with invoice
 
        cart = TestingCartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 

	
 
        invoice_2 = TestingInvoiceController.for_cart(self.reget(cart.cart))
 

	
 
        # Cannot pay with this credit note.
 
        with self.assertRaises(ValidationError):
 
            cn.apply_to_invoice(invoice_2.invoice)
 

	
 
    def test_cannot_refund_an_applied_credit_note(self):
 
        invoice = self._invoice_containing_prod_1(1)
 

	
 
        to_pay = invoice.invoice.value
 
        invoice.pay("Reference", to_pay)
 
        self.assertTrue(invoice.invoice.is_paid)
 

	
 
        invoice.refund()
 

	
 
        self.assertEquals(1, commerce.CreditNote.unclaimed().count())
 
        self.assertEqual(1, commerce.CreditNote.unclaimed().count())
 

	
 
        cn = self._credit_note_for_invoice(invoice.invoice)
 

	
 
        # Create a new cart with invoice
 
        cart = TestingCartController.for_user(self.USER_1)
 
        cart.add_to_cart(self.PROD_1, 1)
 

	
 
        invoice_2 = TestingInvoiceController.for_cart(self.reget(cart.cart))
 
        with self.assertRaises(ValidationError):
 
            # Creating `invoice_2` will automatically apply `cn`.
 
            cn.apply_to_invoice(invoice_2.invoice)
 

	
 
        self.assertEquals(0, commerce.CreditNote.unclaimed().count())
 
        self.assertEqual(0, commerce.CreditNote.unclaimed().count())
 

	
 
        # Cannot refund this credit note as it is already applied.
 
        with self.assertRaises(ValidationError):
 
            cn.refund()
 

	
 
    def test_money_into_void_invoice_generates_credit_note(self):
 
        invoice = self._invoice_containing_prod_1(1)
 
        invoice.void()
 

	
 
        val = invoice.invoice.value
 

	
 
        invoice.pay("Paying into the void.", val, pre_validate=False)
...
 
@@ -318,31 +318,31 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        invoice = TestingInvoiceController.for_cart(cart.cart)
 

	
 
        # Adding to cart will mean that the old invoice for this cart
 
        # will be invalidated. A new invoice should be generated.
 
        cart.add_to_cart(self.PROD_1, 1)
 
        invoice = TestingInvoiceController.for_id(invoice.invoice.id)
 
        invoice2 = TestingInvoiceController.for_cart(cart.cart)  # noqa
 
        cn2 = self._credit_note_for_invoice(invoice.invoice)
 

	
 
        invoice._refresh()
 

	
 
        # The first invoice should be refunded
 
        self.assertEquals(
 
        self.assertEqual(
 
            commerce.Invoice.STATUS_VOID,
 
            invoice.invoice.status,
 
        )
 

	
 
        # Both credit notes should be for the same amount
 
        self.assertEquals(
 
        self.assertEqual(
 
            cn.credit_note.value,
 
            cn2.credit_note.value,
 
        )
 

	
 
    def test_creating_invoice_automatically_applies_credit_note(self):
 
        ''' Single credit note is automatically applied to new invoices. '''
 

	
 
        invoice = self._invoice_containing_prod_1(1)
 
        invoice.pay("boop", invoice.invoice.value)
 
        invoice.refund()
 

	
 
        # Generate a new invoice to the same value as first invoice
vendor/registrasion/registrasion/tests/test_invoice.py
Show inline comments
...
 
@@ -89,25 +89,26 @@ class InvoiceTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        # Cart should not be active
 
        self.assertNotEqual(
 
            commerce.Cart.STATUS_ACTIVE,
 
            invoice.invoice.cart.status,
 
        )
 

	
 
        # Asking for a cart should generate a new one
 
        new_cart = TestingCartController.for_user(self.USER_1)
 
        self.assertNotEqual(invoice.invoice.cart, new_cart.cart)
 

	
 
    def test_total_payments_balance_due(self):
 
        invoice = self._invoice_containing_prod_1(2)
 
        for i in xrange(0, invoice.invoice.value):
 
        invoice_value = int(invoice.invoice.value)
 
        for i in range(0, invoice_value):
 
            self.assertTrue(
 
                i + 1, invoice.invoice.total_payments()
 
            )
 
            self.assertTrue(
 
                invoice.invoice.value - i, invoice.invoice.balance_due()
 
            )
 
            invoice.pay("Pay 1", 1)
 

	
 
    def test_invoice_includes_discounts(self):
 
        voucher = inventory.Voucher.objects.create(
 
            recipient="Voucher recipient",
 
            code="VOUCHER",
...
 
@@ -166,25 +167,25 @@ class InvoiceTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        return TestingInvoiceController.for_cart(current_cart.cart)
 

	
 
    def test_zero_value_invoice_is_automatically_paid(self):
 
        invoice_1 = self._make_zero_value_invoice()
 
        self.assertTrue(invoice_1.invoice.is_paid)
 

	
 
    def test_refunding_zero_value_invoice_releases_cart(self):
 
        invoice_1 = self._make_zero_value_invoice()
 
        cart = invoice_1.invoice.cart
 
        invoice_1.refund()
 

	
 
        cart.refresh_from_db()
 
        self.assertEquals(commerce.Cart.STATUS_RELEASED, cart.status)
 
        self.assertEqual(commerce.Cart.STATUS_RELEASED, cart.status)
 

	
 
    def test_invoice_voids_self_if_cart_changes(self):
 
        current_cart = TestingCartController.for_user(self.USER_1)
 

	
 
        # Should be able to create an invoice after the product is added
 
        current_cart.add_to_cart(self.PROD_1, 1)
 
        invoice_1 = TestingInvoiceController.for_cart(current_cart.cart)
 

	
 
        self.assertFalse(invoice_1.invoice.is_void)
 

	
 
        # Adding item to cart should produce a new invoice
 
        current_cart.add_to_cart(self.PROD_2, 1)
...
 
@@ -313,60 +314,60 @@ class InvoiceTestCase(TestHelperMixin, RegistrationCartTestCase):
 
        description_price_pairs = [
 
            ("Item 1", 15),
 
            ("Item 2", 30),
 
        ]
 

	
 
        due_delta = datetime.timedelta(hours=24)
 

	
 
        _invoice = TestingInvoiceController.manual_invoice(
 
            self.USER_1, due_delta, description_price_pairs
 
        )
 
        inv = TestingInvoiceController(_invoice)
 

	
 
        self.assertEquals(
 
        self.assertEqual(
 
            inv.invoice.value,
 
            sum(i[1] for i in description_price_pairs)
 
        )
 

	
 
        self.assertEquals(
 
        self.assertEqual(
 
            len(inv.invoice.lineitem_set.all()),
 
            len(description_price_pairs)
 
        )
 

	
 
        inv.pay("Demo payment", inv.invoice.value)
 

	
 
    def test_sends_email_on_invoice_creation(self):
 
        invoice = self._invoice_containing_prod_1(1)
 
        self.assertEquals(1, len(self.emails))
 
        self.assertEqual(1, len(self.emails))
 
        email = self.emails[0]
 
        self.assertEquals([self.USER_1.email], email["to"])
 
        self.assertEquals("invoice_created", email["kind"])
 
        self.assertEquals(invoice.invoice, email["context"]["invoice"])
 
        self.assertEqual([self.USER_1.email], email["to"])
 
        self.assertEqual("invoice_created", email["kind"])
 
        self.assertEqual(invoice.invoice, email["context"]["invoice"])
 

	
 
    def test_sends_first_change_email_on_invoice_fully_paid(self):
 
        invoice = self._invoice_containing_prod_1(1)
 

	
 
        self.assertEquals(1, len(self.emails))
 
        self.assertEqual(1, len(self.emails))
 
        invoice.pay("Partial", invoice.invoice.value - 1)
 
        # Should have an "invoice_created" email and nothing else.
 
        self.assertEquals(1, len(self.emails))
 
        self.assertEqual(1, len(self.emails))
 
        invoice.pay("Remainder", 1)
 
        self.assertEquals(2, len(self.emails))
 
        self.assertEqual(2, len(self.emails))
 

	
 
        email = self.emails[1]
 
        self.assertEquals([self.USER_1.email], email["to"])
 
        self.assertEquals("invoice_updated", email["kind"])
 
        self.assertEquals(invoice.invoice, email["context"]["invoice"])
 
        self.assertEqual([self.USER_1.email], email["to"])
 
        self.assertEqual("invoice_updated", email["kind"])
 
        self.assertEqual(invoice.invoice, email["context"]["invoice"])
 

	
 
    def test_sends_email_when_invoice_refunded(self):
 
        invoice = self._invoice_containing_prod_1(1)
 

	
 
        self.assertEquals(1, len(self.emails))
 
        self.assertEqual(1, len(self.emails))
 
        invoice.pay("Payment", invoice.invoice.value)
 
        self.assertEquals(2, len(self.emails))
 
        self.assertEqual(2, len(self.emails))
 
        invoice.refund()
 
        self.assertEquals(3, len(self.emails))
 
        self.assertEqual(3, len(self.emails))
 

	
 
        email = self.emails[2]
 
        self.assertEquals([self.USER_1.email], email["to"])
 
        self.assertEquals("invoice_updated", email["kind"])
 
        self.assertEquals(invoice.invoice, email["context"]["invoice"])
 
        self.assertEqual([self.USER_1.email], email["to"])
 
        self.assertEqual("invoice_updated", email["kind"])
 
        self.assertEqual(invoice.invoice, email["context"]["invoice"])
vendor/registrasion/registrasion/tests/test_speaker.py
Show inline comments
...
 
@@ -58,38 +58,38 @@ class SpeakerTestCase(RegistrationCartTestCase):
 
            annotation="",
 
        )
 
        speaker_2 = speaker_models.Speaker.objects.create(
 
            user=cls.USER_2,
 
            name="Speaker 2",
 
            annotation="",
 
        )
 

	
 
        proposal_1 = proposal_models.ProposalBase.objects.create(
 
            kind=kind_1,
 
            title="Proposal 1",
 
            abstract="Abstract",
 
            description="Description",
 
            #description="Description",
 
            speaker=speaker_1,
 
        )
 
        proposal_models.AdditionalSpeaker.objects.create(
 
            speaker=speaker_2,
 
            proposalbase=proposal_1,
 
            status=proposal_models.AdditionalSpeaker.SPEAKING_STATUS_ACCEPTED,
 
        )
 

	
 
        proposal_2 = proposal_models.ProposalBase.objects.create(
 
            kind=kind_2,
 
            title="Proposal 2",
 
            abstract="Abstract",
 
            description="Description",
 
            #description="Description",
 
            speaker=speaker_1,
 
        )
 
        proposal_models.AdditionalSpeaker.objects.create(
 
            speaker=speaker_2,
 
            proposalbase=proposal_2,
 
            status=proposal_models.AdditionalSpeaker.SPEAKING_STATUS_ACCEPTED,
 
        )
 

	
 
        cls.KIND_1 = kind_1
 
        cls.KIND_2 = kind_2
 
        cls.PROPOSAL_1 = proposal_1
 
        cls.PROPOSAL_2 = proposal_2
vendor/registrasion/registrasion/views.py
Show inline comments
...
 
@@ -19,46 +19,48 @@ from .controllers.credit_note import CreditNoteController
 
from .controllers.discount import DiscountController
 
from .controllers.invoice import InvoiceController
 
from .controllers.item import ItemController
 
from .controllers.product import ProductController
 
from .exceptions import CartValidationError
 

	
 
from collections import namedtuple
 

	
 
from django import forms as django_forms
 
from django.conf import settings
 
from django.contrib.auth.decorators import login_required
 
from django.contrib.auth.decorators import user_passes_test
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.contrib import messages
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.core.exceptions import ValidationError
 
from django.core.mail import send_mass_mail
 
from django.http import Http404, HttpResponse
 
from django.shortcuts import redirect
 
from django.shortcuts import render
 
from django.template import Context, Template, loader
 
from django.urls import reverse
 
import waffle
 

	
 
from lxml import etree
 
from copy import deepcopy
 

	
 
from registrasion.forms import BadgeForm, ticket_selection
 
from registrasion.contrib.badger import (
 
                                         collate,
 
                                         svg_badge,
 
                                         InvalidTicketChoiceError
 
                                         )
 

	
 
User = get_user_model()
 

	
 
_GuidedRegistrationSection = namedtuple(
 
    "GuidedRegistrationSection",
 
    (
 
        "title",
 
        "discounts",
 
        "description",
 
        "form",
 
    )
 
)
 

	
 
@util.all_arguments_optional
 
class GuidedRegistrationSection(_GuidedRegistrationSection):
vendor/registrasion/requirements/base.txt
Show inline comments
 
django-nested-admin==2.2.6
 
django-nested-admin==3.3.2
 
#symposion==1.0b2.dev3
 
lxml==4.0.0
 
lxml==4.6.1
vendor/registripe/registripe/forms.py
Show inline comments
 
import copy
 
from registripe import models
 

	
 
from django import forms
 
from django.core.urlresolvers import reverse
 
import functools
 

	
 
from django import forms
 
from django.core.urlresolvers import reverse
 
from django.core.exceptions import ValidationError
 
from django.db.models import F, Q
 
from django.forms import widgets
 
from django.urls import reverse
 
from django.utils import timezone
 

	
 
from django_countries import countries
 
from django_countries.fields import LazyTypedChoiceField
 
from django_countries.widgets import CountrySelectWidget
 

	
 
from pinax.stripe import models as pinax_stripe_models
 

	
 

	
 
class StripeCardElement(forms.widgets.TextInput):
 

	
 
    def render(self, name, value, attrs=None):
 
    def render(self, name, value, attrs=None, renderer=None):
 
        element = '''
 
            <div class="registrasion-stripe-element" id='%s' style='"-moz-appearance: textfield; -webkit-appearance: textfield;     appearance: field;"'>Please wait.</div>''' % (name, )
 

	
 
        script = '''
 
            <script type='text/javascript'>
 
                window.addEventListener('load', function(event){
 
                    stripeify('%s');
 
                });
 
            </script>''' % (name)
 
        return element + script
 

	
 

	
 
class StripeTokenWidget(forms.widgets.HiddenInput):
 

	
 
    def render(self, name, value, attrs=None):
 
    def render(self, name, value, attrs=None, renderer=None):
 

	
 
        return '''
 
            <div class='registrasion-stripe-token' style='display:none;'
 
            data-input-id='%s'
 
            ></div>
 
        ''' % (name, )
 

	
 

	
 
class CreditCardForm(forms.Form):
 

	
 
    required_css_class = 'label-required'
 

	
vendor/registripe/registripe/models.py
Show inline comments
 
from __future__ import unicode_literals
 

	
 
from django.db import models
 
from registrasion.models import commerce
 
from pinax.stripe.models import Charge
 

	
 

	
 
class StripePayment(commerce.PaymentBase):
 

	
 
    charge = models.ForeignKey(Charge)
 
    charge = models.ForeignKey(Charge, on_delete=models.CASCADE)
 

	
 
class StripeCreditNoteRefund(commerce.CreditNoteRefund):
 

	
 
    charge = models.ForeignKey(Charge)
 
    charge = models.ForeignKey(Charge, on_delete=models.CASCADE)
vendor/registripe/registripe/templates/registrasion/stripe/link_to_refunds_.html
Show inline comments
 
{% comment %}
 
  This is used in the default invoice.html file to display Stripe funcationality if the app is loaded.
 
  This is used in the default invoice.html file to display Stripe functionality if the app is loaded.
 
{% endcomment %}
 

	
 
{% block content %}
 
  <h3>Stripe Refund</h3>
 

	
 
  <p><a href="{% url 'registripe_refund' credit_note_id %}">View Stripe refund options</a></p>
 
{% endblock %}
vendor/registripe/requirements.txt
Show inline comments
 
django-countries>=4.0
 
pinax-stripe==3.2.1
 
requests>=2.11.1
 
stripe==1.38.0
 
django-countries>=6.1.3
 
pinax-stripe==4.4.0
 
requests>=2.24.0
 
stripe==2.55.0
vendor/symposion/conference/models.py
Show inline comments
...
 
@@ -40,25 +40,29 @@ class Conference(models.Model):
 
    class Meta(object):
 
        verbose_name = _("conference")
 
        verbose_name_plural = _("conferences")
 

	
 

	
 
class Section(models.Model):
 
    """
 
    a section of the conference such as "Tutorials", "Workshops",
 
    "Talks", "Expo", "Sprints", that may have its own review and
 
    scheduling process.
 
    """
 

	
 
    conference = models.ForeignKey(Conference, verbose_name=_("Conference"))
 
    conference = models.ForeignKey(
 
        Conference,
 
        verbose_name=_("Conference"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    name = models.CharField(_("Name"), max_length=100)
 
    slug = models.SlugField(verbose_name=_("Slug"))
 

	
 
    # when the section runs
 
    start_date = models.DateField(_("Start date"), null=True, blank=True)
 
    end_date = models.DateField(_("End date"), null=True, blank=True)
 

	
 
    def __str__(self):
 
        return "%s %s" % (self.conference, self.name)
 

	
 
    class Meta(object):
vendor/symposion/conference/views.py
Show inline comments
 
from django.http import Http404
 
from django.shortcuts import render
 

	
 
from django.contrib.auth.decorators import login_required
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 

	
 
User = get_user_model()
 

	
 

	
 
@login_required
 
def user_list(request):
 

	
 
    if not request.user.is_staff:
 
        raise Http404()
 

	
 
    return render(request, "symposion/conference/user_list.html", {
 
        "users": User.objects.all(),
 
    })
vendor/symposion/proposals/models.py
Show inline comments
 
import os
 
import uuid
 

	
 
from django.core.urlresolvers import reverse
 
from django.db import models
 
from django.db.models import Q
 
from django.urls import reverse
 
from django.utils.translation import ugettext_lazy as _
 
from django.utils.timezone import now
 

	
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.core.exceptions import ValidationError
 

	
 
from model_utils.managers import InheritanceManager
 
from reversion import revisions as reversion
 

	
 
from symposion import constants
 
from symposion.text_parser import parse
 
from symposion.conference.models import Section
 
from symposion.speakers.models import Speaker
 

	
 
User = get_user_model()
 

	
 

	
 
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, verbose_name=_("Section"))
 
    section = models.OneToOneField(
 
        Section,
 
        verbose_name=_("Section"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    start = models.DateTimeField(null=True, blank=True, verbose_name=_("Start"))
 
    end = models.DateTimeField(null=True, blank=True, verbose_name=_("End"))
 
    closed = models.NullBooleanField(verbose_name=_("Closed"))
 
    published = models.NullBooleanField(verbose_name=_("Published"))
 

	
 
    @classmethod
 
    def available(cls):
 
        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),
...
 
@@ -57,38 +63,47 @@ class ProposalSection(models.Model):
 
    def __str__(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", verbose_name=_("Section"))
 
    section = models.ForeignKey(
 
        Section,
 
        related_name="proposal_kinds",
 
        verbose_name=_("Section"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    name = models.CharField(_("Name"), max_length=100)
 
    slug = models.SlugField(verbose_name=_("Slug"))
 

	
 
    def __str__(self):
 
        return self.name
 

	
 

	
 
class ProposalBase(models.Model):
 

	
 
    objects = InheritanceManager()
 

	
 
    kind = models.ForeignKey(ProposalKind, verbose_name=_("Kind"))
 
    kind = models.ForeignKey(
 
        ProposalKind,
 
        verbose_name=_("Kind"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    title = models.CharField(max_length=100, verbose_name=_("Title"))
 
    abstract = models.TextField(
 
        _("Abstract"),
 
        help_text=_("This will appear in the conference programme. Up to about "
 
                    "500 words. " + constants.TEXT_FIELD_MONOSPACE_NOTE)
 
    )
 
    abstract_html = models.TextField(blank=True)
 

	
 
    private_abstract = models.TextField(
 
        _("Private Abstract"),
 
        help_text=_("This will only be shown to organisers and reviewers. You "
...
 
@@ -125,25 +140,30 @@ class ProposalBase(models.Model):
 
        _("Video"),
 
        blank=True,
 
        help_text=_("You may optionally provide us with a link to a video of "
 
                    "you speaking at another event, or of a short 'elevator "
 
                    "pitch' of your proposed talk.")
 
    )
 

	
 
    submitted = models.DateTimeField(
 
        default=now,
 
        editable=False,
 
        verbose_name=_("Submitted")
 
    )
 
    speaker = models.ForeignKey(Speaker, related_name="proposals", verbose_name=_("Speaker"))
 
    speaker = models.ForeignKey(
 
        Speaker,
 
        related_name="proposals",
 
        verbose_name=_("Speaker"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    # @@@ this validation used to exist as a validators keyword on additional_speakers
 
    #     M2M field but that is no longer supported by Django. Should be moved to
 
    #     the form level
 
    def additional_speaker_validator(self, a_speaker):
 
        if a_speaker.speaker.email == self.speaker.email:
 
            raise ValidationError(_("%s is same as primary speaker.") % a_speaker.speaker.email)
 
        if a_speaker in [self.additional_speakers]:
 
            raise ValidationError(_("%s has already been in speakers.") % a_speaker.speaker.email)
 

	
 
    additional_speakers = models.ManyToManyField(Speaker, through="AdditionalSpeaker",
 
                                                 blank=True, verbose_name=_("Addtional speakers"))
...
 
@@ -204,49 +224,66 @@ reversion.register(ProposalBase)
 
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(Speaker, verbose_name=_("Speaker"))
 
    proposalbase = models.ForeignKey(ProposalBase, verbose_name=_("Proposalbase"))
 
    speaker = models.ForeignKey(
 
        Speaker,
 
        verbose_name=_("Speaker"),
 
        on_delete=models.CASCADE,
 
    )
 
    proposalbase = models.ForeignKey(
 
        ProposalBase,
 
        verbose_name=_("Proposalbase"),
 
        on_delete=models.CASCADE,
 
    )
 
    status = models.IntegerField(choices=SPEAKING_STATUS, default=SPEAKING_STATUS_PENDING, verbose_name=_("Status"))
 

	
 
    class Meta:
 
        unique_together = ("speaker", "proposalbase")
 
        verbose_name = _("Addtional speaker")
 
        verbose_name_plural = _("Additional speakers")
 

	
 
    def __str__(self):
 
        if self.status is self.SPEAKING_STATUS_PENDING:
 
            return _(u"pending speaker (%s)") % self.speaker.email
 
        elif self.status is self.SPEAKING_STATUS_DECLINED:
 
            return _(u"declined speaker (%s)") % self.speaker.email
 
        else:
 
            return self.speaker.name
 

	
 

	
 
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", verbose_name=_("Proposal"))
 
    proposal = models.ForeignKey(
 
        ProposalBase,
 
        related_name="supporting_documents",
 
        verbose_name=_("Proposal"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    uploaded_by = models.ForeignKey(User, verbose_name=_("Uploaded by"))
 
    uploaded_by = models.ForeignKey(
 
        User,
 
        verbose_name=_("Uploaded by"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    created_at = models.DateTimeField(default=now, verbose_name=_("Created at"))
 

	
 
    file = models.FileField(upload_to=uuid_filename, verbose_name=_("File"))
 
    description = models.CharField(max_length=140, verbose_name=_("Description"))
 

	
 
    def download_url(self):
 
        return self.file.url
vendor/symposion/proposals/views.py
Show inline comments
 
import hashlib
 
import random
 
import sys
 

	
 
from django.conf import settings
 
from django.contrib.auth.decorators import login_required
 
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
 
from django.core.urlresolvers import reverse
 
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.urls import reverse
 
from django.views import static
 

	
 
from django.contrib import messages
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 

	
 
from django.utils.translation import ugettext_lazy as _
 

	
 
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
 
)
 

	
 
User = get_user_model()
 

	
 

	
 
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():
 
    if not request.user.is_authenticated:
 
        messages.info(request, _("To submit a proposal, please "
 
                                 "<a href='{0}'>log in</a> and create a speaker profile "
 
                                 "via the dashboard.".format(settings.LOGIN_URL)))
 
        return redirect("dashboard")  # @@@ unauth'd speaker info page?
 
    else:
 
        try:
 
            request.user.speaker_profile
 
        except ObjectDoesNotExist:
 
            url = reverse("speaker_create")
 
            messages.info(request, _("To submit a proposal, first "
 
                                     "<a href='{0}'>create a speaker "
 
                                     "profile</a>.".format(url)))
vendor/symposion/reviews/models.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
from datetime import datetime
 
from decimal import Decimal
 

	
 
from django.core.exceptions import ValidationError
 

	
 
from django.db import models
 
from django.db.models import Q, F
 
from django.db.models import Case, When, Value
 
from django.db.models import Count
 
from django.db.models.signals import post_save
 

	
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.utils.translation import ugettext_lazy as _
 

	
 
from symposion import constants
 
from symposion.text_parser import parse
 
from symposion.proposals.models import ProposalBase
 
from symposion.schedule.models import Presentation
 

	
 
User = get_user_model()
 

	
 

	
 
class Votes(object):
 
    ABSTAIN = "0"
 
    PLUS_TWO = "+2"
 
    PLUS_ONE = "+1"
 
    MINUS_ONE = "-1"
 
    MINUS_TWO = "-2"
 

	
 
    CHOICES = [
 
        (PLUS_TWO, _("+2 — Good proposal and I will argue for it to be accepted.")),
 
        (PLUS_ONE, _("+1 — OK proposal, but I will not argue for it to be accepted.")),
 
        (MINUS_ONE, _("−1 — Weak proposal, but I will not argue strongly against acceptance.")),
...
 
@@ -42,26 +44,34 @@ class ReviewAssignment(models.Model):
 
    AUTO_ASSIGNED_INITIAL = 0
 
    OPT_IN = 1
 
    AUTO_ASSIGNED_LATER = 2
 

	
 
    NUM_REVIEWERS = 3
 

	
 
    ORIGIN_CHOICES = [
 
        (AUTO_ASSIGNED_INITIAL, _("auto-assigned, initial")),
 
        (OPT_IN, _("opted-in")),
 
        (AUTO_ASSIGNED_LATER, _("auto-assigned, later")),
 
    ]
 

	
 
    proposal = models.ForeignKey(ProposalBase, verbose_name=_("Proposal"))
 
    user = models.ForeignKey(User, verbose_name=_("User"))
 
    proposal = models.ForeignKey(
 
        ProposalBase,
 
        verbose_name=_("Proposal"),
 
        on_delete=models.CASCADE,
 
    )
 
    user = models.ForeignKey(
 
        User,
 
        verbose_name=_("User"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    origin = models.IntegerField(choices=ORIGIN_CHOICES, verbose_name=_("Origin"))
 

	
 
    assigned_at = models.DateTimeField(default=datetime.now, verbose_name=_("Assigned at"))
 
    opted_out = models.BooleanField(default=False, verbose_name=_("Opted out"))
 

	
 
    @classmethod
 
    def create_assignments(cls, proposal, origin=AUTO_ASSIGNED_INITIAL):
 
        speakers = [proposal.speaker] + list(proposal.additional_speakers.all())
 
        reviewers = User.objects.exclude(
 
            pk__in=[
 
                speaker.user_id
...
 
@@ -82,46 +92,64 @@ class ReviewAssignment(models.Model):
 
        )
 
        num_assigned_reviewers = ReviewAssignment.objects.filter(
 
            proposal_id=proposal.id, opted_out=0).count()
 
        for reviewer in reviewers[:max(0, cls.NUM_REVIEWERS - num_assigned_reviewers)]:
 
            cls._default_manager.create(
 
                proposal=proposal,
 
                user=reviewer,
 
                origin=origin,
 
            )
 

	
 

	
 
class ProposalMessage(models.Model):
 
    proposal = models.ForeignKey(ProposalBase, related_name="messages", verbose_name=_("Proposal"))
 
    user = models.ForeignKey(User, verbose_name=_("User"))
 
    proposal = models.ForeignKey(
 
        ProposalBase,
 
        related_name="messages",
 
        verbose_name=_("Proposal"),
 
        on_delete=models.CASCADE,
 
    )
 
    user = models.ForeignKey(
 
        User,
 
        verbose_name=_("User"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    message = models.TextField(verbose_name=_("Message"))
 
    message_html = models.TextField(blank=True)
 
    submitted_at = models.DateTimeField(default=datetime.now, editable=False, verbose_name=_("Submitted at"))
 

	
 
    def save(self, *args, **kwargs):
 
        self.message_html = parse(self.message)
 
        return super(ProposalMessage, self).save(*args, **kwargs)
 

	
 
    class Meta:
 
        ordering = ["submitted_at"]
 
        verbose_name = _("proposal message")
 
        verbose_name_plural = _("proposal messages")
 

	
 

	
 
class Review(models.Model):
 
    VOTES = VOTES
 

	
 
    proposal = models.ForeignKey(ProposalBase, related_name="reviews", verbose_name=_("Proposal"))
 
    user = models.ForeignKey(User, verbose_name=_("User"))
 
    proposal = models.ForeignKey(
 
        ProposalBase,
 
        related_name="reviews",
 
        verbose_name=_("Proposal"),
 
        on_delete=models.CASCADE,
 
    )
 
    user = models.ForeignKey(
 
        User,
 
        verbose_name=_("User"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    # No way to encode "-0" vs. "+0" into an IntegerField, and I don't feel
 
    # like some complicated encoding system.
 
    vote = models.CharField(max_length=2, blank=True, choices=VOTES.CHOICES, verbose_name=_("Vote"))
 
    comment = models.TextField(
 
        blank=True,
 
        verbose_name=_("Comment")
 
    )
 
    comment_html = models.TextField(blank=True)
 
    submitted_at = models.DateTimeField(default=datetime.now, editable=False, verbose_name=_("Submitted at"))
 

	
 
    def clean(self):
...
 
@@ -199,49 +227,63 @@ class Review(models.Model):
 
    @property
 
    def section(self):
 
        return self.proposal.kind.section.slug
 

	
 
    class Meta:
 
        verbose_name = _("review")
 
        verbose_name_plural = _("reviews")
 

	
 

	
 
class LatestVote(models.Model):
 
    VOTES = VOTES
 

	
 
    proposal = models.ForeignKey(ProposalBase, related_name="votes", verbose_name=_("Proposal"))
 
    user = models.ForeignKey(User, verbose_name=_("User"))
 
    proposal = models.ForeignKey(
 
        ProposalBase,
 
        related_name="votes",
 
        verbose_name=_("Proposal"),
 
        on_delete=models.CASCADE,
 
    )
 
    user = models.ForeignKey(
 
        User,
 
        verbose_name=_("User"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    # No way to encode "-0" vs. "+0" into an IntegerField, and I don't feel
 
    # like some complicated encoding system.
 
    vote = models.CharField(max_length=2, choices=VOTES.CHOICES, verbose_name=_("Vote"))
 
    submitted_at = models.DateTimeField(default=datetime.now, editable=False, verbose_name=_("Submitted at"))
 

	
 
    class Meta:
 
        unique_together = [("proposal", "user")]
 
        verbose_name = _("latest vote")
 
        verbose_name_plural = _("latest votes")
 

	
 
    def css_class(self):
 
        return {
 
            self.VOTES.ABSTAIN: "abstain",
 
            self.VOTES.PLUS_TWO: "plus-two",
 
            self.VOTES.PLUS_ONE: "plus-one",
 
            self.VOTES.MINUS_ONE: "minus-one",
 
            self.VOTES.MINUS_TWO: "minus-two",
 
        }[self.vote]
 

	
 

	
 
class ProposalResult(models.Model):
 
    proposal = models.OneToOneField(ProposalBase, related_name="result", verbose_name=_("Proposal"))
 
    proposal = models.OneToOneField(
 
        ProposalBase,
 
        related_name="result",
 
        verbose_name=_("Proposal"),
 
        on_delete=models.CASCADE,
 
    )
 
    score = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal("0.00"), verbose_name=_("Score"))
 
    comment_count = models.PositiveIntegerField(default=0, verbose_name=_("Comment count"))
 
    # vote_count only counts non-abstain votes.
 
    vote_count = models.PositiveIntegerField(default=0, verbose_name=_("Vote count"))
 
    abstain = models.PositiveIntegerField(default=0, verbose_name=_("Abstain"))
 
    plus_two = models.PositiveIntegerField(default=0, verbose_name=_("Plus two"))
 
    plus_one = models.PositiveIntegerField(default=0, verbose_name=_("Plus one"))
 
    minus_one = models.PositiveIntegerField(default=0, verbose_name=_("Minus one"))
 
    minus_two = models.PositiveIntegerField(default=0, verbose_name=_("Minus two"))
 
    accepted = models.NullBooleanField(choices=[
 
        (True, "accepted"),
 
        (False, "rejected"),
...
 
@@ -288,26 +330,35 @@ class ProposalResult(models.Model):
 
        self.minus_one = vote_count[VOTES.MINUS_ONE]
 
        self.minus_two = vote_count[VOTES.MINUS_TWO]
 
        self.vote_count = sum(i[1] for i in vote_count.items()) - self.abstain
 
        self.score = self.calculate_score()
 
        self.save()
 

	
 
    class Meta:
 
        verbose_name = _("proposal_result")
 
        verbose_name_plural = _("proposal_results")
 

	
 

	
 
class Comment(models.Model):
 
    proposal = models.ForeignKey(ProposalBase, related_name="comments", verbose_name=_("Proposal"))
 
    commenter = models.ForeignKey(User, verbose_name=_("Commenter"))
 
    proposal = models.ForeignKey(
 
        ProposalBase,
 
        related_name="comments",
 
        verbose_name=_("Proposal"),
 
        on_delete=models.CASCADE,
 
    )
 
    commenter = models.ForeignKey(
 
        User,
 
        verbose_name=_("Commenter"),
 
        on_delete=models.CASCADE,
 
    )
 
    text = models.TextField(verbose_name=_("Text"))
 
    text_html = models.TextField(blank=True)
 

	
 
    # Or perhaps more accurately, can the user see this comment.
 
    public = models.BooleanField(choices=[(True, _("public")), (False, _("private"))], default=False, verbose_name=_("Public"))
 
    commented_at = models.DateTimeField(default=datetime.now, verbose_name=_("Commented at"))
 

	
 
    class Meta:
 
        verbose_name = _("comment")
 
        verbose_name_plural = _("comments")
 

	
 
    def save(self, *args, **kwargs):
...
 
@@ -320,27 +371,37 @@ class NotificationTemplate(models.Model):
 
    label = models.CharField(max_length=100, verbose_name=_("Label"))
 
    from_address = models.EmailField(verbose_name=_("From address"))
 
    subject = models.CharField(max_length=100, verbose_name=_("Subject"))
 
    body = models.TextField(verbose_name=_("Body"))
 

	
 
    class Meta:
 
        verbose_name = _("notification template")
 
        verbose_name_plural = _("notification templates")
 

	
 

	
 
class ResultNotification(models.Model):
 

	
 
    proposal = models.ForeignKey(ProposalBase, related_name="notifications", verbose_name=_("Proposal"))
 
    template = models.ForeignKey(NotificationTemplate, null=True, blank=True,
 
                                 on_delete=models.SET_NULL, verbose_name=_("Template"))
 
    proposal = models.ForeignKey(
 
        ProposalBase,
 
        related_name="notifications",
 
        verbose_name=_("Proposal"),
 
        on_delete=models.CASCADE,
 
    )
 
    template = models.ForeignKey(
 
        NotificationTemplate,
 
        null=True,
 
        blank=True,
 
        on_delete=models.SET_NULL,
 
        verbose_name=_("Template")
 
    )
 
    timestamp = models.DateTimeField(default=datetime.now, verbose_name=_("Timestamp"))
 
    to_address = models.EmailField(verbose_name=_("To address"))
 
    from_address = models.EmailField(verbose_name=_("From address"))
 
    subject = models.CharField(max_length=255, verbose_name=_("Subject"))
 
    body = models.TextField(verbose_name=_("Body"))
 

	
 
    def recipients(self):
 
        for speaker in self.proposal.speakers():
 
            yield speaker.email
 

	
 
    def __unicode__(self):
 
        return self.proposal.title + ' ' + self.timestamp.strftime('%Y-%m-%d %H:%M:%S')
vendor/symposion/reviews/templatetags/review_tags.py
Show inline comments
 
from django import template
 

	
 
from symposion.reviews.models import ReviewAssignment
 

	
 

	
 
register = template.Library()
 

	
 

	
 
@register.assignment_tag(takes_context=True)
 
@register.simple_tag(takes_context=True)
 
def review_assignments(context):
 
    request = context["request"]
 
    assignments = ReviewAssignment.objects.filter(user=request.user)
 
    return assignments
vendor/symposion/reviews/views.py
Show inline comments
...
 
@@ -259,36 +259,39 @@ def review_random_proposal(request, section_slug):
 
    return redirect("review_detail", pk=chosen.pk)
 

	
 

	
 
@login_required
 
def review_list(request, section_slug, user_pk):
 

	
 
    # if they're not a reviewer admin and they aren't the person whose
 
    # review list is being asked for, don't let them in
 
    if not request.user.has_perm("reviews.can_manage_%s" % section_slug):
 
        if not request.user.pk == user_pk:
 
            return access_not_permitted(request)
 

	
 
    section = get_object_or_404(ProposalSection, section__slug=section_slug)
 

	
 
    queryset = ProposalBase.objects.select_related("speaker__user", "result")
 
    reviewed = LatestVote.objects.filter(user__pk=user_pk).values_list("proposal", flat=True)
 
    queryset = queryset.filter(kind__section__slug=section_slug)
 
    queryset = queryset.filter(pk__in=reviewed)
 
    proposals = queryset.order_by("submitted")
 

	
 
    admin = request.user.has_perm("reviews.can_manage_%s" % section_slug)
 

	
 
    proposals = proposals_generator(request, proposals, user_pk=user_pk, check_speaker=not admin)
 

	
 
    ctx = {
 
        "proposals": proposals,
 
        "section": section,
 
    }
 
    return render(request, "symposion/reviews/review_list.html", ctx)
 

	
 

	
 
@login_required
 
def review_admin(request, section_slug):
 

	
 
    if not request.user.has_perm("reviews.can_manage_%s" % section_slug):
 
        return access_not_permitted(request)
 

	
 
    def reviewers():
 
        already_seen = set()
vendor/symposion/schedule/forms.py
Show inline comments
...
 
@@ -64,24 +64,26 @@ class ScheduleSectionForm(forms.Form):
 
    KIND = 'kind'
 

	
 
    filename = forms.FileField(
 
        label='Select a CSV file to import:',
 
        required=False
 
    )
 

	
 
    def __init__(self, *args, **kwargs):
 
        self.schedule = kwargs.pop("schedule")
 
        if 'encoding' in kwargs:
 
            self.encoding = kwargs['encoding']
 
            kwargs.pop('encoding')
 
        else:
 
            self.encoding = "utf-8"
 
        super(ScheduleSectionForm, self).__init__(*args, **kwargs)
 

	
 
    def clean_filename(self):
 
        if 'submit' in self.data:
 
            fname = self.cleaned_data.get('filename')
 
            if not fname or not fname.name.endswith('.csv'):
 
                raise forms.ValidationError(u'Please upload a .csv file')
 
            return fname
 

	
 
    def _get_start_end_times(self, data):
 
        "Return start and end time objects"
 
        times = []
vendor/symposion/schedule/models.py
Show inline comments
 
import datetime
 

	
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.db import models
 
from django.utils.translation import ugettext_lazy as _
 

	
 
from symposion.managers import DefaultSelectRelatedManager
 
from symposion.text_parser import parse
 
from symposion.proposals.models import ProposalBase
 
from symposion.conference.models import Section
 
from symposion.speakers.models import Speaker
 

	
 
User = get_user_model()
 

	
 

	
 
class Schedule(models.Model):
 
    objects = DefaultSelectRelatedManager()
 

	
 
    section = models.OneToOneField(Section, verbose_name=_("Section"))
 
    section = models.OneToOneField(
 
        Section,
 
        verbose_name=_("Section"),
 
        on_delete=models.CASCADE,
 
    )
 
    published = models.BooleanField(default=True, verbose_name=_("Published"))
 
    hidden = models.BooleanField(_("Hide schedule from overall conference view"), default=False)
 

	
 
    def __str__(self):
 
        return "%s Schedule" % self.section
 

	
 
    class SymposionMeta:
 
        select_related = ('section', )
 

	
 
    class Meta:
 
        ordering = ["section"]
 
        verbose_name = _('Schedule')
 
        verbose_name_plural = _('Schedules')
 

	
 

	
 
class Day(models.Model):
 
    objects = DefaultSelectRelatedManager()
 

	
 

	
 
    schedule = models.ForeignKey(Schedule, verbose_name=_("Schedule"))
 
    schedule = models.ForeignKey(
 
        Schedule,
 
        verbose_name=_("Schedule"),
 
        on_delete=models.CASCADE,
 
    )
 
    date = models.DateField(verbose_name=_("Date"))
 

	
 
    def __str__(self):
 
        return "%s: %s" % (self.schedule.section.name, self.date)
 

	
 
    class SymposionMeta:
 
        select_related = ('schedule__section', )
 

	
 
    class Meta:
 
        unique_together = [("schedule", "date")]
 
        ordering = ["date"]
 
        verbose_name = _("date")
 
        verbose_name_plural = _("dates")
 

	
 

	
 
class Room(models.Model):
 

	
 
    schedule = models.ForeignKey(Schedule, verbose_name=_("Schedule"))
 
    schedule = models.ForeignKey(
 
        Schedule,
 
        verbose_name=_("Schedule"),
 
        on_delete=models.CASCADE,
 
    )
 
    name = models.CharField(max_length=65, verbose_name=_("Name"))
 
    order = models.PositiveIntegerField(verbose_name=_("Order"))
 

	
 
    def __unicode__(self):
 
        return self.name
 

	
 
    def __str__(self):
 
        return self.name
 

	
 
    class Meta:
 
        verbose_name = _("Room")
 
        verbose_name_plural = _("Rooms")
 

	
 

	
 
class Track(models.Model):
 
    name = models.CharField(max_length=80, verbose_name=_("Track"))
 
    room = models.ForeignKey(Room)
 
    day = models.ForeignKey(Day)
 
    room = models.ForeignKey(Room, on_delete=models.CASCADE)
 
    day = models.ForeignKey(Day, on_delete=models.CASCADE)
 

	
 
    def __str__(self):
 
        return self.name
 

	
 
    class Meta:
 
        unique_together = [('room', 'day')]
 
        verbose_name = _("Track")
 
        verbose_name_plural = _("Tracks")
 

	
 

	
 
class SlotKind(models.Model):
 
    """
 
    A slot kind represents what kind a slot is. For example, a slot can be a
 
    break, lunch, or X-minute talk.
 
    """
 

	
 
    schedule = models.ForeignKey(Schedule, verbose_name=_("schedule"))
 
    schedule = models.ForeignKey(
 
        Schedule,
 
        verbose_name=_("schedule"),
 
        on_delete=models.CASCADE,
 
    )
 
    label = models.CharField(max_length=50, verbose_name=_("Label"))
 

	
 
    def __str__(self):
 
        return ": ".join((self.schedule.section.name, self.label))
 

	
 
    class Meta:
 
        verbose_name = _("Slot kind")
 
        verbose_name_plural = _("Slot kinds")
 

	
 

	
 
class Slot(models.Model):
 
    objects = DefaultSelectRelatedManager()
 

	
 
    name = models.CharField(max_length=512, editable=False)
 
    day = models.ForeignKey(Day, verbose_name=_("Day"))
 
    kind = models.ForeignKey(SlotKind, verbose_name=_("Kind"))
 
    day = models.ForeignKey(
 
        Day,
 
        verbose_name=_("Day"),
 
        on_delete=models.CASCADE,
 
    )
 
    kind = models.ForeignKey(
 
        SlotKind,
 
        verbose_name=_("Kind"),
 
        on_delete=models.CASCADE,
 
    )
 
    start = models.TimeField(verbose_name=_("Start"))
 
    end = models.TimeField(verbose_name=_("End"))
 
    exclusive = models.BooleanField(
 
        default=False,
 
        help_text=_("Set to true if this is the only event during this "
 
                    "timeslot"),
 
    )
 
    content_override = models.TextField(blank=True, verbose_name=_("Content override"))
 
    content_override_html = models.TextField(blank=True)
 

	
 
    def assign(self, content):
 
        """
...
 
@@ -185,54 +211,84 @@ class Slot(models.Model):
 

	
 
    class Meta:
 
        ordering = ["day__schedule__section__name", "day", "start" ]
 
        verbose_name = _("slot")
 
        verbose_name_plural = _("slots")
 

	
 

	
 
class SlotRoom(models.Model):
 
    """
 
    Links a slot with a room.
 
    """
 

	
 
    slot = models.ForeignKey(Slot, verbose_name=_("Slot"))
 
    room = models.ForeignKey(Room, verbose_name=_("Room"))
 
    slot = models.ForeignKey(
 
        Slot,
 
        verbose_name=_("Slot"),
 
        on_delete=models.CASCADE,
 
    )
 
    room = models.ForeignKey(
 
        Room,
 
        verbose_name=_("Room"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    def __str__(self):
 
        return "%s %s" % (self.room, self.slot)
 

	
 
    class Meta:
 
        unique_together = [("slot", "room")]
 
        ordering = ["slot", "room__order"]
 
        verbose_name = _("Slot room")
 
        verbose_name_plural = _("Slot rooms")
 

	
 

	
 
class Presentation(models.Model):
 
    objects = DefaultSelectRelatedManager()
 

	
 
    slot = models.OneToOneField(Slot, null=True, blank=True, related_name="content_ptr", verbose_name=_("Slot"))
 
    slot = models.OneToOneField(
 
        Slot,
 
        null=True,
 
        blank=True,
 
        related_name="content_ptr",
 
        verbose_name=_("Slot"),
 
        on_delete=models.CASCADE,
 
    )
 
    title = models.CharField(max_length=100, verbose_name=_("Title"))
 
    abstract = models.TextField(verbose_name=_("Abstract"))
 
    abstract_html = models.TextField(blank=True)
 
    speaker = models.ForeignKey(Speaker, related_name="presentations", verbose_name=_("Speaker"))
 
    speaker = models.ForeignKey(
 
        Speaker,
 
        related_name="presentations",
 
        verbose_name=_("Speaker"),
 
        on_delete=models.CASCADE,
 
    )
 
    additional_speakers = models.ManyToManyField(Speaker, related_name="copresentations",
 
                                                 blank=True, verbose_name=_("Additional speakers"))
 
    unpublish = models.BooleanField(
 
        default=False,
 
        verbose_name=_("Do not publish"),
 
    )
 
    cancelled = models.BooleanField(default=False, verbose_name=_("Cancelled"))
 
    proposal_base = models.OneToOneField(ProposalBase, related_name="presentation", verbose_name=_("Proposal base"))
 
    section = models.ForeignKey(Section, related_name="presentations", verbose_name=_("Section"))
 
    proposal_base = models.OneToOneField(
 
        ProposalBase,
 
        related_name="presentation",
 
        verbose_name=_("Proposal base"),
 
        on_delete=models.CASCADE,
 
    )
 
    section = models.ForeignKey(
 
        Section,
 
        related_name="presentations",
 
        verbose_name=_("Section"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    def save(self, *args, **kwargs):
 
        self.abstract_html = parse(self.abstract)
 
        return super(Presentation, self).save(*args, **kwargs)
 

	
 
    @property
 
    def number(self):
 
        return self.proposal.number
 

	
 
    @property
 
    def proposal(self):
 
        if self.proposal_base_id is None:
...
 
@@ -247,25 +303,30 @@ class Presentation(models.Model):
 

	
 
    def __str__(self):
 
        return "#%s %s (%s)" % (self.number, self.title, self.speaker)
 

	
 
    class Meta:
 
        ordering = ["slot"]
 
        verbose_name = _("presentation")
 
        verbose_name_plural = _("presentations")
 

	
 

	
 
class Session(models.Model):
 

	
 
    day = models.ForeignKey(Day, related_name="sessions", verbose_name=_("Day"))
 
    day = models.ForeignKey(
 
        Day,
 
        related_name="sessions",
 
        verbose_name=_("Day"),
 
        on_delete=models.CASCADE,
 
    )
 
    slots = models.ManyToManyField(Slot, related_name="sessions", verbose_name=_("Slots"))
 

	
 
    def sorted_slots(self):
 
        return self.slots.order_by("start")
 

	
 
    def start(self):
 
        slots = self.sorted_slots()
 
        if slots:
 
            return list(slots)[0].start
 
        else:
 
            return None
 

	
...
 
@@ -309,26 +370,34 @@ class Session(models.Model):
 

	
 

	
 
class SessionRole(models.Model):
 

	
 
    SESSION_ROLE_CHAIR = 1
 
    SESSION_ROLE_RUNNER = 2
 

	
 
    SESSION_ROLE_TYPES = [
 
        (SESSION_ROLE_CHAIR, _("Session Chair")),
 
        (SESSION_ROLE_RUNNER, _("Session Runner")),
 
    ]
 

	
 
    session = models.ForeignKey(Session, verbose_name=_("Session"))
 
    user = models.ForeignKey(User, verbose_name=_("User"))
 
    session = models.ForeignKey(
 
        Session,
 
        verbose_name=_("Session"),
 
        on_delete=models.CASCADE,
 
    )
 
    user = models.ForeignKey(
 
        User,
 
        verbose_name=_("User"),
 
        on_delete=models.CASCADE,
 
    )
 
    role = models.IntegerField(choices=SESSION_ROLE_TYPES, verbose_name=_("Role"))
 
    status = models.NullBooleanField(verbose_name=_("Status"))
 

	
 
    submitted = models.DateTimeField(default=datetime.datetime.now)
 

	
 
    class Meta:
 
        unique_together = [("session", "user", "role")]
 
        verbose_name = _("Session role")
 
        verbose_name_plural = _("Session roles")
 

	
 
    def __str__(self):
 
        return "%s %s: %s" % (self.user, self.session,
vendor/symposion/schedule/tests/factories.py
Show inline comments
 
import datetime
 
import random
 

	
 
import factory
 

	
 
from factory import fuzzy
 

	
 
from symposion.schedule.models import Schedule, Day, Slot, SlotKind
 
from symposion.conference.models import Section, Conference
 

	
 

	
 
class ConferenceFactory(factory.DjangoModelFactory):
 
class ConferenceFactory(factory.django.DjangoModelFactory):
 
    title = fuzzy.FuzzyText()
 
    start_date = fuzzy.FuzzyDate(datetime.date(2014, 1, 1))
 
    end_date = fuzzy.FuzzyDate(
 
        datetime.date(2014, 1, 1) + datetime.timedelta(days=random.randint(1, 10))
 
    )
 
    # timezone = TimeZoneField("UTC")
 

	
 
    class Meta:
 
        model = Conference
 

	
 

	
 
class SectionFactory(factory.DjangoModelFactory):
 
class SectionFactory(factory.django.DjangoModelFactory):
 
    conference = factory.SubFactory(ConferenceFactory)
 
    name = fuzzy.FuzzyText()
 
    slug = fuzzy.FuzzyText()
 

	
 
    class Meta:
 
        model = Section
 

	
 

	
 
class ScheduleFactory(factory.DjangoModelFactory):
 
class ScheduleFactory(factory.django.DjangoModelFactory):
 
    section = factory.SubFactory(SectionFactory)
 
    published = True
 
    hidden = False
 

	
 
    class Meta:
 
        model = Schedule
 

	
 

	
 
class SlotKindFactory(factory.DjangoModelFactory):
 
class SlotKindFactory(factory.django.DjangoModelFactory):
 
    schedule = factory.SubFactory(ScheduleFactory)
 
    label = fuzzy.FuzzyText()
 

	
 
    class Meta:
 
        model = SlotKind
 

	
 

	
 
class DayFactory(factory.DjangoModelFactory):
 
class DayFactory(factory.django.DjangoModelFactory):
 
    schedule = factory.SubFactory(ScheduleFactory)
 
    date = fuzzy.FuzzyDate(datetime.date(2014, 1, 1))
 

	
 
    class Meta:
 
        model = Day
 

	
 

	
 
class SlotFactory(factory.DjangoModelFactory):
 
class SlotFactory(factory.django.DjangoModelFactory):
 
    day = factory.SubFactory(DayFactory)
 
    kind = factory.SubFactory(SlotKindFactory)
 
    start = datetime.time(random.randint(0, 23), random.randint(0, 59))
 
    end = datetime.time(random.randint(0, 23), random.randint(0, 59))
 

	
 
    class Meta:
 
        model = Slot
vendor/symposion/schedule/tests/test_views_session.py
Show inline comments
 
from datetime import date
 

	
 
from django.conf import settings
 
from django.contrib.auth.models import User
 
from django.core.urlresolvers import reverse
 
from django.contrib.auth import get_user_model
 
from django.urls import reverse
 
from django.test import TestCase
 

	
 
from symposion.conference.models import Section, current_conference, Conference
 
from symposion.schedule.models import Day, Schedule, Session
 

	
 
User = get_user_model()
 

	
 

	
 
class TestScheduleViews(TestCase):
 
    username = "user@example.com"
 
    first_name = "Sam"
 
    last_name = "McGillicuddy"
 

	
 
    def setUp(self):
 
        self.user = User.objects.create_user(self.username,
 
                                             password="pass",
 
                                             email=self.username)
 
        self.user.first_name = self.first_name
 
        self.user.last_name = self.last_name
vendor/symposion/schedule/views.py
Show inline comments
 
import json
 
import pytz
 

	
 
from django.core.urlresolvers import reverse
 
from django.http import Http404, HttpResponse
 
from django.shortcuts import render, get_object_or_404, redirect
 
from django.urls import reverse
 
from django.template import loader, Context
 
from django.conf import settings
 

	
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.contrib import messages
 
from django.contrib.sites.models import Site
 

	
 
from django_ical.views import ICalFeed
 

	
 
from django.contrib.auth.decorators import login_required
 

	
 
from symposion.schedule.forms import SlotEditForm, ScheduleSectionForm
 
from symposion.schedule.models import Schedule, Day, Slot, Presentation, Session, SessionRole
 
from symposion.schedule.timetable import TimeTable
 
from symposion.conference.models import Conference
 
from pinaxcon.templatetags.lca2018_tags import speaker_photo
 

	
 
User = get_user_model()
 

	
 

	
 
def fetch_schedule(slug):
 
    qs = Schedule.objects.all()
 

	
 
    if slug is None:
 
        if qs.count() > 1:
 
            raise Http404()
 
        schedule = next(iter(qs), None)
 
        if schedule is None:
 
            raise Http404()
 
    else:
 
        schedule = get_object_or_404(qs, section__slug=slug)
...
 
@@ -360,41 +362,41 @@ def session_staff_email(request):
 

	
 

	
 
def session_detail(request, session_id):
 

	
 
    session = get_object_or_404(Session, id=session_id)
 

	
 
    chair = None
 
    chair_denied = False
 
    chairs = SessionRole.objects.filter(session=session, role=SessionRole.SESSION_ROLE_CHAIR).exclude(status=False)
 
    if chairs:
 
        chair = chairs[0].user
 
    else:
 
        if request.user.is_authenticated():
 
        if request.user.is_authenticated:
 
            # did the current user previously try to apply and got rejected?
 
            if SessionRole.objects.filter(session=session, user=request.user, role=SessionRole.SESSION_ROLE_CHAIR, status=False):
 
                chair_denied = True
 

	
 
    runner = None
 
    runner_denied = False
 
    runners = SessionRole.objects.filter(session=session, role=SessionRole.SESSION_ROLE_RUNNER).exclude(status=False)
 
    if runners:
 
        runner = runners[0].user
 
    else:
 
        if request.user.is_authenticated():
 
        if request.user.is_authenticated:
 
            # did the current user previously try to apply and got rejected?
 
            if SessionRole.objects.filter(session=session, user=request.user, role=SessionRole.SESSION_ROLE_RUNNER, status=False):
 
                runner_denied = True
 

	
 
    if request.method == "POST" and request.user.is_authenticated():
 
    if request.method == "POST" and request.user.is_authenticated:
 
        if not hasattr(request.user, "attendee") or not request.user.attendee.completed_registration:
 
            response = redirect("guided_registration")
 
            response["Location"] += "?next=%s" % request.path
 
            return response
 

	
 
        role = request.POST.get("role")
 
        if role == "chair":
 
            if chair is None and not chair_denied:
 
                SessionRole(session=session, role=SessionRole.SESSION_ROLE_CHAIR, user=request.user).save()
 
        elif role == "runner":
 
            if runner is None and not runner_denied:
 
                SessionRole(session=session, role=SessionRole.SESSION_ROLE_RUNNER, user=request.user).save()
vendor/symposion/speakers/models.py
Show inline comments
 
import datetime
 

	
 
from django.core.urlresolvers import reverse
 
from django.db import models
 
from django.urls import reverse
 
from django.utils.translation import ugettext_lazy as _
 

	
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 

	
 
from symposion import constants
 
from symposion.text_parser import parse
 

	
 
User = get_user_model()
 

	
 

	
 
class Speaker(models.Model):
 

	
 
    user = models.OneToOneField(User, null=True, related_name="speaker_profile", verbose_name=_("User"))
 
    user = models.OneToOneField(
 
        User,
 
        null=True,
 
        related_name="speaker_profile",
 
        verbose_name=_("User"),
 
        on_delete=models.CASCADE,
 
    )
 
    name = models.CharField(verbose_name=_("Name"), max_length=100,
 
                            help_text=_("As you would like it to appear in the"
 
                                        " conference programme."))
 
    biography = models.TextField(
 
        blank=True,
 
        help_text=_("This will appear on the conference website and in the "
 
                    "programme.  Please write in the third person, eg 'Alice "
 
                    "is a Moblin hacker...', 150-200 words. " +
 
                    constants.TEXT_FIELD_MONOSPACE_NOTE),
 
        verbose_name=_("Biography"),
 
    )
 
    biography_html = models.TextField(blank=True)
vendor/symposion/speakers/views.py
Show inline comments
 
from django.core.exceptions import ObjectDoesNotExist
 
from django.http import Http404
 
from django.shortcuts import render, redirect, get_object_or_404
 

	
 
from django.contrib import messages
 
from django.contrib.auth.decorators import login_required
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.utils.translation import ugettext_lazy as _
 

	
 
from symposion.proposals.models import ProposalBase
 
from symposion.speakers.forms import SpeakerForm
 
from symposion.speakers.models import Speaker
 

	
 
User = get_user_model()
 

	
 

	
 
@login_required
 
def speaker_create(request):
 
    try:
 
        return redirect(request.user.speaker_profile)
 
    except ObjectDoesNotExist:
 
        pass
 

	
 
    if request.method == "POST":
 
        try:
 
            speaker = Speaker.objects.get(invite_email=request.user.email)
 
            found = True
vendor/symposion/sponsorship/models.py
Show inline comments
 
import datetime
 

	
 
from django.conf import settings
 
from django.core.exceptions import ValidationError
 
from django.core.urlresolvers import reverse
 
from django.db import models
 
from django.db.models.signals import post_init, post_save
 
from django.urls import reverse
 
from django.utils.translation import ugettext_lazy as _
 

	
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 

	
 
from symposion.conference.models import Conference
 
from symposion.sponsorship.managers import SponsorManager
 

	
 
User = get_user_model()
 

	
 

	
 
# The benefits we track as individual fields on sponsors
 
# Names are the names in the database as defined by organizers.
 
# Field names are the benefit names, lowercased, with
 
# spaces changed to _, and with "_benefit" appended.
 
# Column titles are arbitrary.
 

	
 
# "really just care about the ones we have today: print logo, web logo, print description, web description and the ad."
 

	
 
BENEFITS = [
 
    {
 
        'name': 'Web logo',
...
 
@@ -35,60 +37,80 @@ BENEFITS = [
 
        'field_name': 'company_description_benefit',
 
        'column_title': _(u"Web Desc"),
 
    }, {
 
        'name': 'Print Description',
 
        'field_name': 'print_description_benefit',
 
        'column_title': _(u"Print Desc"),
 
    }
 
]
 

	
 

	
 
class SponsorLevel(models.Model):
 

	
 
    conference = models.ForeignKey(Conference, verbose_name=_("Conference"))
 
    conference = models.ForeignKey(
 
        Conference,
 
        verbose_name=_("Conference"),
 
        on_delete=models.CASCADE,
 
    )
 
    name = models.CharField(_("Name"), max_length=100)
 
    order = models.IntegerField(_("Order"), default=0)
 
    cost = models.PositiveIntegerField(_("Cost"))
 
    description = models.TextField(_("Description"), blank=True, help_text=_("This is private."))
 

	
 
    class Meta:
 
        ordering = ["conference", "order"]
 
        verbose_name = _("Sponsor level")
 
        verbose_name_plural = _("Sponsor levels")
 

	
 
    def __str__(self):
 
        return "%s %s" % (self.conference, self.name)
 

	
 
    def sponsors(self):
 
        return self.sponsor_set.filter(active=True).order_by("added")
 

	
 

	
 
class Sponsor(models.Model):
 

	
 
    applicant = models.ForeignKey(User, related_name="sponsorships", verbose_name=_("Applicant"),
 
                                  null=True)
 
    applicant = models.ForeignKey(
 
        User,
 
        related_name="sponsorships",
 
        verbose_name=_("Applicant"),
 
        null=True,
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    name = models.CharField(_("Sponsor Name"), max_length=100)
 
    display_url = models.URLField(_("display URL"), blank=True)
 
    external_url = models.URLField(_("External URL"))
 
    annotation = models.TextField(_("Annotation"), blank=True)
 
    contact_name = models.CharField(_("Contact Name"), max_length=100)
 
    contact_email = models.EmailField(_("Contact Email"))
 
    level = models.ForeignKey(SponsorLevel, verbose_name=_("level"))
 
    level = models.ForeignKey(
 
        SponsorLevel,
 
        verbose_name=_("level"),
 
        on_delete=models.CASCADE,
 
    )
 
    added = models.DateTimeField(_("added"), default=datetime.datetime.now)
 
    active = models.BooleanField(_("active"), default=False)
 

	
 
    # Denormalization (this assumes only one logo)
 
    sponsor_logo = models.ForeignKey("SponsorBenefit", related_name="+", null=True, blank=True,
 
                                     editable=False, verbose_name=_("Sponsor logo"))
 
    sponsor_logo = models.ForeignKey(
 
        "SponsorBenefit",
 
        related_name="+",
 
        null=True,
 
        blank=True,
 
        editable=False,
 
        verbose_name=_("Sponsor logo"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    # Whether things are complete
 
    # True = complete, False = incomplate, Null = n/a for this sponsor level
 
    web_logo_benefit = models.NullBooleanField(_("Web logo benefit"), help_text=_(u"Web logo benefit is complete"))
 
    print_logo_benefit = models.NullBooleanField(_("Print logo benefit"), help_text=_(u"Print logo benefit is complete"))
 
    print_description_benefit = models.NullBooleanField(_("Print description benefit"), help_text=_(u"Print description benefit is complete"))
 
    company_description_benefit = models.NullBooleanField(_("Company description benefit"), help_text=_(u"Company description benefit is complete"))
 

	
 
    objects = SponsorManager()
 

	
 
    def __str__(self):
 
        return self.name
...
 
@@ -233,44 +255,64 @@ class Benefit(models.Model):
 
    description = models.TextField(_("Description"), blank=True)
 
    type = models.CharField(_("Type"), choices=BENEFIT_TYPE_CHOICES, max_length=10,
 
                            default="simple")
 
    content_type = models.CharField(_("content type"), choices=CONTENT_TYPE_CHOICES,
 
                                    max_length=20, default="simple")
 

	
 
    def __str__(self):
 
        return self.name
 

	
 

	
 
class BenefitLevel(models.Model):
 

	
 
    benefit = models.ForeignKey(Benefit, related_name="benefit_levels", verbose_name=_("Benefit"))
 
    level = models.ForeignKey(SponsorLevel, related_name="benefit_levels", verbose_name=_("Level"))
 
    benefit = models.ForeignKey(
 
        Benefit,
 
        related_name="benefit_levels",
 
        verbose_name=_("Benefit"),
 
        on_delete=models.CASCADE,
 
    )
 
    level = models.ForeignKey(
 
        SponsorLevel,
 
        related_name="benefit_levels",
 
        verbose_name=_("Level"),
 
        on_delete=models.CASCADE,
 
    )
 

	
 
    # default limits for this benefit at given level
 
    max_words = models.PositiveIntegerField(_("Max words"), blank=True, null=True)
 
    other_limits = models.CharField(_("Other limits"), max_length=200, blank=True)
 

	
 
    class Meta:
 
        ordering = ["level"]
 
        verbose_name = _("Benefit level")
 
        verbose_name_plural = _("Benefit levels")
 

	
 
    def __str__(self):
 
        return "%s - %s" % (self.level, self.benefit)
 

	
 

	
 
class SponsorBenefit(models.Model):
 

	
 
    sponsor = models.ForeignKey(Sponsor, related_name="sponsor_benefits", verbose_name=_("Sponsor"))
 
    benefit = models.ForeignKey(Benefit, related_name="sponsor_benefits", verbose_name=_("Benefit"))
 
    sponsor = models.ForeignKey(
 
        Sponsor,
 
        related_name="sponsor_benefits",
 
        verbose_name=_("Sponsor"),
 
        on_delete=models.CASCADE,
 
    )
 
    benefit = models.ForeignKey(
 
        Benefit,
 
        related_name="sponsor_benefits",
 
        verbose_name=_("Benefit"),
 
        on_delete=models.CASCADE,
 
    )
 
    active = models.BooleanField(default=True, verbose_name=_("Active"))
 

	
 
    # Limits: will initially be set to defaults from corresponding BenefitLevel
 
    max_words = models.PositiveIntegerField(_("Max words"), blank=True, null=True)
 
    other_limits = models.CharField(_("Other limits"), max_length=200, blank=True)
 

	
 
    # Data: zero or one of these fields will be used, depending on the
 
    # type of the Benefit (text, file, or simple)
 
    text = models.TextField(_("Text"), blank=True)
 
    upload = models.FileField(_("File"), blank=True, upload_to="sponsor_files")
 

	
 
    # Whether any assets required from the sponsor have been provided
vendor/symposion/sponsorship/tests.py
Show inline comments
 
from cStringIO import StringIO
 
from io import StringIO
 
import os
 
import shutil
 
import tempfile
 
from zipfile import ZipFile
 

	
 
from django.conf import settings
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 
from django.core.exceptions import ValidationError
 
from django.core.urlresolvers import reverse
 
from django.test import TestCase
 
from django.test.utils import override_settings
 
from django.urls import reverse
 

	
 
from pycon.sponsorship.models import Benefit, Sponsor, SponsorBenefit,\
 
    SponsorLevel
 
from symposion.conference.models import current_conference
 

	
 
User = get_user_model()
 

	
 

	
 
class TestSponsorZipDownload(TestCase):
 
    def setUp(self):
 
        self.user = User.objects.create_user(username='joe',
 
                                             email='joe@example.com',
 
                                             password='joe')
 
        self.user.is_staff = True
 
        self.user.save()
 
        self.url = reverse("sponsor_zip_logos")
 
        self.assertTrue(self.client.login(username='joe@example.com',
 
                                          password='joe'))
 

	
vendor/symposion/teams/backends.py
Show inline comments
 
from django.db.models import Q
 

	
 
from .models import Team
 

	
 

	
 
class TeamPermissionsBackend(object):
 

	
 
    def authenticate(self, username=None, password=None):
 
    def authenticate(self, request, username=None, password=None):
 
        return None
 

	
 
    def get_team_permissions(self, user_obj, obj=None):
 
        """
 
        Returns a set of permission strings that this user has through his/her
 
        team memberships.
 
        """
 
        if user_obj.is_anonymous() or obj is not None:
 
        if user_obj.is_anonymous or obj is not None:
 
            return set()
 
        if not hasattr(user_obj, "_team_perm_cache"):
 
            # Member permissions
 
            memberships = Team.objects.filter(
 
                Q(memberships__user=user_obj),
 
                Q(memberships__state="member"),
 
            )
 
            perms = memberships.values_list(
 
                "permissions__content_type__app_label",
 
                "permissions__codename"
 
            ).order_by()
 
            permissions = ["%s.%s" % (ct, name) for ct, name in perms]
vendor/symposion/teams/forms.py
Show inline comments
 
from django import forms
 

	
 
from django.utils.html import escape
 
from django.utils.safestring import mark_safe
 
from django.utils.translation import ugettext_lazy as _
 

	
 
from django.contrib.auth.models import User
 
from django.contrib.auth import get_user_model
 

	
 
from symposion.teams.models import Membership
 

	
 
User = get_user_model()
 

	
 

	
 
class TeamInvitationForm(forms.Form):
 

	
 
    required_css_class = 'label-required'
 

	
 
    email = forms.EmailField(label=_("Email"),
 
                             help_text=_("email address must be that of an account on this "
 
                                         "conference site"))
 

	
 
    def __init__(self, *args, **kwargs):
 
        self.team = kwargs.pop("team")
 
        super(TeamInvitationForm, self).__init__(*args, **kwargs)
vendor/symposion/teams/migrations/0001_initial.py
Show inline comments
 
# -*- coding: utf-8 -*-
 
from django.db import models, migrations
 
import datetime
 
from django.conf import settings
 
import django.db.models.deletion
 

	
 

	
 
class Migration(migrations.Migration):
 

	
 
    dependencies = [
 
        ('auth', '0006_require_contenttypes_0002'),
 
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 
    ]
 

	
 
    operations = [
 
        migrations.CreateModel(
 
            name='Membership',
...
 
@@ -35,20 +36,20 @@ class Migration(migrations.Migration):
 
                ('created', models.DateTimeField(editable=False, default=datetime.datetime.now, verbose_name='Created')),
 
                ('manager_permissions', models.ManyToManyField(related_name='manager_teams', blank=True, to='auth.Permission', verbose_name='Manager permissions')),
 
                ('permissions', models.ManyToManyField(related_name='member_teams', blank=True, to='auth.Permission', verbose_name='Permissions')),
 
            ],
 
            options={
 
                'verbose_name_plural': 'Teams',
 
                'verbose_name': 'Team',
 
            },
 
        ),
 
        migrations.AddField(
 
            model_name='membership',
 
            name='team',
 
            field=models.ForeignKey(verbose_name='Team', to='teams.Team', related_name='memberships'),
 
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, verbose_name='Team', to='teams.Team', related_name='memberships'),
 
        ),
 
        migrations.AddField(
 
            model_name='membership',
 
            name='user',
 
            field=models.ForeignKey(verbose_name='User', to=settings.AUTH_USER_MODEL, related_name='memberships'),
 
            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, verbose_name='User', to=settings.AUTH_USER_MODEL, related_name='memberships'),
 
        ),
 
    ]
vendor/symposion/teams/models.py
Show inline comments
 
import datetime
 

	
 
from django.db import models
 
from django.utils.translation import ugettext_lazy as _
 
from django.urls import reverse
 

	
 
from django.contrib.auth.models import Permission, User
 
from django.contrib.auth.models import Permission
 
from django.contrib.auth import get_user_model
 

	
 
from reversion import revisions as reversion
 

	
 
User = get_user_model()
 

	
 

	
 
TEAM_ACCESS_CHOICES = [
 
    ("open", _("open")),
 
    ("application", _("by application")),
 
    ("invitation", _("by invitation"))
 
]
 

	
 

	
 
class Team(models.Model):
 

	
 
    slug = models.SlugField(unique=True, verbose_name=_("Slug"))
 
    name = models.CharField(max_length=100, verbose_name=_("Name"))
...
 
@@ -27,27 +31,26 @@ class Team(models.Model):
 
    permissions = models.ManyToManyField(Permission, blank=True,
 
                                         related_name="member_teams",
 
                                         verbose_name=_("Permissions"))
 

	
 
    # manager permissions
 
    manager_permissions = models.ManyToManyField(Permission, blank=True,
 
                                                 related_name="manager_teams",
 
                                                 verbose_name=_("Manager permissions"))
 

	
 
    created = models.DateTimeField(default=datetime.datetime.now,
 
                                   editable=False, verbose_name=_("Created"))
 

	
 
    @models.permalink
 
    def get_absolute_url(self):
 
        return ("team_detail", [self.slug])
 
        return reverse("team_detail", args=[self.slug])
 

	
 
    def __str__(self):
 
        return self.name
 

	
 
    def get_state_for_user(self, user):
 
        try:
 
            return self.memberships.get(user=user).state
 
        except Membership.DoesNotExist:
 
            return None
 

	
 
    def applicants(self):
 
        return self.memberships.filter(state="applied")
...
 
@@ -69,26 +72,34 @@ class Team(models.Model):
 
MEMBERSHIP_STATE_CHOICES = [
 
    ("applied", _("applied")),
 
    ("invited", _("invited")),
 
    ("declined", _("declined")),
 
    ("rejected", _("rejected")),
 
    ("member", _("member")),
 
    ("manager", _("manager")),
 
]
 

	
 

	
 
class Membership(models.Model):
 

	
 
    user = models.ForeignKey(User, related_name="memberships",
 
                             verbose_name=_("User"))
 
    team = models.ForeignKey(Team, related_name="memberships",
 
                             verbose_name=_("Team"))
 
    user = models.ForeignKey(
 
        User,
 
        related_name="memberships",
 
        verbose_name=_("User"),
 
        on_delete=models.CASCADE,
 
    )
 
    team = models.ForeignKey(
 
        Team,
 
        related_name="memberships",
 
        verbose_name=_("Team"),
 
        on_delete=models.CASCADE,
 
    )
 
    state = models.CharField(max_length=20, choices=MEMBERSHIP_STATE_CHOICES,
 
                             verbose_name=_("State"))
 
    message = models.TextField(blank=True, verbose_name=_("Message"))
 

	
 
    class Meta:
 
        verbose_name = _("Membership")
 
        verbose_name_plural = _("Memberships")
 

	
 

	
 
reversion.register(Membership)
vendor/symposion/utils/mail.py
Show inline comments
 
import os
 

	
 
from django.conf import settings
 
from django.core.mail import EmailMultiAlternatives
 
from django.template.loader import render_to_string
 
from django.utils.html import strip_tags
 

	
 
from django.contrib.sites.models import Site
 

	
 

	
 
class Sender(object):
 
    ''' Class for sending e-mails under a templete prefix. '''
 
    ''' Class for sending e-mails under a template prefix. '''
 

	
 
    def __init__(self, template_prefix):
 
        self.template_prefix = template_prefix
 

	
 
    def send_email(self, to, kind, **kwargs):
 
        ''' Sends an e-mail to the given address.
 

	
 
        to: The address
 
        kind: the ID for an e-mail kind; it should point to a subdirectory of
 
            self.template_prefix containing subject.txt and message.html, which
 
            are django templates for the subject and HTML message respectively.
 

	
0 comments (0 inline, 0 general)