def generate_statistics_pdf(activities=None, start_date=None, all_years=False, year=None):
    ''' Accepts EighthActivity objects and outputs a PDF file. '''
    if activities is None:
        activities = EighthActivity.objects.all().order_by("name")
    if year is None:
        year = current_school_year()

    if not isinstance(activities, list):
        activities = activities.prefetch_related("rooms").prefetch_related("sponsors")

    pdf_buffer = BytesIO()

    h_margin = 1 * inch
    v_margin = 0.5 * inch
    doc = SimpleDocTemplate(pdf_buffer, pagesize=letter, rightMargin=h_margin, leftMargin=h_margin, topMargin=v_margin, bottomMargin=v_margin)

    elements = []

    styles = getSampleStyleSheet()
    styles.add(ParagraphStyle(name="Indent", leftIndent=15))

    empty_activities = []

    for act in activities:
        lelements = []
        relements = []
        act_stats = calculate_statistics(act, start_date=start_date, all_years=all_years, year=year)
        if act_stats["total_blocks"] == 0:
            empty_activities.append(act.name)
            continue
        elements.append(Paragraph(act.name, styles["Title"]))
        sponsor_str = (", ".join([x.name for x in act.sponsors.all()])) if act.sponsors.count() > 0 else "None"
        lelements.append(Paragraph("<b>Default Sponsors:</b> " + sponsor_str, styles["Normal"]))
        lelements.append(Spacer(0, 0.025 * inch))
        room_str = (", ".join([str(x) for x in act.rooms.all()])) if act.rooms.count() > 0 else "None"
        relements.append(Paragraph("<b>Default Rooms:</b> " + room_str, styles["Normal"]))
        relements.append(Spacer(0, 0.025 * inch))

        relements.append(Paragraph("<b>Total blocks:</b> {}".format(act_stats["total_blocks"]), styles["Normal"]))
        relements.append(Paragraph("<b>Scheduled blocks:</b> {}".format(act_stats["scheduled_blocks"]), styles["Indent"]))
        relements.append(Paragraph("<b>Empty blocks:</b> {}".format(act_stats["empty_blocks"]), styles["Indent"]))
        relements.append(Paragraph("<b>Cancelled blocks:</b> {}".format(act_stats["cancelled_blocks"]), styles["Indent"]))

        lelements.append(Paragraph("<b>Total signups:</b> {}".format(act_stats["total_signups"]), styles["Normal"]))
        lelements.append(Paragraph("<b>Average signups per block:</b> {}".format(act_stats["average_signups"]), styles["Indent"]))
        lelements.append(Paragraph("<b>Average signups per student:</b> {}".format(act_stats["average_user_signups"]), styles["Indent"]))
        lelements.append(
            Paragraph("<b>Unique students:</b> {}, <b>Capacity:</b> {}".format(act_stats["students"], act_stats["capacity"]), styles["Normal"]))

        elements.append(
            Table([[lelements, relements]], style=[('LEFTPADDING', (0, 0), (-1, -1), 0), ('RIGHTPADDING', (0, 0), (-1, -1), 0), ('VALIGN', (0, 0),
                                                                                                                                 (-1, -1), 'TOP')]))

        parsed_members = [[x.username, y] for x, y in act_stats["members"]]
        parsed_members = list(chunks(parsed_members, 30))[:3]
        if parsed_members:
            parsed_members = [[["Username", "Signups"]] + x for x in parsed_members]
            parsed_members = [
                Table(x, style=[('FONT', (0, 0), (1, 0), 'Helvetica-Bold'), ('ALIGN', (1, 0), (1, -1), 'RIGHT')]) for x in parsed_members
            ]
            elements.append(Table([parsed_members], style=[('VALIGN', (-1, -1), (-1, -1), 'TOP')]))
            if act_stats["students"] - 90 > 0:
                elements.append(Paragraph("<b>{}</b> students were not shown on this page. ".format(act_stats["students"] - 90), styles["Normal"]))
        else:
            elements.append(Spacer(0, 0.20 * inch))

        if start_date is not None:
            elements.append(
                Paragraph("<b>{}</b> block(s) are past the start date and are not included on this page.".format(act_stats["past_start_date"]),
                          styles["Normal"]))
        elements.append(
            Paragraph("<b>{}</b> block(s) not in the {}-{} school year are not included on this page.".format(
                act_stats["old_blocks"], year - 1, year), styles["Normal"]))

        elements.append(PageBreak())

    if empty_activities:
        empty_activities = [x[:37] + "..." if len(x) > 40 else x for x in empty_activities]
        empty_activities = [[x] for x in empty_activities]
        empty_activities = list(chunks(empty_activities, 35))
        empty_activities = [[["Activity"]] + x for x in empty_activities]
        empty_activities = [
            Table(x, style=[('FONT', (0, 0), (-1, 0), 'Helvetica-Bold'), ('LEFTPADDING', (0, 0), (-1, -1), 0)]) for x in empty_activities
        ]
        for i in range(0, len(empty_activities), 2):
            elements.append(Paragraph("Empty Activities (Page {})".format(i // 2 + 1), styles["Title"]))
            if all_years:
                elements.append(Paragraph("The following activities have no 8th period blocks assigned to them.", styles["Normal"]))
            else:
                elements.append(
                    Paragraph("The following activities have no 8th period blocks assigned to them for the {}-{} school year.".format(year - 1, year),
                              styles["Normal"]))
            elements.append(Spacer(0, 0.10 * inch))
            ea = [empty_activities[i]]
            if i + 1 < len(empty_activities):
                ea.append(empty_activities[i + 1])
            elements.append(
                Table([ea], style=[
                    ('LEFTPADDING', (0, 0), (-1, -1), 0),
                    ('RIGHTPADDING', (0, 0), (-1, -1), 0),
                    ('VALIGN', (0, 0), (-1, -1), 'TOP'),
                ], hAlign='LEFT'))
            elements.append(PageBreak())

    def first_page(canvas, _):
        if len(activities) == 1:
            canvas.setTitle("{} Statistics".format(activities[0].name))
        else:
            canvas.setTitle("8th Period Activity Statistics")
        canvas.setAuthor("Generated by Ion")

    doc.build(elements, onFirstPage=first_page)

    pdf_buffer.seek(0)

    return pdf_buffer
Accepts EighthActivity objects and outputs a PDF file.