diff --git a/README.md b/README.md
index 1c0e78d5b..318afa478 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ README
======
This project (and its dependencies) contains the EuroPython website source code.
-The code is used for the EuroPython 2019 website.
+The code is used for the EuroPython 2020 website.
LICENSE
=======
diff --git a/assopy/stats.py b/assopy/stats.py
index 568c272ba..73da8b09e 100644
--- a/assopy/stats.py
+++ b/assopy/stats.py
@@ -192,7 +192,7 @@ def _calc_prices(order_id, items):
# Replace prices dicts with sorted lists
for code in tcp.keys():
prices_list = [entry
- for price, entry in sorted(list(tcp[code]['prices'].items()),
+ for price, entry in sorted(tcp[code]['prices'].items(),
reverse=True)]
tcp[code]['prices'] = prices_list
# Create list sorted by fare code
diff --git a/conference/admin.py b/conference/admin.py
index 76c2db9cb..1eb3a1636 100644
--- a/conference/admin.py
+++ b/conference/admin.py
@@ -138,7 +138,17 @@ def wrapper(*args, **kwargs):
def available_stats(self, conf):
stats = []
- for path in settings.ADMIN_ATTENDEE_STATS:
+ stats_modules = (
+ 'p3.stats.tickets_status',
+ 'p3.stats.conference_speakers',
+ 'p3.stats.conference_speakers_day',
+ 'p3.stats.speaker_status',
+ 'p3.stats.presence_days',
+ 'p3.stats.shirt_sizes',
+ 'p3.stats.diet_types',
+ 'p3.stats.pp_tickets',
+ )
+ for path in stats_modules:
func = utils.dotted_import(path)
w = {
'get_data': self._stat_wrapper(func, conf),
diff --git a/conference/debug_panel.py b/conference/debug_panel.py
index 9c5850bd2..ad69e3b48 100644
--- a/conference/debug_panel.py
+++ b/conference/debug_panel.py
@@ -29,8 +29,8 @@
REAL_INVOICE_PREFIX,
next_invoice_code_for_year,
render_invoice_as_html,
- export_invoices_to_2018_tax_report,
- export_invoices_to_2018_tax_report_csv,
+ export_invoices_to_tax_report,
+ export_invoices_to_tax_report_csv,
export_invoices_for_payment_reconciliation,
extract_customer_info,
)
@@ -39,7 +39,7 @@
set_early_bird_fare_dates,
set_regular_fare_dates,
)
-from conference.tickets import count_number_of_sold_training_tickets_including_combined_tickets
+from conference.tickets import sold_training_tickets_including_combined_tickets
def get_current_commit_hash():
@@ -90,9 +90,9 @@ def debug_panel_index(request):
('Python_Version', platform.python_version()),
('Conference_current', Conference.objects.current()),
('SOLD_TRAINING_TICKETS',
- count_number_of_sold_training_tickets_including_combined_tickets(
+ sold_training_tickets_including_combined_tickets(
conference_code=settings.CONFERENCE_CONFERENCE,
- )),
+ ).count()),
]
allowed_settings = [
@@ -187,9 +187,9 @@ def debug_panel_invoice_example(request):
@staff_member_required
-def debug_panel_invoice_export_for_tax_report_2018(request):
+def debug_panel_invoice_export_for_tax_report(request):
start_date, end_date = get_start_end_dates(request)
- invoices_and_exported = export_invoices_to_2018_tax_report(
+ invoices_and_exported = export_invoices_to_tax_report(
start_date, end_date
)
@@ -203,13 +203,13 @@ def debug_panel_invoice_export_for_tax_report_2018(request):
@staff_member_required
-def debug_panel_invoice_export_for_tax_report_2018_csv(request):
+def debug_panel_invoice_export_for_tax_report_csv(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] =\
'attachment; filename="export-invoices.csv"'
start_date, end_date = get_start_end_dates(request)
- export_invoices_to_2018_tax_report_csv(response, start_date, end_date)
+ export_invoices_to_tax_report_csv(response, start_date, end_date)
return response
@@ -274,7 +274,7 @@ class Meta:
new_invoice.html = render_invoice_as_html(new_invoice)
new_invoice.save()
- return redirect('debug_panel_invoice_export_for_tax_report_2018')
+ return redirect('debug_panel_invoice_export_for_tax_report')
else:
customer = (
old_invoice.customer
@@ -350,11 +350,11 @@ class FareSetup:
debug_panel_invoice_force_preview,
name="debug_panel_invoice_forcepreview"),
url(r'^invoices_export/$',
- debug_panel_invoice_export_for_tax_report_2018,
- name='debug_panel_invoice_export_for_tax_report_2018'),
+ debug_panel_invoice_export_for_tax_report,
+ name='debug_panel_invoice_export_for_tax_report'),
url(r'^invoices_export.csv$',
- debug_panel_invoice_export_for_tax_report_2018_csv,
- name='debug_panel_invoice_export_for_tax_report_2018_csv'),
+ debug_panel_invoice_export_for_tax_report_csv,
+ name='debug_panel_invoice_export_for_tax_report_csv'),
url(r'^invoices_export_for_accounting.json$',
debug_panel_invoice_export_for_payment_reconciliation_json,
name='debug_panel_invoice_export_for_payment_reconciliation_json'),
diff --git a/conference/forms/talks.py b/conference/forms/talks.py
index d616b42fa..c5ba2b4f9 100644
--- a/conference/forms/talks.py
+++ b/conference/forms/talks.py
@@ -58,12 +58,10 @@ def save(self, user):
class TalkSlidesForm(forms.ModelForm):
- slides = forms.FileField(required=True)
-
class Meta:
model = Talk
fields = [
- "slides"
+ "slides", "slides_url", "repository_url"
]
diff --git a/conference/invoicing.py b/conference/invoicing.py
index 6179f95e3..18ba0086b 100644
--- a/conference/invoicing.py
+++ b/conference/invoicing.py
@@ -291,7 +291,7 @@ def render_invoice_as_html(invoice):
return render_to_string('assopy/invoice.html', ctx)
-CSV_2018_REPORT_COLUMNS = [
+CSV_REPORT_COLUMNS = [
'ID',
'Emit Date',
'Buyer Name',
@@ -299,13 +299,16 @@ def render_invoice_as_html(invoice):
'Address',
'Country',
'VAT ID',
- 'Net Price in GBP',
- 'VAT in GBP',
- 'Gross Price in GBP',
+ 'Currency',
+ 'Net Price',
+ 'VAT',
+ 'Gross Price',
]
+# For b/w compatibility
+CSV_2018_REPORT_COLUMNS = CSV_REPORT_COLUMNS
-def export_invoices_to_2018_tax_report(start_date, end_date=None):
+def export_invoices_to_tax_report(start_date, end_date=None):
if end_date is None:
end_date = datetime.date.today()
@@ -329,21 +332,22 @@ def export_invoices_to_2018_tax_report(start_date, end_date=None):
output['Country'] = ""
output['VAT ID'] = invoice.order.vat_number
- output['Net Price in %s' % invoice.local_currency] =\
+ output['Currency'] = invoice.local_currency
+ output['Net Price'] =\
invoice.net_price_in_local_currency
- output['VAT in %s' % invoice.local_currency] =\
+ output['VAT'] =\
invoice.vat_in_local_currency
- output['Gross Price in %s' % invoice.local_currency] =\
+ output['Gross Price'] =\
invoice.price_in_local_currency
yield invoice, output
-def export_invoices_to_2018_tax_report_csv(fp, start_date, end_date=None):
- writer = csv.DictWriter(fp, CSV_2018_REPORT_COLUMNS, quoting=csv.QUOTE_ALL)
+def export_invoices_to_tax_report_csv(fp, start_date, end_date=None):
+ writer = csv.DictWriter(fp, CSV_REPORT_COLUMNS, quoting=csv.QUOTE_ALL)
writer.writeheader()
- for invoice, to_export in export_invoices_to_2018_tax_report(
+ for invoice, to_export in export_invoices_to_tax_report(
start_date, end_date
):
writer.writerow(to_export)
diff --git a/conference/migrations/0015_add_talk_url_fields.py b/conference/migrations/0015_add_talk_url_fields.py
new file mode 100644
index 000000000..f206687c9
--- /dev/null
+++ b/conference/migrations/0015_add_talk_url_fields.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.21 on 2019-08-08 17:55
+from __future__ import unicode_literals
+
+import conference.models
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('conference', '0014_stripe_charge_default_uuid'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='talk',
+ name='repository_url',
+ field=models.URLField(blank=True),
+ ),
+ migrations.AddField(
+ model_name='talk',
+ name='slides_url',
+ field=models.URLField(blank=True),
+ ),
+ ]
diff --git a/conference/models.py b/conference/models.py
index 291fe5a3d..fd65ded1a 100644
--- a/conference/models.py
+++ b/conference/models.py
@@ -702,6 +702,8 @@ class Talk(models.Model, UrlMixin):
)
slides = models.FileField(upload_to=_fs_upload_to("slides"), blank=True)
+ slides_url = models.URLField(blank=True)
+ repository_url = models.URLField(blank=True)
video_type = models.CharField(
max_length=30, choices=VIDEO_TYPE, blank=True
)
diff --git a/conference/settings.py b/conference/settings.py
index 8254d06e1..2132bd4a0 100644
--- a/conference/settings.py
+++ b/conference/settings.py
@@ -128,8 +128,6 @@ def _CONFERENCE_TICKETS(conf, ticket_type=None, fare_code=None):
SCHEDULE_ATTENDEES = getattr(settings, 'CONFERENCE_SCHEDULE_ATTENDEES', lambda schedule, forecast=False: 0)
-ADMIN_ATTENDEE_STATS = getattr(settings, 'CONFERENCE_ADMIN_ATTENDEE_STATS', ())
-
X_SENDFILE = getattr(settings, 'CONFERENCE_X_SENDFILE', None)
TALK_VIDEO_ACCESS = getattr(settings, 'CONFERENCE_TALK_VIDEO_ACCESS', lambda r, t: True)
diff --git a/conference/talks.py b/conference/talks.py
index a3cde7ced..c27b6a6cf 100644
--- a/conference/talks.py
+++ b/conference/talks.py
@@ -150,7 +150,8 @@ def dump_relevant_talk_information_to_dict(talk: Talk):
"tags": [t.name for t in talk.tags.all()],
"speakers": [],
"schedule_url": talk.get_schedule_url(),
- "slides_url": talk.slides,
+ "slides_file_url": talk.slides,
+ "slides_remote_url": talk.slides_url,
}
for speaker in talk.get_all_speakers():
diff --git a/conference/tickets.py b/conference/tickets.py
index 53839bf64..354a0d7b5 100644
--- a/conference/tickets.py
+++ b/conference/tickets.py
@@ -20,7 +20,7 @@ def reset_ticket_settings(ticket):
return tc
-def count_number_of_sold_training_tickets_including_combined_tickets(conference_code):
+def sold_training_tickets_including_combined_tickets(conference_code):
qs = Ticket.objects.filter(
fare__conference=conference_code,
frozen=False,
@@ -37,4 +37,4 @@ def count_number_of_sold_training_tickets_including_combined_tickets(conference_
]
)
)
- return qs.count()
+ return qs
diff --git a/p3/management/commands/video_schedule_xlsx.py b/p3/management/commands/video_schedule_xlsx.py
index 333f6c59c..a07d308f1 100644
--- a/p3/management/commands/video_schedule_xlsx.py
+++ b/p3/management/commands/video_schedule_xlsx.py
@@ -39,7 +39,7 @@
LICENSE = """
License: This video is licensed under the CC BY-NC-SA 3.0 license: https://creativecommons.org/licenses/by-nc-sa/3.0/
-Please see our speaker release agreement for details: https://ep2018.europython.eu/en/speaker-release-agreement/
+Please see our speaker release agreement for details: https://ep2019.europython.eu/events/speaker-release-agreement/
"""
# Special handling of poster sessions
diff --git a/p3/stats.py b/p3/stats.py
index 33d54fefb..46657b99c 100644
--- a/p3/stats.py
+++ b/p3/stats.py
@@ -7,8 +7,8 @@
from p3 import models
from conference import models as cmodels
-from conference.models import Ticket, Speaker, Talk
-from conference.tickets import count_number_of_sold_training_tickets_including_combined_tickets
+from conference.models import Ticket, Speaker, Talk, TALK_STATUS
+from conference.tickets import sold_training_tickets_including_combined_tickets
def _create_option(id, title, total_qs, **kwargs):
@@ -198,6 +198,30 @@ def tickets_status(conf, code=None):
elif code == 'spam_recruiting':
output = ticket_status_for_spam_recruiting(spam_recruiting)
+ elif code == 'training_tickets_sold':
+ output = ticket_status_for_training_tickets_including_combined(conference_code=conf)
+
+ return output
+
+
+def ticket_status_for_training_tickets_including_combined(conference_code):
+ output = {
+ 'columns': (
+ ('ticket', 'Ticket'),
+ ('name', 'Attendee name'),
+ ('email', 'Email'),
+ ),
+ 'data': [],
+ }
+ tickets = sold_training_tickets_including_combined_tickets(conference_code=conference_code)
+
+ for ticket in tickets:
+ output['data'].append({
+ 'name': ticket.user.assopy_user.name(),
+ 'email': ticket.user.email,
+ 'ticket': ticket,
+ })
+
return output
@@ -374,7 +398,7 @@ def ticket_status_no_code(conf, multiple_assignments, orphan_tickets, spam_recru
{
'id': 'training_tickets_sold',
'title': 'Sold training tickets (including combined)',
- 'total': count_number_of_sold_training_tickets_including_combined_tickets(conference_code=conf),
+ 'total': sold_training_tickets_including_combined_tickets(conference_code=conf).count(),
},
_create_option(
'tickets_with_unique_email',
@@ -454,13 +478,20 @@ def conference_speakers(conf, code=None):
accepted_spks = Speaker.objects.byConference(conf)
not_scheduled = Speaker.objects\
.filter(talkspeaker__talk__in=Talk.objects\
- .filter(conference=conf, status='accepted', event=None))\
+ .filter(conference=conf, status=TALK_STATUS.accepted, event=None))\
.distinct()
+ no_slides = Speaker.objects.filter(
+ Q(talkspeaker__talk__conference=conf) &
+ Q(talkspeaker__talk__status=TALK_STATUS.accepted) &
+ Q(talkspeaker__talk__slides='') &
+ Q(talkspeaker__talk__slides_url='')
+ ).distinct()
if code is None:
return [
_create_option('all_speakers', 'All speakers', all_spks),
_create_option('accepted_speakers', 'Speakers with accepted talks', accepted_spks),
_create_option('speakers_not_scheduled', 'Speakers with unscheduled accepted talks', not_scheduled),
+ _create_option('speakers_no_slides', 'Speakers who have not uploaded slides', no_slides),
]
else:
if code == 'all_speakers':
@@ -469,10 +500,13 @@ def conference_speakers(conf, code=None):
qs = accepted_spks
elif code == 'speakers_not_scheduled':
qs = not_scheduled
+ elif code == 'speakers_no_slides':
+ qs = no_slides
output = {
'columns': (
('name', 'Name'),
('email', 'Email'),
+ ('talks', 'Talks'),
),
'data': [],
}
@@ -481,12 +515,20 @@ def conference_speakers(conf, code=None):
.select_related('user')\
.order_by('user__first_name', 'user__last_name')
for x in qs:
+ talks = x.talks().filter(conference=conf, status=TALK_STATUS.accepted)
+ if code == 'speakers_no_slides':
+ talks = talks.filter(Q(talkspeaker__talk__slides='') & Q(talkspeaker__talk__slides_url=''))
+
data.append({
'name': '%s %s' % (
reverse('admin:auth_user_change', args=(x.user_id,)),
x.user.first_name,
x.user.last_name),
'email': x.user.email,
+ 'talks': '
'.join([
+ f"{talk.title}"
+ for talk in talks
+ ]),
'uid': x.user_id,
})
return output
diff --git a/pycon/settings.py b/pycon/settings.py
index 333279831..935a17176 100644
--- a/pycon/settings.py
+++ b/pycon/settings.py
@@ -715,18 +715,6 @@ def CONFERENCE_SCHEDULE_ATTENDEES(schedule, forecast):
return 0
-CONFERENCE_ADMIN_ATTENDEE_STATS = (
- 'p3.stats.tickets_status',
- 'p3.stats.conference_speakers',
- 'p3.stats.conference_speakers_day',
- 'p3.stats.speaker_status',
- 'p3.stats.presence_days',
- 'p3.stats.shirt_sizes',
- 'p3.stats.diet_types',
- 'p3.stats.pp_tickets',
-)
-
-
CONFERENCE_TICKET_BADGE_ENABLED = True
CONFERENCE_TICKET_BADGE_PROG_ARGS = ['-e', '0', '-p', 'A4', '-n', '1']
diff --git a/templates/conference/debugpanel/index.html b/templates/conference/debugpanel/index.html
index 5eb039f19..62db60ae2 100644
--- a/templates/conference/debugpanel/index.html
+++ b/templates/conference/debugpanel/index.html
@@ -18,8 +18,8 @@