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.
This commit is contained in:
Joel Addison 2020-11-22 22:21:54 +10:00
parent ac57053ecf
commit 252697b842
78 changed files with 646 additions and 364 deletions

View file

@ -1,2 +1,2 @@
django<1.12,>=1.11 django<3.0,>=2.2
pysaml2==4.8.0 pysaml2>=5.3.0

View file

@ -1,4 +1,4 @@
FROM python:3.6-stretch as symposion_base FROM python:3.8-buster as symposion_base
RUN set -ex \ RUN set -ex \
&& apt-get update && apt-get update
@ -25,6 +25,7 @@ RUN set -ex \
COPY constraints.txt requirements.txt /reqs/ COPY constraints.txt requirements.txt /reqs/
RUN set -ex \ RUN set -ex \
&& pip install -U pip \
&& pip install --no-cache-dir -r /reqs/requirements.txt -c /reqs/constraints.txt \ && pip install --no-cache-dir -r /reqs/requirements.txt -c /reqs/constraints.txt \
&& apt-get purge -y --auto-remove $buildDeps \ && apt-get purge -y --auto-remove $buildDeps \
&& rm -rf /usr/src/python ~/.cache && rm -rf /usr/src/python ~/.cache

View file

@ -8,3 +8,5 @@ LOGIN_URL='/accounts/login'
ROOT_URLCONF = "pinaxcon.devmode_urls" ROOT_URLCONF = "pinaxcon.devmode_urls"
DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage' DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
INTERNAL_IPS = ['*']

View file

@ -1,11 +1,11 @@
from django.conf.urls import include, url from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.auth.views import login, logout from django.urls import include, path
from pinaxcon import urls from pinaxcon import urls
urlpatterns = [ urlpatterns = [
url(r'^accounts/logout', logout, {'template_name': 'admin/logout.html'}), path('accounts/logout', LogoutView.as_view(template_name='admin/logout.html')),
url(r'^accounts/login', login, {'template_name': 'admin/login.html'}), path('accounts/login', LoginView.as_view(template_name='admin/login.html')),
] ]
urlpatterns += urls.urlpatterns urlpatterns += urls.urlpatterns

View file

@ -4,8 +4,13 @@ from functools import wraps
class MonkeyPatchMiddleware(object): class MonkeyPatchMiddleware(object):
''' Ensures that our monkey patching only gets called after it is safe to do so.''' ''' 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() do_monkey_patch()
response = self.get_response(request)
return response
def do_monkey_patch(): def do_monkey_patch():
@ -53,7 +58,7 @@ def patch_stripe_payment_form(): # noqa: C901
if not isinstance(value, HttpRequest): if not isinstance(value, HttpRequest):
continue continue
user = value.user user = value.user
if not user.is_authenticated(): if not user.is_authenticated:
break break
try: try:
attendee_profile = models.AttendeeProfile.objects.get( attendee_profile = models.AttendeeProfile.objects.get(

View file

@ -1,3 +1,4 @@
from django.conf import settings
from django.db import models from django.db import models
from pinaxcon.raffle.mixins import PrizeMixin, RaffleMixin from pinaxcon.raffle.mixins import PrizeMixin, RaffleMixin
@ -26,11 +27,18 @@ class Prize(PrizeMixin, models.Model):
unlocked. unlocked.
""" """
description = models.CharField(max_length=255) 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() order = models.PositiveIntegerField()
winning_ticket = models.OneToOneField( winning_ticket = models.OneToOneField(
'pinaxcon_raffle.DrawnTicket', null=True, 'pinaxcon_raffle.DrawnTicket',
blank=True, related_name='+', on_delete=models.PROTECT null=True,
blank=True,
related_name='+',
on_delete=models.PROTECT,
) )
class Meta: class Meta:
@ -45,9 +53,16 @@ class PrizeAudit(models.Model):
Stores an audit event for changes to a particular :model:`pinaxcon_raffle.Prize`. Stores an audit event for changes to a particular :model:`pinaxcon_raffle.Prize`.
""" """
reason = models.CharField(max_length=255) 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) timestamp = models.DateTimeField(auto_now_add=True)
class Meta: class Meta:
@ -62,8 +77,15 @@ class Draw(models.Model):
Stores a draw for a given :model:`pinaxcon_raffle.Raffle`, along with audit fields Stores a draw for a given :model:`pinaxcon_raffle.Raffle`, along with audit fields
for the creating :model:`auth.User` and the creation timestamp. for the creating :model:`auth.User` and the creation timestamp.
""" """
raffle = models.ForeignKey('pinaxcon_raffle.Raffle', related_name='draws') raffle = models.ForeignKey(
drawn_by = models.ForeignKey('auth.User') '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) drawn_time = models.DateTimeField(auto_now_add=True)
def __str__(self): def __str__(self):
@ -78,9 +100,18 @@ class DrawnTicket(models.Model):
""" """
ticket = models.CharField(max_length=255) ticket = models.CharField(max_length=255)
draw = models.ForeignKey('pinaxcon_raffle.Draw') draw = models.ForeignKey(
prize = models.ForeignKey('pinaxcon_raffle.Prize') 'pinaxcon_raffle.Draw',
lineitem = models.ForeignKey('registrasion.LineItem') 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): def __str__(self):
return f"{self.ticket}: {self.draw.raffle}" return f"{self.ticket}: {self.draw.raffle}"

View file

@ -1,4 +1,4 @@
from django.contrib.auth.models import User from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from symposion.conference.models import Section, current_conference from symposion.conference.models import Section, current_conference
@ -8,6 +8,8 @@ from symposion.schedule.models import Presentation
from symposion.proposals.models import ProposalKind from symposion.proposals.models import ProposalKind
from pinaxcon.proposals.models import TalkProposal from pinaxcon.proposals.models import TalkProposal
User = get_user_model()
class Command(BaseCommand): class Command(BaseCommand):

View file

@ -8,6 +8,7 @@ import saml2
import saml2.saml import saml2.saml
from datetime import date, datetime, timedelta from datetime import date, datetime, timedelta
import pytz
from dataclasses import dataclass from dataclasses import dataclass
@ -124,7 +125,7 @@ else:
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '*'] ALLOWED_HOSTS = ['127.0.0.1', 'localhost', '*']
TIME_ZONE = "Australia/Brisbane" TIME_ZONE = "Australia/Melbourne"
DATE_FORMAT = "j F Y" DATE_FORMAT = "j F Y"
LANGUAGE_CODE = "en-au" LANGUAGE_CODE = "en-au"
@ -176,12 +177,12 @@ TEMPLATES = [
}, },
] ]
MIDDLEWARE_CLASSES = [ MIDDLEWARE = [
"whitenoise.middleware.WhiteNoiseMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware", "django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware", "django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.auth.middleware.SessionAuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
"debug_toolbar.middleware.DebugToolbarMiddleware", "debug_toolbar.middleware.DebugToolbarMiddleware",
"reversion.middleware.RevisionMiddleware", "reversion.middleware.RevisionMiddleware",
@ -200,6 +201,7 @@ else:
WSGI_APPLICATION = "pinaxcon.wsgi.application" WSGI_APPLICATION = "pinaxcon.wsgi.application"
INSTALLED_APPS = [ INSTALLED_APPS = [
"whitenoise.runserver_nostatic",
"django.contrib.admin", "django.contrib.admin",
"django.contrib.auth", "django.contrib.auth",
"django.contrib.contenttypes", "django.contrib.contenttypes",
@ -224,6 +226,7 @@ INSTALLED_APPS = [
"taggit", "taggit",
"reversion", "reversion",
"sitetree", "sitetree",
"django_jsonfield_backport",
"pinax.eventlog", "pinax.eventlog",
# symposion # symposion
@ -283,9 +286,12 @@ DEBUG_TOOLBAR_PANELS = [
DEBUG_TOOLBAR_CONFIG = { DEBUG_TOOLBAR_CONFIG = {
'INTERCEPT_REDIRECTS': False, 'INTERCEPT_REDIRECTS': False,
'SHOW_TOOLBAR_CALLBACK': lambda x: DEBUG,
} }
INTERNAL_IPS = [
'127.0.0.1',
]
from debug_toolbar.panels.logging import collector from debug_toolbar.panels.logging import collector
LOGGING = { LOGGING = {
'version': 1, 'version': 1,
@ -502,9 +508,10 @@ class PenguinDinnerCat(Category):
return t return t
LCA_START = datetime(2021, 1, 23) _TZINFO = pytz.timezone(TIME_ZONE)
LCA_END = datetime(2021, 1, 25) LCA_START = datetime(2021, 1, 23, tzinfo=_TZINFO)
EARLY_BIRD_DEADLINE = datetime(2020, 12, 1) 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) PENGUIN_DINNER_TICKET_DATE = date(2021, 1, 23)
SPEAKER_DINNER_TICKET_DATE = date(2021, 1, 25) SPEAKER_DINNER_TICKET_DATE = date(2021, 1, 25)
PDNS_TICKET_DATE = date(2021, 1, 24) PDNS_TICKET_DATE = date(2021, 1, 24)

View file

@ -1,5 +1,5 @@
{% extends "site_base.html" %} {% extends "site_base.html" %}
{% load staticfiles %} {% load static %}
{% load i18n %} {% load i18n %}

View file

@ -1,5 +1,5 @@
{% extends "site_base.html" %} {% extends "site_base.html" %}
{% load staticfiles %} {% load static %}
{% load i18n %} {% load i18n %}

View file

@ -1,12 +1,11 @@
{% extends "site_base.html" %} {% extends "site_base.html" %}
{% load staticfiles %} {% load static %}
{% load i18n %} {% load i18n %}
{% load review_tags %} {% load review_tags %}
{% load teams_tags %} {% load teams_tags %}
{% load registrasion_tags %} {% load registrasion_tags %}
{% load lca2018_tags %} {% load lca2018_tags %}
{% load staticfiles %}
{% block head_title %}Dashboard{% endblock %} {% block head_title %}Dashboard{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "site_base.html" %} {% extends "site_base.html" %}
{% load registrasion_tags %} {% load registrasion_tags %}
{% load lca2018_tags %} {% load lca2018_tags %}
{% load staticfiles %} {% load static %}
{% block head_title %}Raffle Tickets{% endblock %} {% block head_title %}Raffle Tickets{% endblock %}
{% block page_title %}Raffle Tickets{% endblock %} {% block page_title %}Raffle Tickets{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends "site_base.html" %} {% extends "site_base.html" %}
{% load registrasion_tags %} {% load registrasion_tags %}
{% load lca2018_tags %} {% load lca2018_tags %}
{% load staticfiles %} {% load static %}
{% block head_title %}Raffle Winners{% endblock %} {% block head_title %}Raffle Winners{% endblock %}
{% block page_title %}Raffle Winners{% endblock %} {% block page_title %}Raffle Winners{% endblock %}

View file

@ -1,5 +1,5 @@
{% extends "site_base.html" %} {% extends "site_base.html" %}
{% load staticfiles %} {% load static %}
{% load lca2018_tags %} {% load lca2018_tags %}
{% load i18n %} {% load i18n %}

View file

@ -1,7 +1,7 @@
{% extends "registrasion/base.html" %} {% extends "registrasion/base.html" %}
{% load registrasion_tags %} {% load registrasion_tags %}
{% load lca2018_tags %} {% load lca2018_tags %}
{% load staticfiles %} {% load static %}
{% block head_title %}Tax Invoice/Statement #{{ invoice.id }}{% endblock %} {% block head_title %}Tax Invoice/Statement #{{ invoice.id }}{% endblock %}
{% block page_title %}{% conference_name %}{% endblock %} {% block page_title %}{% conference_name %}{% endblock %}

View file

@ -1,4 +1,4 @@
{% load staticfiles %} {% load static %}
{% load i18n %} {% load i18n %}
{% load sitetree %} {% load sitetree %}
{% load sass_tags %} {% load sass_tags %}

View file

@ -5,7 +5,7 @@
{% load registrasion_tags %} {% load registrasion_tags %}
{% load lca2018_tags %} {% load lca2018_tags %}
{% load lca2019_tags %} {% load lca2019_tags %}
{% load staticfiles %} {% load static %}
{% load waffle_tags %} {% load waffle_tags %}
{% if user.is_staff %} {% if user.is_staff %}

View file

@ -1,5 +1,5 @@
{% extends "site_base.html" %} {% extends "site_base.html" %}
{% load staticfiles %} {% load static %}
{% block body_outer %} {% block body_outer %}

View file

@ -1,5 +1,5 @@
{% extends "site_base.html" %} {% extends "site_base.html" %}
{% load staticfiles %} {% load static %}
{% load i18n %} {% load i18n %}

View file

@ -14,8 +14,10 @@
<h4>All proposals</h4> <h4>All proposals</h4>
{% elif reviewed == 'user_reviewed' %} {% 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 %} {% else %}
<h4>Proposals you have not yet reviewed</h4> <h4>Proposals reviewed by selected reviewer</h4>
{% endif %} {% endif %}
{% include "symposion/reviews/_review_table.html" %} {% include "symposion/reviews/_review_table.html" %}

View file

@ -1,5 +1,5 @@
{% extends "site_base.html" %} {% extends "site_base.html" %}
{% load staticfiles %} {% load static %}
{% load bootstrap %} {% load bootstrap %}

View file

@ -3,7 +3,7 @@
{% load lca2018_tags %} {% load lca2018_tags %}
{% load lca2019_tags %} {% load lca2019_tags %}
{% load sitetree %} {% load sitetree %}
{% load staticfiles %} {% load static %}
{% load thumbnail %} {% load thumbnail %}
{% block head_title %}Presentation: {{ presentation.title }}{% endblock %} {% block head_title %}Presentation: {{ presentation.title }}{% endblock %}

View file

@ -2,7 +2,7 @@
{% load lca2018_tags %} {% load lca2018_tags %}
{% load sitetree %} {% load sitetree %}
{% load staticfiles %} {% load static %}
{% load thumbnail %} {% load thumbnail %}
{% load i18n %} {% load i18n %}

View file

@ -2,7 +2,7 @@
{% load lca2018_tags %} {% load lca2018_tags %}
{% load sitetree %} {% load sitetree %}
{% load staticfiles %} {% load static %}
{% load thumbnail %} {% load thumbnail %}
{% load i18n %} {% load i18n %}

View file

@ -15,7 +15,7 @@ GST_RATE = settings.GST_RATE
register = template.Library() register = template.Library()
@register.assignment_tag() @register.simple_tag()
def classname(ob): def classname(ob):
return ob.__class__.__name__ return ob.__class__.__name__

View file

@ -1,8 +1,9 @@
import debug_toolbar
from django.conf import settings from django.conf import settings
from django.conf.urls import include, url
from django.conf.urls.static import static from django.conf.urls.static import static
from django.views.generic import RedirectView from django.views.generic import RedirectView
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.urls import include, path
from django.contrib.flatpages.views import flatpage from django.contrib.flatpages.views import flatpage
from django.contrib import admin from django.contrib import admin
@ -11,31 +12,30 @@ import symposion.views
urlpatterns = [ urlpatterns = [
url(r'^saml2/', include('djangosaml2.urls')), path('saml2/', include('djangosaml2.urls')),
url(r"^admin/", include(admin.site.urls)), path('admin/', admin.site.urls),
url(r"^speaker/", include("symposion.speakers.urls")), path("speaker/", include("symposion.speakers.urls")),
url(r"^proposals/", include("symposion.proposals.urls")), path("proposals/", include("symposion.proposals.urls")),
url(r"^reviews/", include("symposion.reviews.urls")), path("reviews/", include("symposion.reviews.urls")),
url(r"^schedule/", include("symposion.schedule.urls")), path("schedule/", include("symposion.schedule.urls")),
url(r"^conference/", include("symposion.conference.urls")), path("conference/", include("symposion.conference.urls")),
url(r"^teams/", include("symposion.teams.urls")), path("teams/", include("symposion.teams.urls")),
url(r'^raffle/', include("pinaxcon.raffle.urls")), path('raffle/', include("pinaxcon.raffle.urls")),
# Required by registrasion # Required by registrasion
url(r'^tickets/payments/', include('registripe.urls')), path('tickets/payments/', include('registripe.urls')),
url(r'^tickets/', include('registrasion.urls')), path('tickets/', include('registrasion.urls')),
url(r'^nested_admin/', include('nested_admin.urls')), path('nested_admin/', include('nested_admin.urls')),
url(r'^checkin/', include('regidesk.urls')), path('checkin/', include('regidesk.urls')),
url(r'^pages/', include('django.contrib.flatpages.urls')), path('pages/', include('django.contrib.flatpages.urls')),
url(r'^dashboard/', symposion.views.dashboard, name="dashboard"), path('dashboard/', symposion.views.dashboard, name="dashboard"),
url(r'^boardingpass', RedirectView.as_view(pattern_name="regidesk:boardingpass")), path('boardingpass', RedirectView.as_view(pattern_name="regidesk:boardingpass")),
# Debug Toolbar. Always include to ensure tests work.
path('__debug__/', include(debug_toolbar.urls)),
] ]
if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
import debug_toolbar
urlpatterns.insert(0, url(r'^__debug__/', include(debug_toolbar.urls)))
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)#

View file

@ -5,6 +5,4 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pinaxcon.settings")
from django.core.wsgi import get_wsgi_application # noqa from django.core.wsgi import get_wsgi_application # noqa
from dj_static import Cling, MediaCling # noqa application = get_wsgi_application()
application = Cling(MediaCling(get_wsgi_application()))

View file

@ -1,86 +1,51 @@
asn1crypto==0.24.0 Django>=2.2
bleach==2.1.3 pinax-theme-bootstrap==8.0.1
cachetools==2.1.0 pinax-eventlog[django-lts]==5.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-bootstrap-form==3.4
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-formset-js==0.5.0
django-gapc-storage==0.5.1 whitenoise==5.2.0
django-ical==1.4 dj-database-url==0.5.0
django-jquery-js==3.1.1 pylibmc==1.6.1
django-model-utils==3.1.2 django-debug-toolbar==3.1.1
django-nested-admin==2.2.6 django-bootstrap-form==3.4
django-nose==1.4.5 django-settings-export~=1.2.1
django-reversion==1.10.1 django-capture-tag==1.0
django-sass-processor==0.7.3 djangosaml2==0.50.0
django-settings-export==1.2.1 django-gapc-storage==0.5.2
django-sitetree==1.10.0 django-waffle==2.0.0
django-taggit==0.18.0
django-timezone-field==2.1 # database
django-waffle==0.14.0 mysqlclient==2.0.1
djangosaml2==0.17.2
easy-thumbnails==2.5 # For testing
future==0.16.0 django-nose==1.4.7
google-api-python-client==1.7.0 coverage==5.3
google-auth==1.5.1 factory_boy==3.1.0
google-auth-httplib2==0.0.3
html5lib==1.0.1 # Symposion reqs
httplib2==0.11.3 django-appconf==1.0.4
icalendar==4.0.2 django-model-utils==4.0.0
idna==2.7 django-reversion==3.0.8
jsonfield==2.0.2 django-sitetree==1.16.0
libsass==0.19.3 django-taggit==1.3.0
lxml==4.0.0 django-timezone-field==4.0
mysqlclient==1.3.13 easy-thumbnails==2.7.0
nose==1.3.7 bleach==3.2.1
oauth2client==4.1.2 pytz>=2020.1
Paste==2.0.3 django-ical==1.7.1
Pillow==5.2.0
pinax-eventlog==1.1.1 # Registrasion reqs
pinax-stripe==3.2.1 django-nested-admin==3.3.2
pinax-theme-bootstrap==7.10.2 CairoSVG==2.4.2
pyasn1==0.4.4
pyasn1-modules==0.2.2 # Registripe
pycparser==2.18 django-countries>=6.1.3
pycryptodomex==3.6.4 pinax-stripe==4.4.0
pylibmc==1.5.1 requests==2.24.0
pyOpenSSL==18.0.0 stripe==2.55.0
pypng==0.0.18
PyQRCode==1.2.1 # SASS Compiler and template tags
pysaml2==4.8.0 libsass==0.20.1
python-dateutil==2.7.3 django-sass-processor==0.8.2
pytz==2018.4 django-compressor==2.4
rcssmin==1.0.6
repoze.who==2.3 django-crispy-forms==1.9.2
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

View file

@ -1,15 +1,13 @@
import copy import copy
from regidesk import models from regidesk import models
from django import forms
from django.core.urlresolvers import reverse
import functools import functools
from django import forms from django import forms
from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db.models import F, Q from django.db.models import F, Q
from django.forms import widgets from django.forms import widgets
from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django_countries import countries from django_countries import countries

View file

@ -2,10 +2,13 @@
from django.core.management.base import BaseCommand, CommandError 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 _convert_img as convert_img
from registrasion.views import render_badge_svg from registrasion.views import render_badge_svg
User = get_user_model()
class Command(BaseCommand): class Command(BaseCommand):
def handle(self, *args, **options): def handle(self, *args, **options):

View file

@ -12,7 +12,7 @@ from django.db.models import Q, F
from django.db.models import Case, When, Value from django.db.models import Case, When, Value
from django.db.models import Count from django.db.models import Count
from django.db.models.signals import post_save 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 import pyqrcode
from symposion import constants from symposion import constants
@ -20,6 +20,8 @@ from symposion.text_parser import parse
from registrasion.models import commerce from registrasion.models import commerce
from registrasion.util import generate_access_code as generate_code from registrasion.util import generate_access_code as generate_code
User = get_user_model()
class BoardingPassTemplate(models.Model): class BoardingPassTemplate(models.Model):
@ -60,7 +62,7 @@ class BoardingPass(models.Model):
class CheckIn(models.Model): class CheckIn(models.Model):
user = models.OneToOneField(User) user = models.OneToOneField(User, on_delete=models.CASCADE)
boardingpass = models.OneToOneField(BoardingPass, null=True, boardingpass = models.OneToOneField(BoardingPass, null=True,
blank=True, on_delete=models.SET_NULL) blank=True, on_delete=models.SET_NULL)
seen = models.DateTimeField(null=True,blank=True) seen = models.DateTimeField(null=True,blank=True)

View file

@ -1,5 +1,5 @@
{% extends "site_base.html" %} {% extends "site_base.html" %}
{% load staticfiles %} {% load static %}
{% load i18n %} {% load i18n %}

View file

@ -9,7 +9,8 @@ from django.conf import settings
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required, user_passes_test, login_required from django.contrib.auth.decorators import permission_required, user_passes_test, login_required
from django.contrib.auth.mixins import PermissionRequiredMixin 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.contrib.sites.models import Site
from django.db import transaction from django.db import transaction
from django.db.models import F, Q from django.db.models import F, Q
@ -32,10 +33,10 @@ from symposion.conference.models import Conference
from regidesk import forms from regidesk import forms
from regidesk.models import BoardingPass, BoardingPassTemplate, CheckIn from regidesk.models import BoardingPass, BoardingPassTemplate, CheckIn
User = get_user_model()
AttendeeProfile = util.get_object_from_name(settings.ATTENDEE_PROFILE_MODEL) AttendeeProfile = util.get_object_from_name(settings.ATTENDEE_PROFILE_MODEL)
def _staff_only(user): def _staff_only(user):
''' Returns true if the user is staff. ''' ''' Returns true if the user is staff. '''
return user.is_staff return user.is_staff

View file

@ -1,4 +1,4 @@
django-countries>=4.0 django-countries>=6.1.3
requests>=2.11.1 requests>=2.24.0
pypng pypng
pyqrcode pyqrcode

View file

@ -44,12 +44,14 @@ class DiscountForProductInline(admin.TabularInline):
model = conditions.DiscountForProduct model = conditions.DiscountForProduct
verbose_name = _("Product included in discount") verbose_name = _("Product included in discount")
verbose_name_plural = _("Products included in discount") verbose_name_plural = _("Products included in discount")
sortable_options = []
class DiscountForCategoryInline(admin.TabularInline): class DiscountForCategoryInline(admin.TabularInline):
model = conditions.DiscountForCategory model = conditions.DiscountForCategory
verbose_name = _("Category included in discount") verbose_name = _("Category included in discount")
verbose_name_plural = _("Categories included in discount") verbose_name_plural = _("Categories included in discount")
sortable_options = []
@admin.register(conditions.TimeOrStockLimitDiscount) @admin.register(conditions.TimeOrStockLimitDiscount)
@ -137,7 +139,7 @@ class VoucherFlagInline(nested_admin.NestedStackedInline):
@admin.register(inventory.Voucher) @admin.register(inventory.Voucher)
class VoucherAdmin(nested_admin.NestedAdmin): class VoucherAdmin(nested_admin.NestedModelAdmin):
def effects(self, obj): def effects(self, obj):
''' List the effects of the voucher in the admin. ''' ''' List the effects of the voucher in the admin. '''
@ -178,7 +180,7 @@ class VoucherAdmin(nested_admin.NestedAdmin):
# Enabling conditions # Enabling conditions
@admin.register(conditions.ProductFlag) @admin.register(conditions.ProductFlag)
class ProductFlagAdmin( class ProductFlagAdmin(
nested_admin.NestedAdmin, nested_admin.NestedModelAdmin,
EffectsDisplayMixin): EffectsDisplayMixin):
def enablers(self, obj): def enablers(self, obj):
@ -194,7 +196,7 @@ class ProductFlagAdmin(
# Enabling conditions # Enabling conditions
@admin.register(conditions.CategoryFlag) @admin.register(conditions.CategoryFlag)
class CategoryFlagAdmin( class CategoryFlagAdmin(
nested_admin.NestedAdmin, nested_admin.NestedModelAdmin,
EffectsDisplayMixin): EffectsDisplayMixin):
model = conditions.CategoryFlag model = conditions.CategoryFlag
@ -206,7 +208,7 @@ class CategoryFlagAdmin(
@admin.register(conditions.SpeakerFlag) @admin.register(conditions.SpeakerFlag)
class SpeakerFlagAdmin(nested_admin.NestedAdmin, EffectsDisplayMixin): class SpeakerFlagAdmin(nested_admin.NestedModelAdmin, EffectsDisplayMixin):
model = conditions.SpeakerFlag model = conditions.SpeakerFlag
fields = ("description", "is_presenter", "is_copresenter", "proposal_kind", fields = ("description", "is_presenter", "is_copresenter", "proposal_kind",

View file

@ -24,7 +24,8 @@ import pdb
from django.core.management.base import BaseCommand 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 django.db.utils import OperationalError, ProgrammingError
from pinaxcon.registrasion.models import AttendeeProfile from pinaxcon.registrasion.models import AttendeeProfile
from registrasion.controllers.cart import CartController from registrasion.controllers.cart import CartController
@ -35,6 +36,8 @@ from registrasion.models import Product
from registrasion.models import Invoice from registrasion.models import Invoice
from symposion.speakers.models import Speaker from symposion.speakers.models import Speaker
User = get_user_model()
# A few unicode encodings ... # A few unicode encodings ...
GLYPH_PLUS = '+' GLYPH_PLUS = '+'
GLYPH_GLASS = u'\ue001' GLYPH_GLASS = u'\ue001'

View file

@ -1,7 +1,9 @@
import contextlib import contextlib
import functools import functools
from django.contrib.auth.models import User from django.contrib.auth import get_user_model
User = get_user_model()
class BatchController(object): class BatchController(object):

View file

@ -6,7 +6,7 @@ from functools import reduce
from registrasion.models import commerce from registrasion.models import commerce
from registrasion.models import inventory from registrasion.models import inventory
from collections import Iterable from collections.abc import Iterable
from collections import namedtuple from collections import namedtuple
from django.db.models import Case from django.db.models import Case
from django.db.models import Q from django.db.models import Q

View file

@ -1,7 +1,7 @@
from . import conditions from . import conditions
from . import inventory 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.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.db.models import F, Q, Sum from django.db.models import F, Q, Sum
@ -10,6 +10,8 @@ from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_utils.managers import InheritanceManager from model_utils.managers import InheritanceManager
User = get_user_model()
# Commerce Models # Commerce Models
@ -38,7 +40,7 @@ class Cart(models.Model):
(STATUS_RELEASED, _("Released")), (STATUS_RELEASED, _("Released")),
] ]
user = models.ForeignKey(User) user = models.ForeignKey(User, on_delete=models.CASCADE)
# ProductItems (foreign key) # ProductItems (foreign key)
vouchers = models.ManyToManyField(inventory.Voucher, blank=True) vouchers = models.ManyToManyField(inventory.Voucher, blank=True)
time_last_updated = models.DateTimeField( time_last_updated = models.DateTimeField(
@ -76,8 +78,8 @@ class ProductItem(models.Model):
return "product: %s * %d in Cart: %s" % ( return "product: %s * %d in Cart: %s" % (
self.product, self.quantity, self.cart) self.product, self.quantity, self.cart)
cart = models.ForeignKey(Cart) cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
product = models.ForeignKey(inventory.Product) product = models.ForeignKey(inventory.Product, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(db_index=True) quantity = models.PositiveIntegerField(db_index=True)
@ -93,9 +95,10 @@ class DiscountItem(models.Model):
return "%s: %s * %d in Cart: %s" % ( return "%s: %s * %d in Cart: %s" % (
self.discount, self.product, self.quantity, self.cart) self.discount, self.product, self.quantity, self.cart)
cart = models.ForeignKey(Cart) cart = models.ForeignKey(Cart, on_delete=models.CASCADE)
product = models.ForeignKey(inventory.Product) product = models.ForeignKey(inventory.Product, on_delete=models.CASCADE)
discount = models.ForeignKey(conditions.DiscountBase) discount = models.ForeignKey(conditions.DiscountBase,
on_delete=models.CASCADE)
quantity = models.PositiveIntegerField() quantity = models.PositiveIntegerField()
@ -189,8 +192,8 @@ class Invoice(models.Model):
return self.value - self.total_payments() return self.value - self.total_payments()
# Invoice Number # Invoice Number
user = models.ForeignKey(User) user = models.ForeignKey(User, on_delete=models.CASCADE)
cart = models.ForeignKey(Cart, null=True) cart = models.ForeignKey(Cart, null=True, on_delete=models.CASCADE)
cart_revision = models.IntegerField( cart_revision = models.IntegerField(
null=True, null=True,
db_index=True, db_index=True,
@ -242,11 +245,12 @@ class LineItem(models.Model):
''' price * quantity ''' ''' price * quantity '''
return self.price * self.quantity return self.price * self.quantity
invoice = models.ForeignKey(Invoice) invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE)
description = models.CharField(max_length=255) description = models.CharField(max_length=255)
quantity = models.PositiveIntegerField() quantity = models.PositiveIntegerField()
price = models.DecimalField(max_digits=8, decimal_places=2) 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 @python_2_unicode_compatible
@ -275,7 +279,7 @@ class PaymentBase(models.Model):
def __str__(self): def __str__(self):
return "Payment: ref=%s amount=%s" % (self.reference, self.amount) 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) time = models.DateTimeField(default=timezone.now)
reference = models.CharField(max_length=255) reference = models.CharField(max_length=255)
amount = models.DecimalField(max_digits=8, decimal_places=2) amount = models.DecimalField(max_digits=8, decimal_places=2)
@ -287,7 +291,7 @@ class ManualPayment(PaymentBase):
class Meta: class Meta:
app_label = "registrasion" app_label = "registrasion"
entered_by = models.ForeignKey(User) entered_by = models.ForeignKey(User, on_delete=models.CASCADE)
class CreditNote(PaymentBase): class CreditNote(PaymentBase):
@ -364,7 +368,7 @@ class CreditNoteApplication(CleanOnSave, PaymentBase):
"Cannot apply a refunded credit note to an invoice" "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): class CreditNoteRefund(CleanOnSave, models.Model):
@ -391,7 +395,7 @@ class CreditNoteRefund(CleanOnSave, models.Model):
"Cannot refund a credit note that has been paid to an invoice" "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) time = models.DateTimeField(default=timezone.now)
reference = models.CharField(max_length=255) reference = models.CharField(max_length=255)
@ -402,4 +406,4 @@ class ManualCreditNoteRefund(CreditNoteRefund):
class Meta: class Meta:
app_label = "registrasion" app_label = "registrasion"
entered_by = models.ForeignKey(User) entered_by = models.ForeignKey(User, on_delete=models.CASCADE)

View file

@ -10,8 +10,6 @@ from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_utils.managers import InheritanceManager from model_utils.managers import InheritanceManager
from symposion import proposals
# Condition Types # Condition Types
@ -99,7 +97,7 @@ class SpeakerCondition(models.Model):
"presentation."), "presentation."),
) )
proposal_kind = models.ManyToManyField( proposal_kind = models.ManyToManyField(
proposals.models.ProposalKind, "symposion_proposals.ProposalKind",
help_text=_("The types of proposals that these users may be " help_text=_("The types of proposals that these users may be "
"presenters of."), "presenters of."),
) )
@ -520,6 +518,7 @@ class CategoryFlag(FlagBase):
inventory.Category, inventory.Category,
help_text=_("If a product from this category is purchased, this " help_text=_("If a product from this category is purchased, this "
"condition is met."), "condition is met."),
on_delete=models.CASCADE,
) )

View file

@ -156,7 +156,8 @@ class Product(models.Model):
) )
category = models.ForeignKey( category = models.ForeignKey(
Category, Category,
verbose_name=_("Product category") verbose_name=_("Product category"),
on_delete=models.CASCADE,
) )
price = models.DecimalField( price = models.DecimalField(
max_digits=8, max_digits=8,

View file

@ -1,7 +1,7 @@
from registrasion import util from registrasion import util
from django.conf import settings 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.core.exceptions import ObjectDoesNotExist
from django.db import models from django.db import models
from django.utils.encoding import python_2_unicode_compatible from django.utils.encoding import python_2_unicode_compatible
@ -9,6 +9,9 @@ from model_utils.managers import InheritanceManager
from registrasion.models.commerce import Invoice, ProductItem from registrasion.models.commerce import Invoice, ProductItem
User = get_user_model()
# User models # User models
@python_2_unicode_compatible @python_2_unicode_compatible

View file

@ -2,8 +2,8 @@ import csv
from django.contrib.auth.decorators import user_passes_test from django.contrib.auth.decorators import user_passes_test
from django.shortcuts import render from django.shortcuts import render
from django.core.urlresolvers import reverse
from django.http import HttpResponse from django.http import HttpResponse
from django.urls import reverse
from functools import wraps from functools import wraps
from registrasion import views from registrasion import views

View file

@ -6,8 +6,7 @@ import itertools
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import user_passes_test 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.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.db.models import F, Q, Subquery, OuterRef from django.db.models import F, Q, Subquery, OuterRef
from django.db.models import Count, Max, Sum from django.db.models import Count, Max, Sum
@ -15,6 +14,7 @@ from django.db.models import Case, When, Value
from django.db.models.fields.related import RelatedField from django.db.models.fields.related import RelatedField
from django.db.models.fields import CharField from django.db.models.fields import CharField
from django.shortcuts import render from django.shortcuts import render
from django.urls import reverse
from registrasion.controllers.cart import CartController from registrasion.controllers.cart import CartController
from registrasion.controllers.item import ItemController from registrasion.controllers.item import ItemController
@ -39,6 +39,7 @@ def CURRENCY():
return models.DecimalField(decimal_places=2) return models.DecimalField(decimal_places=2)
User = get_user_model()
AttendeeProfile = util.get_object_from_name(settings.ATTENDEE_PROFILE_MODEL) AttendeeProfile = util.get_object_from_name(settings.ATTENDEE_PROFILE_MODEL)

View file

@ -5,9 +5,6 @@ from registrasion.controllers.item import ItemController
from django import template from django import template
from django.conf import settings from django.conf import settings
from django.db.models import Sum 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 from operator import attrgetter
@ -24,7 +21,7 @@ def user_for_context(context):
return context.request.user return context.request.user
@register.assignment_tag(takes_context=True) @register.simple_tag(takes_context=True)
def available_categories(context): def available_categories(context):
''' Gets all of the currently available products. ''' Gets all of the currently available products.
@ -36,7 +33,7 @@ def available_categories(context):
return CategoryController.available_categories(user_for_context(context)) return CategoryController.available_categories(user_for_context(context))
@register.assignment_tag(takes_context=True) @register.simple_tag(takes_context=True)
def missing_categories(context): def missing_categories(context):
''' Adds the categories that the user does not currently have. ''' ''' Adds the categories that the user does not currently have. '''
user = user_for_context(context) user = user_for_context(context)
@ -52,7 +49,7 @@ def missing_categories(context):
return sorted(set(i for i in missing), key=attrgetter("order")) 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): def available_credit(context):
''' Calculates the sum of unclaimed credit from this user's credit notes. ''' Calculates the sum of unclaimed credit from this user's credit notes.
@ -69,7 +66,7 @@ def available_credit(context):
return 0 - ret return 0 - ret
@register.assignment_tag(takes_context=True) @register.simple_tag(takes_context=True)
def invoices(context): def invoices(context):
''' '''
@ -78,7 +75,7 @@ def invoices(context):
return commerce.Invoice.objects.filter(user=user_for_context(context)) 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): def items_pending(context):
''' Gets all of the items that the user from this context has reserved. ''' Gets all of the items that the user from this context has reserved.
@ -89,7 +86,7 @@ def items_pending(context):
return ItemController(user_for_context(context)).items_pending() 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): def items_purchased(context, category=None):
''' Returns the items purchased for this user. ''' Returns the items purchased for this user.
@ -102,7 +99,7 @@ def items_purchased(context, category=None):
) )
@register.assignment_tag(takes_context=True) @register.simple_tag(takes_context=True)
def total_items_purchased(context, category=None): def total_items_purchased(context, category=None):
''' Returns the number of items purchased for this user (sum of quantities). ''' Returns the number of items purchased for this user (sum of quantities).
@ -113,7 +110,7 @@ def total_items_purchased(context, category=None):
return sum(i.quantity for i in items_purchased(context, category)) 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): def report_as_csv(context, section):
old_query = context.request.META["QUERY_STRING"] old_query = context.request.META["QUERY_STRING"]
@ -126,7 +123,7 @@ def report_as_csv(context, section):
return context.request.path + "?" + querystring return context.request.path + "?" + querystring
@register.assignment_tag(takes_context=True) @register.simple_tag(takes_context=True)
def sold_out_and_unregistered(context): def sold_out_and_unregistered(context):
''' If the current user is unregistered, returns True if there are no ''' If the current user is unregistered, returns True if there are no
products in the TICKET_PRODUCT_CATEGORY that are available to that user. products in the TICKET_PRODUCT_CATEGORY that are available to that user.
@ -186,7 +183,7 @@ def include_if_exists(parser, token):
try: try:
tag_name, template_name = token.split_contents() tag_name, template_name = token.split_contents()
except ValueError: except ValueError:
raise (template.TemplateSyntaxError, raise template.TemplateSyntaxError(
"%r tag requires a single argument" % token.contents.split()[0]) "%r tag requires a single argument" % token.contents.split()[0])
return IncludeNode(template_name) return IncludeNode(template_name)

View file

@ -127,11 +127,11 @@ class BatchTestCase(RegistrationCartTestCase):
# end_batch should get called once on exiting the batch # end_batch should get called once on exiting the batch
with BatchController.batch(self.USER_1): with BatchController.batch(self.USER_1):
ender = get_ender(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 # end_batch should get called once on exiting the batch
# no matter how deep the object gets cached # no matter how deep the object gets cached
with BatchController.batch(self.USER_1): with BatchController.batch(self.USER_1):
with BatchController.batch(self.USER_1): with BatchController.batch(self.USER_1):
ender = get_ender(self.USER_1) ender = get_ender(self.USER_1)
self.assertEquals(1, ender.end_count) self.assertEqual(1, ender.end_count)

View file

@ -2,7 +2,7 @@ import datetime
import pytz import pytz
from decimal import Decimal 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 ObjectDoesNotExist
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.management import call_command from django.core.management import call_command
@ -19,6 +19,7 @@ from registrasion.tests.controller_helpers import TestingCartController
from registrasion.tests.patches import MixInPatches from registrasion.tests.patches import MixInPatches
UTC = pytz.timezone('UTC') UTC = pytz.timezone('UTC')
User = get_user_model()
class RegistrationCartTestCase(MixInPatches, TestCase): class RegistrationCartTestCase(MixInPatches, TestCase):
@ -85,7 +86,7 @@ class RegistrationCartTestCase(MixInPatches, TestCase):
prod = inventory.Product.objects.create( prod = inventory.Product.objects.create(
name="Product " + str(i + 1), name="Product " + str(i + 1),
description="This is a test product.", 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"), price=Decimal("10.00"),
reservation_duration=cls.RESERVATION, reservation_duration=cls.RESERVATION,
limit_per_user=10, limit_per_user=10,
@ -192,7 +193,7 @@ class BasicCartTests(RegistrationCartTestCase):
product=self.PROD_1) product=self.PROD_1)
self.assertEqual(1, len(items)) self.assertEqual(1, len(items))
item = items[0] item = items[0]
self.assertEquals(2, item.quantity) self.assertEqual(2, item.quantity)
def test_set_quantity(self): def test_set_quantity(self):
current_cart = TestingCartController.for_user(self.USER_1) current_cart = TestingCartController.for_user(self.USER_1)

View file

@ -114,7 +114,7 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
cn = self._credit_note_for_invoice(invoice.invoice) cn = self._credit_note_for_invoice(invoice.invoice)
# That credit note should be in the unclaimed pile # 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 # Create a new (identical) cart with invoice
cart = TestingCartController.for_user(self.USER_1) cart = TestingCartController.for_user(self.USER_1)
@ -126,7 +126,7 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
self.assertTrue(invoice2.invoice.is_paid) self.assertTrue(invoice2.invoice.is_paid)
# That invoice should not show up as unclaimed any more # 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): def test_apply_credit_note_generates_new_credit_note_if_overpaying(self):
@ -141,7 +141,7 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
# There should be one credit note generated out of the invoice. # There should be one credit note generated out of the invoice.
cn = self._credit_note_for_invoice(invoice.invoice) # noqa 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 # Create a new invoice for a cart of half value of inv 1
invoice2 = self._invoice_containing_prod_1(1) invoice2 = self._invoice_containing_prod_1(1)
@ -150,7 +150,7 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
# We generated a new credit note, and spent the old one, # We generated a new credit note, and spent the old one,
# unclaimed should still be 1. # 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( credit_note2 = commerce.CreditNote.objects.get(
invoice=invoice2.invoice, invoice=invoice2.invoice,
@ -158,7 +158,7 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
# The new credit note should be the residual of the cost of cart 1 # The new credit note should be the residual of the cost of cart 1
# minus the cost of cart 2. # minus the cost of cart 2.
self.assertEquals( self.assertEqual(
invoice.invoice.value - invoice2.invoice.value, invoice.invoice.value - invoice2.invoice.value,
credit_note2.value, credit_note2.value,
) )
@ -210,14 +210,14 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
invoice.refund() 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 = self._credit_note_for_invoice(invoice.invoice)
cn.refund() cn.refund()
# Refunding a credit note should mark it as claimed # 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 # Create a new cart with invoice
cart = TestingCartController.for_user(self.USER_1) cart = TestingCartController.for_user(self.USER_1)
@ -238,7 +238,7 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
invoice.refund() 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 = self._credit_note_for_invoice(invoice.invoice)
@ -251,7 +251,7 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
# Creating `invoice_2` will automatically apply `cn`. # Creating `invoice_2` will automatically apply `cn`.
cn.apply_to_invoice(invoice_2.invoice) 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. # Cannot refund this credit note as it is already applied.
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
@ -327,13 +327,13 @@ class CreditNoteTestCase(TestHelperMixin, RegistrationCartTestCase):
invoice._refresh() invoice._refresh()
# The first invoice should be refunded # The first invoice should be refunded
self.assertEquals( self.assertEqual(
commerce.Invoice.STATUS_VOID, commerce.Invoice.STATUS_VOID,
invoice.invoice.status, invoice.invoice.status,
) )
# Both credit notes should be for the same amount # Both credit notes should be for the same amount
self.assertEquals( self.assertEqual(
cn.credit_note.value, cn.credit_note.value,
cn2.credit_note.value, cn2.credit_note.value,
) )

View file

@ -98,7 +98,8 @@ class InvoiceTestCase(TestHelperMixin, RegistrationCartTestCase):
def test_total_payments_balance_due(self): def test_total_payments_balance_due(self):
invoice = self._invoice_containing_prod_1(2) 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( self.assertTrue(
i + 1, invoice.invoice.total_payments() i + 1, invoice.invoice.total_payments()
) )
@ -175,7 +176,7 @@ class InvoiceTestCase(TestHelperMixin, RegistrationCartTestCase):
invoice_1.refund() invoice_1.refund()
cart.refresh_from_db() 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): def test_invoice_voids_self_if_cart_changes(self):
current_cart = TestingCartController.for_user(self.USER_1) current_cart = TestingCartController.for_user(self.USER_1)
@ -322,12 +323,12 @@ class InvoiceTestCase(TestHelperMixin, RegistrationCartTestCase):
) )
inv = TestingInvoiceController(_invoice) inv = TestingInvoiceController(_invoice)
self.assertEquals( self.assertEqual(
inv.invoice.value, inv.invoice.value,
sum(i[1] for i in description_price_pairs) sum(i[1] for i in description_price_pairs)
) )
self.assertEquals( self.assertEqual(
len(inv.invoice.lineitem_set.all()), len(inv.invoice.lineitem_set.all()),
len(description_price_pairs) len(description_price_pairs)
) )
@ -336,37 +337,37 @@ class InvoiceTestCase(TestHelperMixin, RegistrationCartTestCase):
def test_sends_email_on_invoice_creation(self): def test_sends_email_on_invoice_creation(self):
invoice = self._invoice_containing_prod_1(1) invoice = self._invoice_containing_prod_1(1)
self.assertEquals(1, len(self.emails)) self.assertEqual(1, len(self.emails))
email = self.emails[0] email = self.emails[0]
self.assertEquals([self.USER_1.email], email["to"]) self.assertEqual([self.USER_1.email], email["to"])
self.assertEquals("invoice_created", email["kind"]) self.assertEqual("invoice_created", email["kind"])
self.assertEquals(invoice.invoice, email["context"]["invoice"]) self.assertEqual(invoice.invoice, email["context"]["invoice"])
def test_sends_first_change_email_on_invoice_fully_paid(self): def test_sends_first_change_email_on_invoice_fully_paid(self):
invoice = self._invoice_containing_prod_1(1) 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) invoice.pay("Partial", invoice.invoice.value - 1)
# Should have an "invoice_created" email and nothing else. # 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) invoice.pay("Remainder", 1)
self.assertEquals(2, len(self.emails)) self.assertEqual(2, len(self.emails))
email = self.emails[1] email = self.emails[1]
self.assertEquals([self.USER_1.email], email["to"]) self.assertEqual([self.USER_1.email], email["to"])
self.assertEquals("invoice_updated", email["kind"]) self.assertEqual("invoice_updated", email["kind"])
self.assertEquals(invoice.invoice, email["context"]["invoice"]) self.assertEqual(invoice.invoice, email["context"]["invoice"])
def test_sends_email_when_invoice_refunded(self): def test_sends_email_when_invoice_refunded(self):
invoice = self._invoice_containing_prod_1(1) 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) invoice.pay("Payment", invoice.invoice.value)
self.assertEquals(2, len(self.emails)) self.assertEqual(2, len(self.emails))
invoice.refund() invoice.refund()
self.assertEquals(3, len(self.emails)) self.assertEqual(3, len(self.emails))
email = self.emails[2] email = self.emails[2]
self.assertEquals([self.USER_1.email], email["to"]) self.assertEqual([self.USER_1.email], email["to"])
self.assertEquals("invoice_updated", email["kind"]) self.assertEqual("invoice_updated", email["kind"])
self.assertEquals(invoice.invoice, email["context"]["invoice"]) self.assertEqual(invoice.invoice, email["context"]["invoice"])

View file

@ -67,7 +67,7 @@ class SpeakerTestCase(RegistrationCartTestCase):
kind=kind_1, kind=kind_1,
title="Proposal 1", title="Proposal 1",
abstract="Abstract", abstract="Abstract",
description="Description", #description="Description",
speaker=speaker_1, speaker=speaker_1,
) )
proposal_models.AdditionalSpeaker.objects.create( proposal_models.AdditionalSpeaker.objects.create(
@ -80,7 +80,7 @@ class SpeakerTestCase(RegistrationCartTestCase):
kind=kind_2, kind=kind_2,
title="Proposal 2", title="Proposal 2",
abstract="Abstract", abstract="Abstract",
description="Description", #description="Description",
speaker=speaker_1, speaker=speaker_1,
) )
proposal_models.AdditionalSpeaker.objects.create( proposal_models.AdditionalSpeaker.objects.create(

View file

@ -28,7 +28,7 @@ from django import forms as django_forms
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test 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.contrib import messages
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -50,6 +50,8 @@ from registrasion.contrib.badger import (
InvalidTicketChoiceError InvalidTicketChoiceError
) )
User = get_user_model()
_GuidedRegistrationSection = namedtuple( _GuidedRegistrationSection = namedtuple(
"GuidedRegistrationSection", "GuidedRegistrationSection",
( (

View file

@ -1,3 +1,3 @@
django-nested-admin==2.2.6 django-nested-admin==3.3.2
#symposion==1.0b2.dev3 #symposion==1.0b2.dev3
lxml==4.0.0 lxml==4.6.1

View file

@ -1,15 +1,13 @@
import copy import copy
from registripe import models from registripe import models
from django import forms
from django.core.urlresolvers import reverse
import functools import functools
from django import forms from django import forms
from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db.models import F, Q from django.db.models import F, Q
from django.forms import widgets from django.forms import widgets
from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django_countries import countries from django_countries import countries
@ -21,7 +19,7 @@ from pinax.stripe import models as pinax_stripe_models
class StripeCardElement(forms.widgets.TextInput): class StripeCardElement(forms.widgets.TextInput):
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None, renderer=None):
element = ''' element = '''
<div class="registrasion-stripe-element" id='%s' style='"-moz-appearance: textfield; -webkit-appearance: textfield; appearance: field;"'>Please wait.</div>''' % (name, ) <div class="registrasion-stripe-element" id='%s' style='"-moz-appearance: textfield; -webkit-appearance: textfield; appearance: field;"'>Please wait.</div>''' % (name, )
@ -36,7 +34,7 @@ class StripeCardElement(forms.widgets.TextInput):
class StripeTokenWidget(forms.widgets.HiddenInput): class StripeTokenWidget(forms.widgets.HiddenInput):
def render(self, name, value, attrs=None): def render(self, name, value, attrs=None, renderer=None):
return ''' return '''
<div class='registrasion-stripe-token' style='display:none;' <div class='registrasion-stripe-token' style='display:none;'

View file

@ -7,8 +7,8 @@ from pinax.stripe.models import Charge
class StripePayment(commerce.PaymentBase): class StripePayment(commerce.PaymentBase):
charge = models.ForeignKey(Charge) charge = models.ForeignKey(Charge, on_delete=models.CASCADE)
class StripeCreditNoteRefund(commerce.CreditNoteRefund): class StripeCreditNoteRefund(commerce.CreditNoteRefund):
charge = models.ForeignKey(Charge) charge = models.ForeignKey(Charge, on_delete=models.CASCADE)

View file

@ -1,5 +1,5 @@
{% comment %} {% 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 %} {% endcomment %}
{% block content %} {% block content %}

View file

@ -1,4 +1,4 @@
django-countries>=4.0 django-countries>=6.1.3
pinax-stripe==3.2.1 pinax-stripe==4.4.0
requests>=2.11.1 requests>=2.24.0
stripe==1.38.0 stripe==2.55.0

View file

@ -49,7 +49,11 @@ class Section(models.Model):
scheduling process. 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) name = models.CharField(_("Name"), max_length=100)
slug = models.SlugField(verbose_name=_("Slug")) slug = models.SlugField(verbose_name=_("Slug"))

View file

@ -2,7 +2,9 @@ from django.http import Http404
from django.shortcuts import render from django.shortcuts import render
from django.contrib.auth.decorators import login_required 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 @login_required

View file

@ -1,13 +1,13 @@
import os import os
import uuid import uuid
from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.db.models import Q from django.db.models import Q
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.utils.timezone import now 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 ObjectDoesNotExist
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -19,6 +19,8 @@ from symposion.text_parser import parse
from symposion.conference.models import Section from symposion.conference.models import Section
from symposion.speakers.models import Speaker from symposion.speakers.models import Speaker
User = get_user_model()
class ProposalSection(models.Model): class ProposalSection(models.Model):
""" """
@ -30,7 +32,11 @@ class ProposalSection(models.Model):
* closed is NULL or False * 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")) start = models.DateTimeField(null=True, blank=True, verbose_name=_("Start"))
end = models.DateTimeField(null=True, blank=True, verbose_name=_("End")) end = models.DateTimeField(null=True, blank=True, verbose_name=_("End"))
@ -66,7 +72,12 @@ class ProposalKind(models.Model):
to distinguish the section as well as the kind. 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) name = models.CharField(_("Name"), max_length=100)
slug = models.SlugField(verbose_name=_("Slug")) slug = models.SlugField(verbose_name=_("Slug"))
@ -79,7 +90,11 @@ class ProposalBase(models.Model):
objects = InheritanceManager() 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")) title = models.CharField(max_length=100, verbose_name=_("Title"))
abstract = models.TextField( abstract = models.TextField(
@ -134,7 +149,12 @@ class ProposalBase(models.Model):
editable=False, editable=False,
verbose_name=_("Submitted") 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 # @@@ 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 # M2M field but that is no longer supported by Django. Should be moved to
@ -213,8 +233,16 @@ class AdditionalSpeaker(models.Model):
(SPEAKING_STATUS_DECLINED, _("Declined")), (SPEAKING_STATUS_DECLINED, _("Declined")),
] ]
speaker = models.ForeignKey(Speaker, verbose_name=_("Speaker")) speaker = models.ForeignKey(
proposalbase = models.ForeignKey(ProposalBase, verbose_name=_("Proposalbase")) 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")) status = models.IntegerField(choices=SPEAKING_STATUS, default=SPEAKING_STATUS_PENDING, verbose_name=_("Status"))
class Meta: class Meta:
@ -239,9 +267,18 @@ def uuid_filename(instance, filename):
class SupportingDocument(models.Model): 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")) created_at = models.DateTimeField(default=now, verbose_name=_("Created at"))

View file

@ -5,14 +5,14 @@ import sys
from django.conf import settings from django.conf import settings
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.core.urlresolvers import reverse
from django.db.models import Q from django.db.models import Q
from django.http import Http404, HttpResponse, HttpResponseForbidden from django.http import Http404, HttpResponse, HttpResponseForbidden
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse
from django.views import static from django.views import static
from django.contrib import messages 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 django.utils.translation import ugettext_lazy as _
@ -27,6 +27,8 @@ from symposion.proposals.forms import (
AddSpeakerForm, SupportingDocumentCreateForm AddSpeakerForm, SupportingDocumentCreateForm
) )
User = get_user_model()
def get_form(name): def get_form(name):
dot = name.rindex(".") dot = name.rindex(".")
@ -36,7 +38,7 @@ def get_form(name):
def proposal_submit(request): def proposal_submit(request):
if not request.user.is_authenticated(): if not request.user.is_authenticated:
messages.info(request, _("To submit a proposal, please " messages.info(request, _("To submit a proposal, please "
"<a href='{0}'>log in</a> and create a speaker profile " "<a href='{0}'>log in</a> and create a speaker profile "
"via the dashboard.".format(settings.LOGIN_URL))) "via the dashboard.".format(settings.LOGIN_URL)))

View file

@ -10,7 +10,7 @@ from django.db.models import Case, When, Value
from django.db.models import Count from django.db.models import Count
from django.db.models.signals import post_save 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 django.utils.translation import ugettext_lazy as _
from symposion import constants from symposion import constants
@ -18,6 +18,8 @@ from symposion.text_parser import parse
from symposion.proposals.models import ProposalBase from symposion.proposals.models import ProposalBase
from symposion.schedule.models import Presentation from symposion.schedule.models import Presentation
User = get_user_model()
class Votes(object): class Votes(object):
ABSTAIN = "0" ABSTAIN = "0"
@ -51,8 +53,16 @@ class ReviewAssignment(models.Model):
(AUTO_ASSIGNED_LATER, _("auto-assigned, later")), (AUTO_ASSIGNED_LATER, _("auto-assigned, later")),
] ]
proposal = models.ForeignKey(ProposalBase, verbose_name=_("Proposal")) proposal = models.ForeignKey(
user = models.ForeignKey(User, verbose_name=_("User")) 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")) origin = models.IntegerField(choices=ORIGIN_CHOICES, verbose_name=_("Origin"))
@ -91,8 +101,17 @@ class ReviewAssignment(models.Model):
class ProposalMessage(models.Model): class ProposalMessage(models.Model):
proposal = models.ForeignKey(ProposalBase, related_name="messages", verbose_name=_("Proposal")) proposal = models.ForeignKey(
user = models.ForeignKey(User, verbose_name=_("User")) 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 = models.TextField(verbose_name=_("Message"))
message_html = models.TextField(blank=True) message_html = models.TextField(blank=True)
@ -111,8 +130,17 @@ class ProposalMessage(models.Model):
class Review(models.Model): class Review(models.Model):
VOTES = VOTES VOTES = VOTES
proposal = models.ForeignKey(ProposalBase, related_name="reviews", verbose_name=_("Proposal")) proposal = models.ForeignKey(
user = models.ForeignKey(User, verbose_name=_("User")) 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 # No way to encode "-0" vs. "+0" into an IntegerField, and I don't feel
# like some complicated encoding system. # like some complicated encoding system.
@ -208,8 +236,17 @@ class Review(models.Model):
class LatestVote(models.Model): class LatestVote(models.Model):
VOTES = VOTES VOTES = VOTES
proposal = models.ForeignKey(ProposalBase, related_name="votes", verbose_name=_("Proposal")) proposal = models.ForeignKey(
user = models.ForeignKey(User, verbose_name=_("User")) 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 # No way to encode "-0" vs. "+0" into an IntegerField, and I don't feel
# like some complicated encoding system. # like some complicated encoding system.
@ -232,7 +269,12 @@ class LatestVote(models.Model):
class ProposalResult(models.Model): 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")) 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")) comment_count = models.PositiveIntegerField(default=0, verbose_name=_("Comment count"))
# vote_count only counts non-abstain votes. # vote_count only counts non-abstain votes.
@ -297,8 +339,17 @@ class ProposalResult(models.Model):
class Comment(models.Model): class Comment(models.Model):
proposal = models.ForeignKey(ProposalBase, related_name="comments", verbose_name=_("Proposal")) proposal = models.ForeignKey(
commenter = models.ForeignKey(User, verbose_name=_("Commenter")) 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 = models.TextField(verbose_name=_("Text"))
text_html = models.TextField(blank=True) text_html = models.TextField(blank=True)
@ -329,9 +380,19 @@ class NotificationTemplate(models.Model):
class ResultNotification(models.Model): class ResultNotification(models.Model):
proposal = models.ForeignKey(ProposalBase, related_name="notifications", verbose_name=_("Proposal")) proposal = models.ForeignKey(
template = models.ForeignKey(NotificationTemplate, null=True, blank=True, ProposalBase,
on_delete=models.SET_NULL, verbose_name=_("Template")) 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")) timestamp = models.DateTimeField(default=datetime.now, verbose_name=_("Timestamp"))
to_address = models.EmailField(verbose_name=_("To address")) to_address = models.EmailField(verbose_name=_("To address"))
from_address = models.EmailField(verbose_name=_("From address")) from_address = models.EmailField(verbose_name=_("From address"))

View file

@ -6,7 +6,7 @@ from symposion.reviews.models import ReviewAssignment
register = template.Library() register = template.Library()
@register.assignment_tag(takes_context=True) @register.simple_tag(takes_context=True)
def review_assignments(context): def review_assignments(context):
request = context["request"] request = context["request"]
assignments = ReviewAssignment.objects.filter(user=request.user) assignments = ReviewAssignment.objects.filter(user=request.user)

View file

@ -268,6 +268,8 @@ def review_list(request, section_slug, user_pk):
if not request.user.pk == user_pk: if not request.user.pk == user_pk:
return access_not_permitted(request) return access_not_permitted(request)
section = get_object_or_404(ProposalSection, section__slug=section_slug)
queryset = ProposalBase.objects.select_related("speaker__user", "result") queryset = ProposalBase.objects.select_related("speaker__user", "result")
reviewed = LatestVote.objects.filter(user__pk=user_pk).values_list("proposal", flat=True) reviewed = LatestVote.objects.filter(user__pk=user_pk).values_list("proposal", flat=True)
queryset = queryset.filter(kind__section__slug=section_slug) queryset = queryset.filter(kind__section__slug=section_slug)
@ -280,6 +282,7 @@ def review_list(request, section_slug, user_pk):
ctx = { ctx = {
"proposals": proposals, "proposals": proposals,
"section": section,
} }
return render(request, "symposion/reviews/review_list.html", ctx) return render(request, "symposion/reviews/review_list.html", ctx)

View file

@ -73,6 +73,8 @@ class ScheduleSectionForm(forms.Form):
if 'encoding' in kwargs: if 'encoding' in kwargs:
self.encoding = kwargs['encoding'] self.encoding = kwargs['encoding']
kwargs.pop('encoding') kwargs.pop('encoding')
else:
self.encoding = "utf-8"
super(ScheduleSectionForm, self).__init__(*args, **kwargs) super(ScheduleSectionForm, self).__init__(*args, **kwargs)
def clean_filename(self): def clean_filename(self):

View file

@ -1,7 +1,7 @@
import datetime import datetime
from django.core.exceptions import ObjectDoesNotExist 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.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
@ -11,11 +11,17 @@ from symposion.proposals.models import ProposalBase
from symposion.conference.models import Section from symposion.conference.models import Section
from symposion.speakers.models import Speaker from symposion.speakers.models import Speaker
User = get_user_model()
class Schedule(models.Model): class Schedule(models.Model):
objects = DefaultSelectRelatedManager() 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")) published = models.BooleanField(default=True, verbose_name=_("Published"))
hidden = models.BooleanField(_("Hide schedule from overall conference view"), default=False) hidden = models.BooleanField(_("Hide schedule from overall conference view"), default=False)
@ -35,7 +41,11 @@ class Day(models.Model):
objects = DefaultSelectRelatedManager() 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")) date = models.DateField(verbose_name=_("Date"))
def __str__(self): def __str__(self):
@ -53,7 +63,11 @@ class Day(models.Model):
class Room(models.Model): 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")) name = models.CharField(max_length=65, verbose_name=_("Name"))
order = models.PositiveIntegerField(verbose_name=_("Order")) order = models.PositiveIntegerField(verbose_name=_("Order"))
@ -70,8 +84,8 @@ class Room(models.Model):
class Track(models.Model): class Track(models.Model):
name = models.CharField(max_length=80, verbose_name=_("Track")) name = models.CharField(max_length=80, verbose_name=_("Track"))
room = models.ForeignKey(Room) room = models.ForeignKey(Room, on_delete=models.CASCADE)
day = models.ForeignKey(Day) day = models.ForeignKey(Day, on_delete=models.CASCADE)
def __str__(self): def __str__(self):
return self.name return self.name
@ -88,7 +102,11 @@ class SlotKind(models.Model):
break, lunch, or X-minute talk. 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")) label = models.CharField(max_length=50, verbose_name=_("Label"))
def __str__(self): def __str__(self):
@ -103,8 +121,16 @@ class Slot(models.Model):
objects = DefaultSelectRelatedManager() objects = DefaultSelectRelatedManager()
name = models.CharField(max_length=512, editable=False) name = models.CharField(max_length=512, editable=False)
day = models.ForeignKey(Day, verbose_name=_("Day")) day = models.ForeignKey(
kind = models.ForeignKey(SlotKind, verbose_name=_("Kind")) 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")) start = models.TimeField(verbose_name=_("Start"))
end = models.TimeField(verbose_name=_("End")) end = models.TimeField(verbose_name=_("End"))
exclusive = models.BooleanField( exclusive = models.BooleanField(
@ -194,8 +220,16 @@ class SlotRoom(models.Model):
Links a slot with a room. Links a slot with a room.
""" """
slot = models.ForeignKey(Slot, verbose_name=_("Slot")) slot = models.ForeignKey(
room = models.ForeignKey(Room, verbose_name=_("Room")) Slot,
verbose_name=_("Slot"),
on_delete=models.CASCADE,
)
room = models.ForeignKey(
Room,
verbose_name=_("Room"),
on_delete=models.CASCADE,
)
def __str__(self): def __str__(self):
return "%s %s" % (self.room, self.slot) return "%s %s" % (self.room, self.slot)
@ -210,11 +244,23 @@ class SlotRoom(models.Model):
class Presentation(models.Model): class Presentation(models.Model):
objects = DefaultSelectRelatedManager() 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")) title = models.CharField(max_length=100, verbose_name=_("Title"))
abstract = models.TextField(verbose_name=_("Abstract")) abstract = models.TextField(verbose_name=_("Abstract"))
abstract_html = models.TextField(blank=True) 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", additional_speakers = models.ManyToManyField(Speaker, related_name="copresentations",
blank=True, verbose_name=_("Additional speakers")) blank=True, verbose_name=_("Additional speakers"))
unpublish = models.BooleanField( unpublish = models.BooleanField(
@ -222,8 +268,18 @@ class Presentation(models.Model):
verbose_name=_("Do not publish"), verbose_name=_("Do not publish"),
) )
cancelled = models.BooleanField(default=False, verbose_name=_("Cancelled")) cancelled = models.BooleanField(default=False, verbose_name=_("Cancelled"))
proposal_base = models.OneToOneField(ProposalBase, related_name="presentation", verbose_name=_("Proposal base")) proposal_base = models.OneToOneField(
section = models.ForeignKey(Section, related_name="presentations", verbose_name=_("Section")) 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): def save(self, *args, **kwargs):
self.abstract_html = parse(self.abstract) self.abstract_html = parse(self.abstract)
@ -256,7 +312,12 @@ class Presentation(models.Model):
class Session(models.Model): 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")) slots = models.ManyToManyField(Slot, related_name="sessions", verbose_name=_("Slots"))
def sorted_slots(self): def sorted_slots(self):
@ -318,8 +379,16 @@ class SessionRole(models.Model):
(SESSION_ROLE_RUNNER, _("Session Runner")), (SESSION_ROLE_RUNNER, _("Session Runner")),
] ]
session = models.ForeignKey(Session, verbose_name=_("Session")) session = models.ForeignKey(
user = models.ForeignKey(User, verbose_name=_("User")) 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")) role = models.IntegerField(choices=SESSION_ROLE_TYPES, verbose_name=_("Role"))
status = models.NullBooleanField(verbose_name=_("Status")) status = models.NullBooleanField(verbose_name=_("Status"))

View file

@ -9,7 +9,7 @@ from symposion.schedule.models import Schedule, Day, Slot, SlotKind
from symposion.conference.models import Section, Conference from symposion.conference.models import Section, Conference
class ConferenceFactory(factory.DjangoModelFactory): class ConferenceFactory(factory.django.DjangoModelFactory):
title = fuzzy.FuzzyText() title = fuzzy.FuzzyText()
start_date = fuzzy.FuzzyDate(datetime.date(2014, 1, 1)) start_date = fuzzy.FuzzyDate(datetime.date(2014, 1, 1))
end_date = fuzzy.FuzzyDate( end_date = fuzzy.FuzzyDate(
@ -21,7 +21,7 @@ class ConferenceFactory(factory.DjangoModelFactory):
model = Conference model = Conference
class SectionFactory(factory.DjangoModelFactory): class SectionFactory(factory.django.DjangoModelFactory):
conference = factory.SubFactory(ConferenceFactory) conference = factory.SubFactory(ConferenceFactory)
name = fuzzy.FuzzyText() name = fuzzy.FuzzyText()
slug = fuzzy.FuzzyText() slug = fuzzy.FuzzyText()
@ -30,7 +30,7 @@ class SectionFactory(factory.DjangoModelFactory):
model = Section model = Section
class ScheduleFactory(factory.DjangoModelFactory): class ScheduleFactory(factory.django.DjangoModelFactory):
section = factory.SubFactory(SectionFactory) section = factory.SubFactory(SectionFactory)
published = True published = True
hidden = False hidden = False
@ -39,7 +39,7 @@ class ScheduleFactory(factory.DjangoModelFactory):
model = Schedule model = Schedule
class SlotKindFactory(factory.DjangoModelFactory): class SlotKindFactory(factory.django.DjangoModelFactory):
schedule = factory.SubFactory(ScheduleFactory) schedule = factory.SubFactory(ScheduleFactory)
label = fuzzy.FuzzyText() label = fuzzy.FuzzyText()
@ -47,7 +47,7 @@ class SlotKindFactory(factory.DjangoModelFactory):
model = SlotKind model = SlotKind
class DayFactory(factory.DjangoModelFactory): class DayFactory(factory.django.DjangoModelFactory):
schedule = factory.SubFactory(ScheduleFactory) schedule = factory.SubFactory(ScheduleFactory)
date = fuzzy.FuzzyDate(datetime.date(2014, 1, 1)) date = fuzzy.FuzzyDate(datetime.date(2014, 1, 1))
@ -55,7 +55,7 @@ class DayFactory(factory.DjangoModelFactory):
model = Day model = Day
class SlotFactory(factory.DjangoModelFactory): class SlotFactory(factory.django.DjangoModelFactory):
day = factory.SubFactory(DayFactory) day = factory.SubFactory(DayFactory)
kind = factory.SubFactory(SlotKindFactory) kind = factory.SubFactory(SlotKindFactory)
start = datetime.time(random.randint(0, 23), random.randint(0, 59)) start = datetime.time(random.randint(0, 23), random.randint(0, 59))

View file

@ -1,13 +1,15 @@
from datetime import date from datetime import date
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse from django.urls import reverse
from django.test import TestCase from django.test import TestCase
from symposion.conference.models import Section, current_conference, Conference from symposion.conference.models import Section, current_conference, Conference
from symposion.schedule.models import Day, Schedule, Session from symposion.schedule.models import Day, Schedule, Session
User = get_user_model()
class TestScheduleViews(TestCase): class TestScheduleViews(TestCase):
username = "user@example.com" username = "user@example.com"

View file

@ -1,13 +1,13 @@
import json import json
import pytz import pytz
from django.core.urlresolvers import reverse
from django.http import Http404, HttpResponse from django.http import Http404, HttpResponse
from django.shortcuts import render, get_object_or_404, redirect from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse
from django.template import loader, Context from django.template import loader, Context
from django.conf import settings 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 import messages
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
@ -21,6 +21,8 @@ from symposion.schedule.timetable import TimeTable
from symposion.conference.models import Conference from symposion.conference.models import Conference
from pinaxcon.templatetags.lca2018_tags import speaker_photo from pinaxcon.templatetags.lca2018_tags import speaker_photo
User = get_user_model()
def fetch_schedule(slug): def fetch_schedule(slug):
qs = Schedule.objects.all() qs = Schedule.objects.all()
@ -369,7 +371,7 @@ def session_detail(request, session_id):
if chairs: if chairs:
chair = chairs[0].user chair = chairs[0].user
else: else:
if request.user.is_authenticated(): if request.user.is_authenticated:
# did the current user previously try to apply and got rejected? # 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): if SessionRole.objects.filter(session=session, user=request.user, role=SessionRole.SESSION_ROLE_CHAIR, status=False):
chair_denied = True chair_denied = True
@ -380,12 +382,12 @@ def session_detail(request, session_id):
if runners: if runners:
runner = runners[0].user runner = runners[0].user
else: else:
if request.user.is_authenticated(): if request.user.is_authenticated:
# did the current user previously try to apply and got rejected? # 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): if SessionRole.objects.filter(session=session, user=request.user, role=SessionRole.SESSION_ROLE_RUNNER, status=False):
runner_denied = True 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: if not hasattr(request.user, "attendee") or not request.user.attendee.completed_registration:
response = redirect("guided_registration") response = redirect("guided_registration")
response["Location"] += "?next=%s" % request.path response["Location"] += "?next=%s" % request.path

View file

@ -1,18 +1,26 @@
import datetime import datetime
from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _ 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 import constants
from symposion.text_parser import parse from symposion.text_parser import parse
User = get_user_model()
class Speaker(models.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, name = models.CharField(verbose_name=_("Name"), max_length=100,
help_text=_("As you would like it to appear in the" help_text=_("As you would like it to appear in the"
" conference programme.")) " conference programme."))

View file

@ -4,13 +4,15 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required 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 django.utils.translation import ugettext_lazy as _
from symposion.proposals.models import ProposalBase from symposion.proposals.models import ProposalBase
from symposion.speakers.forms import SpeakerForm from symposion.speakers.forms import SpeakerForm
from symposion.speakers.models import Speaker from symposion.speakers.models import Speaker
User = get_user_model()
@login_required @login_required
def speaker_create(request): def speaker_create(request):

View file

@ -2,16 +2,18 @@ import datetime
from django.conf import settings from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.db import models from django.db import models
from django.db.models.signals import post_init, post_save 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.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.conference.models import Conference
from symposion.sponsorship.managers import SponsorManager from symposion.sponsorship.managers import SponsorManager
User = get_user_model()
# The benefits we track as individual fields on sponsors # The benefits we track as individual fields on sponsors
# Names are the names in the database as defined by organizers. # Names are the names in the database as defined by organizers.
@ -44,7 +46,11 @@ BENEFITS = [
class SponsorLevel(models.Model): 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) name = models.CharField(_("Name"), max_length=100)
order = models.IntegerField(_("Order"), default=0) order = models.IntegerField(_("Order"), default=0)
cost = models.PositiveIntegerField(_("Cost")) cost = models.PositiveIntegerField(_("Cost"))
@ -64,8 +70,13 @@ class SponsorLevel(models.Model):
class Sponsor(models.Model): class Sponsor(models.Model):
applicant = models.ForeignKey(User, related_name="sponsorships", verbose_name=_("Applicant"), applicant = models.ForeignKey(
null=True) User,
related_name="sponsorships",
verbose_name=_("Applicant"),
null=True,
on_delete=models.CASCADE,
)
name = models.CharField(_("Sponsor Name"), max_length=100) name = models.CharField(_("Sponsor Name"), max_length=100)
display_url = models.URLField(_("display URL"), blank=True) display_url = models.URLField(_("display URL"), blank=True)
@ -73,13 +84,24 @@ class Sponsor(models.Model):
annotation = models.TextField(_("Annotation"), blank=True) annotation = models.TextField(_("Annotation"), blank=True)
contact_name = models.CharField(_("Contact Name"), max_length=100) contact_name = models.CharField(_("Contact Name"), max_length=100)
contact_email = models.EmailField(_("Contact Email")) 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) added = models.DateTimeField(_("added"), default=datetime.datetime.now)
active = models.BooleanField(_("active"), default=False) active = models.BooleanField(_("active"), default=False)
# Denormalization (this assumes only one logo) # Denormalization (this assumes only one logo)
sponsor_logo = models.ForeignKey("SponsorBenefit", related_name="+", null=True, blank=True, sponsor_logo = models.ForeignKey(
editable=False, verbose_name=_("Sponsor logo")) "SponsorBenefit",
related_name="+",
null=True,
blank=True,
editable=False,
verbose_name=_("Sponsor logo"),
on_delete=models.CASCADE,
)
# Whether things are complete # Whether things are complete
# True = complete, False = incomplate, Null = n/a for this sponsor level # True = complete, False = incomplate, Null = n/a for this sponsor level
@ -242,8 +264,18 @@ class Benefit(models.Model):
class BenefitLevel(models.Model): class BenefitLevel(models.Model):
benefit = models.ForeignKey(Benefit, related_name="benefit_levels", verbose_name=_("Benefit")) benefit = models.ForeignKey(
level = models.ForeignKey(SponsorLevel, related_name="benefit_levels", verbose_name=_("Level")) 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 # default limits for this benefit at given level
max_words = models.PositiveIntegerField(_("Max words"), blank=True, null=True) max_words = models.PositiveIntegerField(_("Max words"), blank=True, null=True)
@ -260,8 +292,18 @@ class BenefitLevel(models.Model):
class SponsorBenefit(models.Model): class SponsorBenefit(models.Model):
sponsor = models.ForeignKey(Sponsor, related_name="sponsor_benefits", verbose_name=_("Sponsor")) sponsor = models.ForeignKey(
benefit = models.ForeignKey(Benefit, related_name="sponsor_benefits", verbose_name=_("Benefit")) 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")) active = models.BooleanField(default=True, verbose_name=_("Active"))
# Limits: will initially be set to defaults from corresponding BenefitLevel # Limits: will initially be set to defaults from corresponding BenefitLevel

View file

@ -1,20 +1,22 @@
from cStringIO import StringIO from io import StringIO
import os import os
import shutil import shutil
import tempfile import tempfile
from zipfile import ZipFile from zipfile import ZipFile
from django.conf import settings 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.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from django.urls import reverse
from pycon.sponsorship.models import Benefit, Sponsor, SponsorBenefit,\ from pycon.sponsorship.models import Benefit, Sponsor, SponsorBenefit,\
SponsorLevel SponsorLevel
from symposion.conference.models import current_conference from symposion.conference.models import current_conference
User = get_user_model()
class TestSponsorZipDownload(TestCase): class TestSponsorZipDownload(TestCase):
def setUp(self): def setUp(self):

View file

@ -5,7 +5,7 @@ from .models import Team
class TeamPermissionsBackend(object): class TeamPermissionsBackend(object):
def authenticate(self, username=None, password=None): def authenticate(self, request, username=None, password=None):
return None return None
def get_team_permissions(self, user_obj, obj=None): def get_team_permissions(self, user_obj, obj=None):
@ -13,7 +13,7 @@ class TeamPermissionsBackend(object):
Returns a set of permission strings that this user has through his/her Returns a set of permission strings that this user has through his/her
team memberships. team memberships.
""" """
if user_obj.is_anonymous() or obj is not None: if user_obj.is_anonymous or obj is not None:
return set() return set()
if not hasattr(user_obj, "_team_perm_cache"): if not hasattr(user_obj, "_team_perm_cache"):
# Member permissions # Member permissions

View file

@ -4,10 +4,12 @@ from django.utils.html import escape
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _ 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 from symposion.teams.models import Membership
User = get_user_model()
class TeamInvitationForm(forms.Form): class TeamInvitationForm(forms.Form):

View file

@ -2,6 +2,7 @@
from django.db import models, migrations from django.db import models, migrations
import datetime import datetime
from django.conf import settings from django.conf import settings
import django.db.models.deletion
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -44,11 +45,11 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='membership', model_name='membership',
name='team', 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( migrations.AddField(
model_name='membership', model_name='membership',
name='user', 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'),
), ),
] ]

View file

@ -2,11 +2,15 @@ import datetime
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ 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 from reversion import revisions as reversion
User = get_user_model()
TEAM_ACCESS_CHOICES = [ TEAM_ACCESS_CHOICES = [
("open", _("open")), ("open", _("open")),
@ -36,9 +40,8 @@ class Team(models.Model):
created = models.DateTimeField(default=datetime.datetime.now, created = models.DateTimeField(default=datetime.datetime.now,
editable=False, verbose_name=_("Created")) editable=False, verbose_name=_("Created"))
@models.permalink
def get_absolute_url(self): def get_absolute_url(self):
return ("team_detail", [self.slug]) return reverse("team_detail", args=[self.slug])
def __str__(self): def __str__(self):
return self.name return self.name
@ -78,10 +81,18 @@ MEMBERSHIP_STATE_CHOICES = [
class Membership(models.Model): class Membership(models.Model):
user = models.ForeignKey(User, related_name="memberships", user = models.ForeignKey(
verbose_name=_("User")) User,
team = models.ForeignKey(Team, related_name="memberships", related_name="memberships",
verbose_name=_("Team")) 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, state = models.CharField(max_length=20, choices=MEMBERSHIP_STATE_CHOICES,
verbose_name=_("State")) verbose_name=_("State"))
message = models.TextField(blank=True, verbose_name=_("Message")) message = models.TextField(blank=True, verbose_name=_("Message"))

View file

@ -9,7 +9,7 @@ from django.contrib.sites.models import Site
class Sender(object): 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): def __init__(self, template_prefix):
self.template_prefix = template_prefix self.template_prefix = template_prefix