def test_get_election0_xlsx(self):
        response = self.client.get(
            reverse('results-export'),
            { 'election': str(Election.objects.get(name='Election 0').id) }
        )

        self.assertEqual(response.status_code, 200)        

        self.assertEqual(
            response['Content-Disposition'],
            'attachment; filename="Election 0 Results.xlsx"'
        )

        wb = openpyxl.load_workbook(io.BytesIO(response.content))

        self.assertEqual(len(wb.worksheets), 1)

        # Check first worksheet.
        ws = wb.worksheets[0]

        self.assertEqual(wb.sheetnames[0], 'Election 0')

        row_count = ws.max_row
        col_count = ws.max_column
        self.assertEqual(row_count, 25)
        self.assertEqual(col_count, 5)

        self.assertEqual(str(ws.cell(1, 1).value), 'Election 0 Results')

        self.assertEqual(str(ws.cell(2, 1).value), 'Candidates')

        cellContents = [
            'Position 0',
            'Party 0',
            '0, 0',
            'Party 1',
            '3, 3',
            'Party 2',
            'None',
            'Position 1',
            'Party 0',
            '1, 1',
            'Party 1',
            '4, 4',
            'Party 2',
            'None',
            'Position 2',
            'Party 0',
            '2, 2',
            'Party 1',
            '5, 5',
            'Party 2',
            'None'
        ]
        for cellIndex, content in enumerate(cellContents, 5):
            self.assertEqual(str(ws.cell(cellIndex, 1).value), content) 

        self.assertEqual(str(ws.cell(2, 2).value), 'Number of Votes')

        self.assertEqual(str(ws.cell(3, 2).value), '0')

        self.assertEqual(str(ws.cell(4, 2).value), '0') # Section

        self.assertEqual(str(ws.cell(7, 2).value), '1')
        self.assertEqual(str(ws.cell(9, 2).value), '0')
        self.assertEqual(str(ws.cell(11, 2).value), 'N/A')
        self.assertEqual(str(ws.cell(14, 2).value), '2')
        self.assertEqual(str(ws.cell(16, 2).value), '0')
        self.assertEqual(str(ws.cell(18, 2).value), 'N/A')
        self.assertEqual(str(ws.cell(21, 2).value), '0')
        self.assertEqual(str(ws.cell(23, 2).value), '0')
        self.assertEqual(str(ws.cell(25, 2).value), 'N/A')

        self.assertEqual(str(ws.cell(4, 3).value), '1') # Section

        self.assertEqual(str(ws.cell(7, 3).value), '0')
        self.assertEqual(str(ws.cell(9, 3).value), '1')
        self.assertEqual(str(ws.cell(11, 2).value), 'N/A')
        self.assertEqual(str(ws.cell(14, 3).value), '0')
        self.assertEqual(str(ws.cell(16, 3).value), '0')
        self.assertEqual(str(ws.cell(18, 2).value), 'N/A')
        self.assertEqual(str(ws.cell(21, 3).value), '1')
        self.assertEqual(str(ws.cell(23, 3).value), '0')
        self.assertEqual(str(ws.cell(25, 2).value), 'N/A')

        self.assertEqual(str(ws.cell(3, 4).value), '1')

        self.assertEqual(str(ws.cell(4, 4).value), '2') # Section

        self.assertEqual(str(ws.cell(7, 4).value), '0')
        self.assertEqual(str(ws.cell(9, 4).value), '0')
        self.assertEqual(str(ws.cell(11, 2).value), 'N/A')
        self.assertEqual(str(ws.cell(14, 4).value), '0')
        self.assertEqual(str(ws.cell(16, 4).value), '1')
        self.assertEqual(str(ws.cell(18, 2).value), 'N/A')
        self.assertEqual(str(ws.cell(21, 4).value), '0')
        self.assertEqual(str(ws.cell(23, 4).value), '1')
        self.assertEqual(str(ws.cell(25, 2).value), 'N/A')

        self.assertEqual(str(ws.cell(3, 5).value), 'Total Votes')
        self.assertEqual(str(ws.cell(7, 5).value), '1')
        self.assertEqual(str(ws.cell(9, 5).value), '1')
        self.assertEqual(str(ws.cell(11, 2).value), 'N/A')
        self.assertEqual(str(ws.cell(14, 5).value), '2')
        self.assertEqual(str(ws.cell(16, 5).value), '1')
        self.assertEqual(str(ws.cell(18, 2).value), 'N/A')
        self.assertEqual(str(ws.cell(21, 5).value), '1')
        self.assertEqual(str(ws.cell(23, 5).value), '1')
        self.assertEqual(str(ws.cell(25, 2).value), 'N/A')
def characteristics(char, title=''):
    fig, axs = plt.subplots(2, 2, figsize=(10, 8), sharex=True)
    if title:
        fig.suptitle(title)

    n = np.array(char['n'])*60
    pmech = np.array(char['pmech'])*1e-3

    axs[0, 0].plot(n, np.array(char['T']), 'C0-', label='Torque')
    axs[0, 0].set_ylabel("Torque / Nm")
    axs[0, 0].grid()
    axs[0, 0].legend(loc='center left')
    ax1 = axs[0, 0].twinx()
    ax1.plot(n, pmech, 'C1-', label='P mech')
    ax1.set_ylabel("Power / kW")
    ax1.legend(loc='lower center')

    axs[0, 1].plot(n[1:], np.array(char['u1'][1:]), 'C0-', label='Voltage')
    axs[0, 1].set_ylabel("Voltage / V",)
    axs[0, 1].grid()
    axs[0, 1].legend(loc='center left')
    ax2 = axs[0, 1].twinx()
    ax2.plot(n[1:], char['cosphi'][1:], 'C1-', label='Cos Phi')
    ax2.set_ylabel("Cos Phi")
    ax2.legend(loc='lower right')

    if 'id' in char:
        axs[1, 0].plot(n, np.array(char['id']), label='Id')
    if 'iq' in char:
        axs[1, 0].plot(n, np.array(char['iq']), label='Iq')
    axs[1, 0].plot(n, np.array(char['i1']), label='I1')
    axs[1, 0].set_xlabel("Speed / rpm")
    axs[1, 0].set_ylabel("Current / A")
    axs[1, 0].legend(loc='center left')
    if 'beta' in char:
        ax3 = axs[1, 0].twinx()
        ax3.plot(n, char['beta'], 'C3-', label='Beta')
        ax3.set_ylabel("Beta / °")
        ax3.legend(loc='center right')
    axs[1, 0].grid()

    plfe = np.array(char['plfe'])*1e-3
    plcu = np.array(char['plcu'])*1e-3
    pl = np.array(char['losses'])*1e-3
    axs[1, 1].plot(n, plcu, 'C0-', label='Cu Losses')
    axs[1, 1].plot(n, plfe, 'C1-', label='Fe Losses')
    axs[1, 1].set_ylabel("Losses / kW")
    axs[1, 1].legend(loc='center left')
    axs[1, 1].grid()
    axs[1, 1].set_xlabel("Speed / rpm")
    ax4 = axs[1, 1].twinx()
    ax4.plot(n[1:-1], char['eta'][1:-1], 'C3-', label="Eta")
    ax4.legend(loc='upper center')
    ax4.set_ylabel("Efficiency")

    fig.tight_layout()