## $Id: export.py 7757 2012-03-03 06:02:36Z henrik $
##
## Copyright (C) 2012 Uli Fouquet & Henrik Bettermann
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##
"""Exporters for faculties, departments, and other academics components.
"""
import csv
import grok
from cStringIO import StringIO
from waeup.sirp.interfaces import ICSVExporter

class FacultyExporter(grok.GlobalUtility):
    """Exporter for faculties.
    """
    grok.implements(ICSVExporter)
    grok.name('faculties')

    #: Fieldnames considered by this exporter
    fields = ('code', 'title', 'title_prefix')

    def mangle_value(self, value, name, context=None):
        """Hook for mangling values in derived classes
        """
        if isinstance(value, bool):
            value = value and '1' or '0'
        elif isinstance(value, unicode):
            # CSV writers like byte streams better than unicode
            value = value.encode('utf-8')
        elif value is None:
            # None is not really representable in CSV files
            value = ''
        return value

    def get_csv_writer(self, filepath=None):
        """Get a CSV dict writer instance open for writing.

        Returns a tuple (<writer>, <outfile>) where ``<writer>`` is a
        :class:`csv.DictWriter` instance and outfile is the real file
        which is written to. The latter is important when writing to
        StringIO and can normally be ignored otherwise.

        The returned file will already be filled with the header row.
        """
        if filepath is None:
            outfile = StringIO()
        else:
            outfile = open(filepath, 'wb')
        writer = csv.DictWriter(outfile, self.fields)
        writer.writerow(dict(zip(self.fields, self.fields))) # header
        return writer, outfile

    def write_item(self, faculty, writer):
        """Write a row into using `writer`.
        """
        row = {}
        for name in self.fields:
            value = getattr(faculty, name, None)
            value = self.mangle_value(value, name, faculty)
            row[name] = value
        writer.writerow(row)
        return

    def close_outfile(self, filepath, outfile):
        """Close outfile.

        If filepath is None, the contents of outfile is returned.
        """
        outfile.seek(0)
        if filepath is None:
            return outfile.read()
        outfile.close()
        return

    def export(self, faculties, filepath=None):
        """Export `faculties`, an iterable, as CSV file.

        If `filepath` is ``None``, a raw string with CSV data is returned.
        """
        writer, outfile = self.get_csv_writer(filepath)
        for faculty in faculties:
            self.write_item(faculty, writer)
        return self.close_outfile(filepath, outfile)

    def export_all(self, site, filepath=None):
        """Export faculties in facultycontainer into filepath as CSV data.

        If `filepath` is ``None``, a raw string with CSV data is returned.
        """
        writer, outfile = self.get_csv_writer(filepath)
        faculties = site.get('faculties', {})
        return self.export(faculties.values(), filepath)
        for faculty in faculties.values():
            self.write_item(faculty, writer)
        return self.close_outfile(filepath, outfile)

class DepartmentExporter(FacultyExporter, grok.GlobalUtility):
    """Exporter for departments.
    """
    grok.implements(ICSVExporter)
    grok.name('departments')

    #: Fieldnames considered by this exporter
    fields = ('code', 'faculty_code', 'title', 'title_prefix')

    def mangle_value(self, value, name, context=None):
        """Hook for mangling values in derived classes
        """
        if name == 'faculty_code':
            value = getattr(
                getattr(context, '__parent__', None),
                'code', None)
        return super(DepartmentExporter, self).mangle_value(
            value, name, context)

    def export_all(self, site, filepath=None):
        """Export faculties in facultycontainer into filepath as CSV data.

        If `filepath` is ``None``, a raw string with CSV data is returned.
        """
        writer, outfile = self.get_csv_writer(filepath)
        faculties = site.get('faculties', {})
        for faculty in faculties.values():
            for department in faculty.values():
                self.write_item(department, writer)
        return self.close_outfile(filepath, outfile)


class CourseExporter(FacultyExporter, grok.GlobalUtility):
    """Exporter for courses.
    """
    grok.implements(ICSVExporter)
    grok.name('courses')

    #: Fieldnames considered by this exporter
    fields = ('code', 'faculty_code', 'department_code', 'title', 'credits',
              'passmark', 'semester')

    def mangle_value(self, value, name, context=None):
        """Hook for mangling values in derived classes
        """
        if name == 'faculty_code':
            try:
                value = context.__parent__.__parent__.__parent__.code
            except AttributeError:
                value = None
        elif name == 'department_code':
            try:
                value = context.__parent__.__parent__.code
            except AttributeError:
                value = None
        return super(CourseExporter, self).mangle_value(
            value, name, context)

    def export_all(self, site, filepath=None):
        """Export faculties in facultycontainer into filepath as CSV data.

        If `filepath` is ``None``, a raw string with CSV data is returned.
        """
        writer, outfile = self.get_csv_writer(filepath)
        faculties = site.get('faculties', {})
        for faculty in faculties.values():
            for department in faculty.values():
                for course in department.courses.values():
                    self.write_item(course, writer)
        return self.close_outfile(filepath, outfile)

class CertificateExporter(CourseExporter, grok.GlobalUtility):
    """Exporter for courses.
    """
    grok.implements(ICSVExporter)
    grok.name('certificates')

    #: Fieldnames considered by this exporter
    fields = ('code', 'faculty_code', 'department_code', 'title', 'study_mode',
              'start_level', 'end_level', 'application_category')

    def export_all(self, site, filepath=None):
        """Export faculties in facultycontainer into filepath as CSV data.

        If `filepath` is ``None``, a raw string with CSV data is returned.
        """
        writer, outfile = self.get_csv_writer(filepath)
        faculties = site.get('faculties', {})
        for faculty in faculties.values():
            for department in faculty.values():
                for cert in department.certificates.values():
                    self.write_item(cert, writer)
        return self.close_outfile(filepath, outfile)

class CertificateCourseExporter(CourseExporter, grok.GlobalUtility):
    """Exporter for courses.
    """
    grok.implements(ICSVExporter)
    grok.name('certificate_courses')

    #: Fieldnames considered by this exporter
    fields = ('course', 'faculty_code', 'department_code', 'certificate_code',
              'level', 'mandatory')

    def mangle_value(self, value, name, context=None):
        """Hook for mangling values in derived classes
        """
        if name == 'faculty_code':
            try:
                value = context.__parent__.__parent__.__parent__.__parent__.code
            except AttributeError:
                value = None
        elif name == 'department_code':
            try:
                value = context.__parent__.__parent__.__parent__.code
            except AttributeError:
                value = None
        elif name == 'certificate_code':
            value = getattr(context, '__parent__', None)
            value = getattr(value, 'code', None)
        if name == 'course':
            value = getattr(value, 'code', None)
        return super(CourseExporter, self).mangle_value(
            value, name, context)

    def export_all(self, site, filepath=None):
        """Export faculties in facultycontainer into filepath as CSV data.

        If `filepath` is ``None``, a raw string with CSV data is returned.
        """
        writer, outfile = self.get_csv_writer(filepath)
        faculties = site.get('faculties', {})
        for faculty in faculties.values():
            for department in faculty.values():
                for cert in department.certificates.values():
                    for certref in cert.values():
                        self.write_item(certref, writer)
        return self.close_outfile(filepath, outfile)
