class PlanDVHGraphTestCase(ConradTestCase):
        @classmethod
        def setUpClass(self):
            self.beams = 50
            self.anatomy = Anatomy([
                Structure(0, 'PTV', True, A=np.random.rand(100, self.beams)),
                Structure(1, 'OAR1', False, A=np.random.rand(300, self.beams)),
                Structure(2, 'OAR2', False, A=np.random.rand(250, self.beams))
            ])
            self.anatomy['PTV'].constraints += D(80) > 40 * Gy
            self.anatomy['PTV'].constraints += D(20) < 45 * Gy

        def test_plandvh_graph_init(self):
            # explicit initialization
            pd = self.anatomy.plotting_data()
            pdg = PlanDVHGraph(pd)
            self.assertIsInstance(pdg.structure_DVHs, dict)
            for structure in self.anatomy:
                self.assertIn(structure.label, pdg.structure_DVHs)
                self.assertIn(structure.label, pdg.structure_labels)
                self.assertIn(structure.label, pdg.structure_colors)
                self.assertIsInstance(pdg[structure.label], StructureDVHGraph)
            for _, s_dvh in pdg:
                self.assertIsInstance(s_dvh, StructureDVHGraph)

            # implicit initialization
            pdg = PlanDVHGraph(self.anatomy)
            for structure in self.anatomy:
                self.assertIsInstance(pdg[structure.label], StructureDVHGraph)
            for _, s_dvh in pdg:
                self.assertIsInstance(s_dvh, StructureDVHGraph)

        def test_plandvh_graph_maxdose(self):
            self.anatomy.calculate_doses(np.random.rand(self.beams))
            pd = self.anatomy.plotting_data()
            dmax = dmaxdc = 0
            for label in pd:
                sdata = pd[label]
                dmax = max(dmax, sdata['curve']['dose'][-1])
                dmaxdc = max(dmaxdc, dmax)
                for _, constr in sdata['constraints']:
                    dmaxdc = max(dmaxdc, max(constr['dose']))

            pdg = PlanDVHGraph(pd)
            self.assertEqual(dmaxdc, pdg.maxdose())
            self.assertEqual(dmax, pdg.maxdose(exclude_constraints=True))
    class PlanConstraintsGraphTestCase(ConradTestCase):
        @classmethod
        def setUpClass(self):
            self.beams = 50
            self.anatomy = Anatomy([
                Structure(0, 'PTV', True, A=np.random.rand(100, self.beams)),
                Structure(1, 'OAR1', False, A=np.random.rand(300, self.beams)),
                Structure(2, 'OAR2', False, A=np.random.rand(250, self.beams))
            ])
            self.anatomy['PTV'].constraints += D(80) > 40 * Gy
            self.anatomy['PTV'].constraints += D(20) < 45 * Gy

        def test_plan_constraints_graph_init(self):
            # # explicit initialization
            pd = self.anatomy.plotting_data(constraints_only=True)
            pcg = PlanConstraintsGraph(pd)
            self.assertIsInstance(pcg.structure_constraints, dict)
            for structure in self.anatomy:
                self.assertIn(structure.label, pcg.structure_constraints)
                self.assertIn(structure.label, pcg.structure_labels)
                self.assertIn(structure.label, pcg.structure_colors)
                self.assertIsInstance(pcg[structure.label],
                                      StructureConstraintsGraph)
            for _, s_constr in pcg:
                self.assertIsInstance(s_constr, StructureConstraintsGraph)

            # implicit initialization
            pcg = PlanConstraintsGraph(self.anatomy)
            for structure in self.anatomy:
                self.assertIsInstance(pcg[structure.label],
                                      StructureConstraintsGraph)
            for _, s_constr in pcg:
                self.assertIsInstance(s_constr, StructureConstraintsGraph)

        def test_plan_constraints_graph_maxdose(self):
            self.anatomy.calculate_doses(np.random.rand(self.beams))
            pd = self.anatomy.plotting_data(constraints_only=True)
            dmax = 0
            for label in pd:
                for _, constr in pd[label]:
                    dmax = max(dmax, max(constr['dose']))

            pcg = PlanConstraintsGraph(pd)
            self.assertEqual(dmax, pcg.maxdose)
    class DVHPlotTestCase(ConradTestCase):
        @classmethod
        def setUpClass(self):
            self.beams = 50
            self.anatomy = Anatomy([
                Structure(0, 'PTV', True, A=np.random.rand(100, self.beams)),
                Structure(1, 'OAR1', False, A=np.random.rand(300, self.beams)),
                Structure(2, 'OAR2', False, A=np.random.rand(250, self.beams))
            ])
            self.panel_assignments = {0: 0, 1: 0, 2: 0}
            self.names = {0: 'PTV', 1: 'OAR1', 2: 'OAR2'}
            self.case = Case(anatomy=self.anatomy)

        def setUp(self):
            self.build_call = DVHPlot.build

        def tearDown(self):
            DVHPlot.build = self.build_call

        def test_dvhplot_init_properties(self):
            DVHPlot.build = lambda arg_self: None

            d = DVHPlot(self.panel_assignments)
            self.assertNotIsInstance(d.figure, mpl.figure.Figure)
            self.assertEqual(d.n_structures, 3)
            self.assertIsInstance(d.subplots, dict)
            self.assertEqual(len(d.subplots), 0)
            self.assertIsInstance(d.panels, list)
            self.assertEqual(len(d.panels), 0)
            for k, v in d.subplot_assignments.items():
                self.assertEqual(self.panel_assignments[k], v)

            self.assertEqual(d.cols, 1)
            self.assertEqual(d.rows, 1)
            self.assertEqual(d.layout, 'auto')

        def test_dvhplot_panels_to_cols(self):
            DVHPlot.build = lambda arg_self: None
            d = DVHPlot(self.panel_assignments)

            self.assertEqual(d.subplots_to_cols(1), 1)
            self.assertEqual(d.subplots_to_cols(2), 2)
            self.assertEqual(d.subplots_to_cols(3), 2)
            self.assertEqual(d.subplots_to_cols(4), 2)
            self.assertEqual(d.subplots_to_cols(5), 3)
            self.assertEqual(d.subplots_to_cols(6), 3)
            self.assertEqual(d.subplots_to_cols(7), 4)
            self.assertEqual(d.subplots_to_cols(8), 4)
            self.assertEqual(d.subplots_to_cols(12), 4)
            self.assertEqual(d.subplots_to_cols(15), 4)
            self.assertEqual(d.subplots_to_cols(25), 4)

        def test_dvhplot_sift_options(self):
            DVHPlot.build = lambda arg_self: None
            d = DVHPlot(self.panel_assignments)
            options = {
                'legend_opt1': 1,
                'legend_opt2': 2,
                'other_opt1': 3,
                'other_opt2': 4
            }
            legend_options = {'opt1': 1, 'opt2': 2}
            other_options = {'other_opt1': 3, 'other_opt2': 4}

            o_opt, l_opt = d.sift_options(**options)
            for k, v in legend_options.items():
                self.assertEqual(l_opt[k], v)
            for k, v in other_options.items():
                self.assertEqual(o_opt[k], v)

        def test_dvhplot_build_clear(self):
            DVHPlot.build = lambda arg_self: None
            d = DVHPlot(self.panel_assignments)
            self.assertNotIsInstance(d.figure, mpl.figure.Figure)
            self.assertIsInstance(d.subplots, dict)
            self.assertEqual(len(d.subplots), 0)
            self.assertIsInstance(d.panels, list)
            self.assertEqual(len(d.panels), 0)

            # test calculate panels
            d.calculate_panels()
            self.assertEqual(d.n_subplots, 1)
            self.assertEqual(d.rows, 1)
            self.assertEqual(d.cols, 1)

            d._DVHPlot__subplot_assignments_by_structure = {0: 0, 1: 1, 2: 2}
            d.calculate_panels()
            self.assertEqual(d.n_subplots, 3)
            self.assertEqual(d.rows, 2)
            self.assertEqual(d.cols, 2)

            d._DVHPlot__layout = 'vertical'
            d.calculate_panels()
            self.assertEqual(d.n_subplots, 3)
            self.assertEqual(d.rows, 3)
            self.assertEqual(d.cols, 1)

            d._DVHPlot__layout = 'horizontal'
            d.calculate_panels()
            self.assertEqual(d.n_subplots, 3)
            self.assertEqual(d.rows, 1)
            self.assertEqual(d.cols, 3)

            # test build/clear
            DVHPlot.build = self.build_call
            d._DVHPlot__layout = 'auto'
            d.subplot_assignments = {0: 0, 1: 0, 2: 0}
            d.build()
            self.assertIsInstance(d.figure, mpl.figure.Figure)
            self.assertEqual(len(d.subplots), 3)
            for sp in d.subplots.values():
                self.assertIsInstance(sp, DVHSubplot)
            self.assertIsInstance(d.panels, list)
            self.assertEqual(len(d.panels), 1)
            for sp in d.panels:
                self.assertIsInstance(sp, DVHSubplot)

            d.clear()
            self.assertNotIsInstance(d.figure, mpl.figure.Figure)
            self.assertIsInstance(d.subplots, dict)
            self.assertEqual(len(d.subplots), 0)
            self.assertIsInstance(d.panels, list)
            self.assertEqual(len(d.panels), 0)

        def test_dvhplot_panels_layout(self):
            d = DVHPlot(self.panel_assignments)
            self.assertIsInstance(d.figure, mpl.figure.Figure)
            self.assertEqual(d.n_subplots, 1)
            self.assertEqual(d.rows, 1)
            self.assertEqual(d.cols, 1)
            self.assertEqual(len(d.subplots), 3)
            self.assertEqual(len(d.panels), 1)
            for i in xrange(3):
                self.assertEqual(d.subplots[i], d.panels[0])
                self.assertTrue(d.subplots[i].bottom)
                self.assertTrue(d.subplots[i].left)

            d.subplot_assignments = {0: 0, 1: 1, 2: 2}
            d.build()

            self.assertEqual(d.n_subplots, 3)
            self.assertEqual(d.rows, 2)
            self.assertEqual(d.cols, 2)
            self.assertEqual(len(d.subplots), 3)
            self.assertEqual(len(d.panels), 3)
            for i in xrange(3):
                self.assertEqual(d.subplots[i], d.panels[i])
                self.assertEqual(d.subplots[i].bottom, bool(i >= d.cols))
                self.assertEqual(d.subplots[i].left, bool(i % d.cols == 0))

            d.layout = 'vertical'
            self.assertEqual(d.n_subplots, 3)
            self.assertEqual(d.rows, 3)
            self.assertEqual(d.cols, 1)
            self.assertEqual(len(d.subplots), 3)
            self.assertEqual(len(d.panels), 3)
            for i in xrange(3):
                self.assertEqual(d.subplots[i], d.panels[i])
                self.assertEqual(d.subplots[i].bottom,
                                 bool(i == d.n_subplots - 1))
                self.assertTrue(d.subplots[i].left)

            d.layout = 'horizontal'
            self.assertEqual(d.n_subplots, 3)
            self.assertEqual(d.rows, 1)
            self.assertEqual(d.cols, 3)
            self.assertEqual(len(d.subplots), 3)
            self.assertEqual(len(d.panels), 3)
            for i in xrange(3):
                self.assertEqual(d.subplots[i], d.panels[i])
                self.assertTrue(d.subplots[i].bottom)
                self.assertEqual(d.subplots[i].left, bool(i == 0))

            with self.assertRaises(ValueError):
                d.layout = 'not a valid layout string'

        def test_dvhplot_getitem(self):
            d = DVHPlot(self.panel_assignments)

            for key in [0, 1, 2, 'upper right']:
                self.assertIsInstance(d[key], DVHSubplot)
                self.assertEqual(d[key], d.panels[0])

            d.subplot_assignments = {0: 0, 1: 1, 2: 2}
            d.build()

            for key in [0, 1, 2, 'upper right']:
                self.assertIsInstance(d[key], DVHSubplot)
                if isinstance(key, int):
                    self.assertEqual(d[key], d.panels[key])
                else:
                    self.assertEqual(d[key], d.panels[1])

            d.layout = 'horizontal'
            self.assertEqual(d['upper right'], d.panels[-1])

        def test_dvhplot_drawlegend(self):
            d = DVHPlot(self.panel_assignments)
            series = [mpl.lines.Line2D([], []) for i in xrange(3)]
            names = ['first', 'second', 'third']

            self.assertEqual(len(d.figure.legends), 0)
            d.draw_legend(series, names)
            self.assertEqual(len(d.figure.legends), 1)

            self.assertEqual(len(d.figure.legends[-1].get_lines()),
                             len(series))
            for text in d.figure.legends[-1].get_texts():
                self.assertTrue(text.get_text() in names)

            # coordinates
            width = d.figure.legends[-1].get_bbox_to_anchor().x1
            height = d.figure.legends[-1].get_bbox_to_anchor().y1

            coords = [0.5, 0.6]
            d.draw_legend(series, names, coordinates=coords)

            origin_x = d.figure.legends[-1].get_bbox_to_anchor().x0
            origin_y = d.figure.legends[-1].get_bbox_to_anchor().y0
            self.assertTrue(coords[0] * width == origin_x)
            self.assertTrue(coords[1] * height == origin_y)

            # location
            d.draw_legend(series, names, coordinates=coords, alignment='right')
            d.draw_legend(series,
                          names,
                          coordinates=coords,
                          alignment='center')
            self.assertNotEqual(d.figure.legends[-2]._loc,
                                d.figure.legends[-1]._loc)

        def test_dvhplot_drawtitle(self):
            d = DVHPlot(self.panel_assignments)
            d.draw_title('test title')
            self.assertEqual(d.figure.texts[-1].get_text(), 'test title')

        def test_dvhplot_format_subplots(self):
            d = DVHPlot(self.panel_assignments)

            xmax = 50.
            d.format_subplots(xmax)

            for subplot in d.panels:
                self.assertEqual(subplot.xmax, xmax)

        def test_check_figure(self):
            DVHPlot.build = lambda arg_self: None

            d = DVHPlot(self.panel_assignments)
            with self.assertRaises(AttributeError):
                d.check_figure()

            DVHPlot.build = self.build_call

            d = DVHPlot(self.panel_assignments)
            d.check_figure()

            d.clear()
            with self.assertRaises(AttributeError):
                d.check_figure()

        def test_dvhplot_plotvirtual(self):
            d = DVHPlot(self.panel_assignments)

            series_names = ['first', 'second', 'third']
            series_aesthetics = [LineAesthetic() for i in xrange(3)]

            d.plot_virtual(series_names, series_aesthetics)

            self.assertEqual(len(d.figure.legends), 1)
            self.assertEqual(len(d.figure.legends[-1].get_lines()), 3)
            for text in d.figure.legends[-1].get_texts():
                self.assertIn(text.get_text(), series_names)

        def test_dvhplot_plotlabels(self):
            d = DVHPlot(self.panel_assignments)

            coords = (0.5, 0.6)
            d.plot_labels({1: coords}, self.anatomy.plotting_data())

            self.assertEqual(len(d.subplots[1].axes.texts), 1)
            t = d.subplots[1].axes.texts[-1]
            self.assertEqual(t.get_text(), self.anatomy[1].name)
            self.assertEqual(t.get_unitless_position(), coords)

            with self.assertRaises(KeyError):
                d.plot_labels({4: coords}, self.anatomy.plotting_data())

        def test_dvhplot_plotconstraints(self):
            d = DVHPlot(self.panel_assignments)

            LABEL = 0
            self.anatomy[LABEL].constraints += D(30) < 20 * Gy
            pc = PlanConstraintsGraph(self.anatomy)

            d.plot_constraints(pc)
            for constr in pc[LABEL]:
                self.assertIs(constr.axes, d.subplots[LABEL].axes)

        def test_dvhplot_plot(self):
            d = DVHPlot({i: i for i in xrange(3)})
            d.layout = 'horizontal'

            LABEL = 0
            self.anatomy.calculate_doses(np.random.rand(self.beams))
            self.anatomy[LABEL].constraints += D(30) < 20 * Gy
            pd = PlanDVHGraph(self.anatomy.plotting_data())

            # vanilla plot + self-title subplots
            d.plot(self.anatomy.plotting_data(), self_title_subplots=True)
            for structure in self.anatomy:
                self.assertEqual(d.subplots[structure.label].title,
                                 structure.name)

            # individual legends
            d.plot(self.anatomy.plotting_data(), legend='each')
            for structure in self.anatomy:
                self.assertIsNotNone(d.subplots[structure.label].legend)
                self.assertEqual(
                    len(d.subplots[structure.label].legend.get_lines()), 1)
            self.assertEqual(len(d.figure.legends), 0)

            # overall legend
            d.plot(self.anatomy.plotting_data(), legend=True)
            for structure in self.anatomy:
                self.assertIsNone(d.subplots[structure.label].legend)
            self.assertEqual(len(d.figure.legends), 1)

            # self-title for multiple structures on single subplot
            d.subplot_assignments = {i: 0 for i in xrange(3)}
            d.build()
            d.plot(pd, self_title_subplots=True)
            title = ''
            for label, _ in pd:
                title += self.anatomy[label].name
                title += ', '

            title = title[:-2]  # trim terminal ", "
            self.assertEqual(d.panels[0].title, title)

        # def test_dvhplot_show(self):
        # pass

        def test_dvhplot_save(self):
            d = DVHPlot(self.panel_assignments)

            filename = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                                    'test.pdf')

            self.assertFalse(os.path.exists(filename))
            d.build()
            d.save(filename)
            self.assertTrue(os.path.exists(filename))
            os.remove(filename)
            self.assertFalse(os.path.exists(filename))