~replaceafill/ubuntu/trusty/schooltool/2.8_custom-css

« back to all changes in this revision

Viewing changes to src/schooltool/export/export.py

  • Committer: Gediminas Paulauskas
  • Date: 2014-04-18 16:25:33 UTC
  • mfrom: (1.1.33)
  • Revision ID: menesis@pov.lt-20140418162533-noklnc6b89w2epee
Tags: 1:2.7.0-0ubuntu1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
48
48
from schooltool.term.interfaces import ITermContainer
49
49
from schooltool.course.interfaces import ICourseContainer
50
50
from schooltool.course.interfaces import ISectionContainer
 
51
from schooltool.level.interfaces import ILevelContainer
51
52
from schooltool.report.browser.report import RequestRemoteReportDialog
52
53
from schooltool.report.browser.report import ProgressReportPage
53
54
from schooltool.report.report import ReportLinkViewlet
82
83
        response.setHeader('Content-Type', 'application/vnd.ms-excel')
83
84
        response.setHeader('Content-Length', len(data))
84
85
 
85
 
    def listIds(self, header, items, ws, offset, last=False):
 
86
    def listRelationships(self, header, relationships, ws, offset, last=False):
 
87
        items = sorted(relationships.all(), key=lambda i: i.__name__)
86
88
        if not items:
87
89
            return offset - 1
88
90
        self.write_header(ws, offset + 1, 0,  header, merge=1)
89
 
        for n, item in enumerate(sorted(items, key=lambda i: i.__name__)):
 
91
        for n, item in enumerate(items):
90
92
            self.write(ws, offset + 2 + n, 0,  item.__name__)
91
93
            self.write(ws, offset + 2 + n, 1,  "")
 
94
            state = relationships.state(item)
 
95
            for x, (date, meaning, code) in enumerate(state):
 
96
                self.write(ws, offset + 2 + n, 2+x*2,
 
97
                           Date(date))
 
98
                self.write(ws, offset + 2 + n, 2+x*2+1, code)
92
99
        return 1 + offset + len(items)
93
100
 
94
101
    def skipRow(self, ws, offset):
113
120
            self._font_cache[font_key] = font = self._makeFont(font_key)
114
121
        return font
115
122
 
116
 
    def write(self, ws, row, col, data,
117
 
              bold=False,
118
 
              color=None,
119
 
              format_str=None,
120
 
              borders=None,
121
 
              merge=None):
 
123
    def write(self, ws, row, col, data, **kw):
 
124
        if isinstance(data, Text):
 
125
            kw.update(data.style)
 
126
            data = data.data
 
127
        bold = kw.get('bold', False)
 
128
        color = kw.get('color', None)
 
129
        format_str = kw.get('format_str', None)
 
130
        borders = kw.get('borders', None)
 
131
        merge = kw.get('merge', None)
122
132
        if borders is None:
123
133
            borders = []
124
134
        if data is None:
255
265
        self.data = data
256
266
 
257
267
    def __cmp__(self, other):
258
 
        return cmp(self.data, other.data)
 
268
        try:
 
269
            return cmp(self.data, other.data)
 
270
        except TypeError:
 
271
            return cmp(unicode(self.data), unicode(other.data))
259
272
 
260
273
    def __repr__(self):
261
274
        return 'Text(%r)' % self.data
289
302
 
290
303
    overall_line_id = 'overall'
291
304
 
292
 
    def print_table(self, table, ws):
293
 
        for x, row in enumerate(table):
294
 
            for y, cell in enumerate(row):
295
 
                self.write(ws, x, y, cell.data, **cell.style)
 
305
    def print_table(self, table, ws, row=0, col=0):
 
306
        for y, cells in enumerate(table):
 
307
            self.print_row(cells, ws, row=(row+y), col=col)
296
308
        return len(table)
297
309
 
298
 
    def format_table(self, fields, items, importer=None, major_progress=()):
 
310
    def print_row(self, cells, ws, row=0, col=0):
 
311
        for x, cell in enumerate(cells):
 
312
            self.write(ws, row, col+x, cell.data, **cell.style)
 
313
 
 
314
    def format_table(self, fields, items, importer=None, major_progress=(),
 
315
                     sort=True):
299
316
        headers = [Header(header)
300
317
                   for header, style, getter in fields]
301
318
        rows = []
307
324
            if importer is not None:
308
325
                self.progress(importer, normalized_progress(
309
326
                        *(major_progress + (n, total_items))))
310
 
        rows.sort()
 
327
        if sort:
 
328
            rows.sort()
311
329
        return [headers] + rows
312
330
 
313
331
    def format_school_years(self):
505
523
        return self.format_table(fields, items, importer='export_contacts',
506
524
                                 major_progress=(0,2))
507
525
 
 
526
    def format_person_contacts(self, person, major_progress=()):
 
527
        contacts = IContactable(person).contacts
 
528
        rows = []
 
529
        for contact in contacts.all():
 
530
            row = []
 
531
            row.append(Text(person.username))
 
532
            target_person = IBasicPerson(contact.__parent__, None)
 
533
            if target_person is None:
 
534
                row.append(Text(contact.__name__))
 
535
            else:
 
536
                row.append(Text(target_person.username))
 
537
 
 
538
            state = contacts.state(contact)
 
539
            for x, (date, meaning, code) in enumerate(state):
 
540
                row.append(Date(date))
 
541
                row.append(Text(code))
 
542
            rows.append(row)
 
543
        return rows
 
544
 
508
545
    def format_contact_relationships(self):
509
 
        fields = [('Person ID', Text, attrgetter('person')),
510
 
                  ('Contact ID', Text, attrgetter('contact')),
511
 
                  ('Relationship', Text, attrgetter('relationship'))]
512
 
 
513
 
        items = []
514
 
        for person in self.context['persons'].values():
 
546
        rows = []
 
547
        persons = self.context['persons']
 
548
        total = len(persons)
 
549
        for nperson, person in enumerate(persons.values()):
515
550
            person = removeSecurityProxy(person)
516
 
            for contact in IContactable(person).contacts:
517
 
                try:
518
 
                    links = IRelationshipLinks(person)
519
 
                    link = links.find(
520
 
                        URIPerson, contact, URIContact, URIContactRelationship)
521
 
                except ValueError:
522
 
                    continue
523
 
                target_person = IBasicPerson(contact.__parent__, None)
524
 
                if target_person is None:
525
 
                    name = contact.__name__
526
 
                else:
527
 
                    name = target_person.username
528
 
                item = ContactRelationship(person.username,
529
 
                    name, link.extra_info.relationship)
530
 
                items.append(item)
531
 
 
532
 
        return self.format_table(fields, items, importer='export_contacts',
533
 
                                 major_progress=(1,2))
 
551
            person_rows = self.format_person_contacts(
 
552
                person, major_progress=(1, 2, nperson, total))
 
553
            rows.extend(person_rows)
 
554
        rows.sort()
 
555
 
 
556
        headers = [Header('Person ID'), Header('Contact ID'), Header('Relationship')]
 
557
        rows.insert(0, headers)
 
558
 
 
559
        return rows
534
560
 
535
561
    def export_contacts(self, wb):
536
562
        self.task_progress.force('export_contacts', active=True)
553
579
        self.print_table(self.format_resources(), ws)
554
580
        self.finish('export_resources')
555
581
 
 
582
    def format_levels(self):
 
583
        fields = [('ID', Text, attrgetter('__name__')),
 
584
                  ('Title', Text, attrgetter('title'))]
 
585
        levels = ILevelContainer(self.context)
 
586
        items = levels.values()
 
587
        return self.format_table(fields, items, importer='export_levels',
 
588
                                 sort=False)
 
589
 
 
590
    def export_levels(self, wb):
 
591
        self.task_progress.force('export_levels', active=True)
 
592
        ws = wb.add_sheet("Grade Levels")
 
593
        self.print_table(self.format_levels(), ws)
 
594
        self.finish('export_levels')
 
595
 
556
596
    def format_courses(self):
 
597
        def get_course_level(course):
 
598
            return ', '.join([l.__name__ for l in course.levels])
557
599
        fields = [('School Year', Text, lambda c: ISchoolYear(c).__name__),
558
600
                  ('ID', Text, attrgetter('__name__')),
559
601
                  ('Title', Text, attrgetter('title')),
560
602
                  ('Description', Text, attrgetter('description')),
561
603
                  ('Local ID', Text, attrgetter('course_id')),
562
604
                  ('Government ID', Text, attrgetter('government_id')),
563
 
                  ('Credits', Text, attrgetter('credits'))]
 
605
                  ('Credits', Text, attrgetter('credits')),
 
606
                  ('Grade Level ID', Text, get_course_level)]
564
607
 
565
608
        school_years = ISchoolYearContainer(self.context).values()
566
609
        items = []
605
648
        return offset
606
649
 
607
650
    def format_section(self, year, courses, term, section, ws, row):
608
 
        teachers = [t.__name__ for t in section.instructors]
609
651
        resources = [r.__name__ for r in section.resources]
610
652
        self.write(ws, row, 0, year.__name__)
611
653
        self.write(ws, row, 1, courses)
618
660
        self.write(ws, row, 6, section.title)
619
661
        if section.description:
620
662
            self.write(ws, row, 7, section.description)
621
 
        self.write(ws, row, 8, ', '.join(teachers))
622
 
        self.write(ws, row, 9, ', '.join(resources))
 
663
        self.write(ws, row, 8, ', '.join(resources))
623
664
 
624
665
    def export_sections(self, wb):
625
666
        self.task_progress.force('export_sections', active=True)
626
667
        ws = wb.add_sheet("Sections")
627
668
        headers = ["School Year", "Courses", "Term", "Section ID",
628
669
                   "Previous ID", "Next ID", "Title", "Description",
629
 
                   "Instructors", "Resources"]
 
670
                   "Resources"]
630
671
        for index, header in enumerate(headers):
631
672
            self.write_header(ws, 0, index, header)
632
673
 
650
691
            row += 1
651
692
        self.finish('export_sections')
652
693
 
653
 
    def format_student_sections(self, year, student_sections, ws, row):
654
 
        headers = ["School Year", "Term", "Section ID"]
655
 
        for index, header in enumerate(headers):
656
 
            self.write_header(ws, row, index, header)
657
 
        row += 1
658
 
 
659
 
        for first, term, section_id, section in sorted(student_sections):
660
 
            self.write(ws, row, 0, year.__name__)
661
 
            self.write(ws, row, 1, term.__name__)
662
 
            self.write(ws, row, 2, section.__name__)
663
 
            row += 1
664
 
        return row + 1
665
 
 
666
 
    def format_students_block(self, students, ws, row):
667
 
        self.write_header(ws, row, 0, "Students")
668
 
        row += 1
669
 
 
670
 
        for student in students.split(','):
671
 
            self.write(ws, row, 0, student)
672
 
            row += 1
673
 
        return row + 1
674
 
 
675
 
    def export_section_enrollment(self, wb):
676
 
        self.task_progress.force('export_section_enrollment', active=True)
 
694
    def format_membership_block(self, relationship, headers):
 
695
        items = sorted(relationship.all(),
 
696
                       key=lambda item: item.__name__)
 
697
        if not items:
 
698
            return []
 
699
        table = [headers]
 
700
        for item in items:
 
701
            cells = [Text(item.__name__), Text('')]
 
702
            state = relationship.state(item)
 
703
            for x, (date, meaning, code) in enumerate(state):
 
704
                cells.append(Date(date))
 
705
                cells.append(Text(code))
 
706
            table.append(cells)
 
707
        table.append([])
 
708
        return table
 
709
 
 
710
    def export_section_enrollment(self, ws, year, term, section, row=0):
 
711
        row += self.print_table([
 
712
            [Header('School Year'), Header('Term'), Header('Section ID')],
 
713
            [Text(year.__name__), Text(term.__name__), Text(section.__name__)],
 
714
            [],
 
715
            ], ws, row=row)
 
716
 
 
717
        row += self.print_table(
 
718
            self.format_membership_block(section.instructors, [Header('Instructors')]),
 
719
            ws, row=row, col=0)
 
720
 
 
721
        row += self.print_table(
 
722
            self.format_membership_block(section.members, [Header('Students')]),
 
723
            ws, row=row, col=0)
 
724
 
 
725
        return row
 
726
 
 
727
    def export_sections_enrollment(self, wb):
 
728
        self.task_progress.force('export_sections_enrollment', active=True)
677
729
        ws = wb.add_sheet("SectionEnrollment")
678
730
 
679
 
        year_sections = {}
 
731
        row = 0
680
732
        years = ISchoolYearContainer(self.context)
681
 
        for ny, year in enumerate(years.values()):
682
 
            sections = year_sections[year] = {}
683
 
            for nt, term in enumerate(year.values()):
684
 
                term_sections = ISectionContainer(term)
685
 
                for ns, section in enumerate(term_sections.values()):
686
 
                    if not list(section.courses):
687
 
                        continue
688
 
                    student_ids = [s.username for s in section.members]
689
 
                    students = ','.join(sorted(student_ids))
690
 
                    student_sections = sections.setdefault(students, [])
691
 
                    student_sections.append((term.first, term, section.__name__,
692
 
                                             section))
693
 
                    self.progress('export_section_enrollment', normalized_progress(
694
 
                        ny, len(years),
695
 
                        nt, len(year),
696
 
                        ns, len(term_sections),
697
 
                        ) * 0.9)
 
733
        total_years = len(years)
 
734
        for ny, year in enumerate(sorted(years.values(), key=lambda year: year.first)):
 
735
            total_terms = len(year)
 
736
            for nt, term in enumerate(sorted(year.values(), key=lambda term: term.first)):
 
737
                sections = ISectionContainer(term)
 
738
                total_sections = len(sections)
 
739
                for ns, section in enumerate(sorted(sections.values(),
 
740
                                                    key=lambda section: section.__name__)):
 
741
                    row = self.export_section_enrollment(
 
742
                        ws, year, term, section, row=row)
 
743
                    self.progress(
 
744
                        'export_sections_enrollment',
 
745
                        normalized_progress(
 
746
                            ny, total_years,
 
747
                            nt, total_terms,
 
748
                            ns, total_sections,
 
749
                            ))
698
750
 
699
 
        row = 0
700
 
        for ny, (year, sections) in enumerate(sorted(year_sections.items())):
701
 
            for ns, (students, student_sections) in enumerate(sorted(sections.items())):
702
 
                row = self.format_student_sections(year, student_sections, ws,
703
 
                                                   row)
704
 
                row = self.format_students_block(students, ws, row)
705
 
                self.progress('export_section_enrollment', normalized_progress(
706
 
                        ny, len(year_sections),
707
 
                        ns, len(sections),
708
 
                        ) * 0.1 + 0.9)
709
 
        self.finish('export_section_enrollment')
 
751
        self.finish('export_sections_enrollment')
710
752
 
711
753
    def format_timetable_sections(self, year, timetable_sections, ws, row):
712
754
        headers = ["School Year", "Term", "Section ID"]
794
836
                  lambda i: ("Description", i.description, None)]
795
837
 
796
838
        offset = self.listFields(group, fields, ws, offset)
797
 
        offset = self.listIds("Members", group.members, ws, offset) + 1
798
 
        offset = self.listIds("Leaders", IAsset(group).leaders, ws, offset,
799
 
                              last=True) + 1
 
839
 
 
840
        offset += self.print_table(
 
841
            self.format_membership_block(group.members, [Header('Members')]),
 
842
            ws, row=offset, col=0)
 
843
 
 
844
        offset += 1
 
845
 
 
846
        leaders = IAsset(group).leaders
 
847
        offset += self.print_table(
 
848
            self.format_membership_block(leaders, [Header('Leaders')]),
 
849
            ws, row=offset, col=0)
 
850
 
800
851
        return offset
801
852
 
802
853
    def export_groups(self, wb):
826
877
                     title=_('School Timetables'), progress=0.0)
827
878
        progress.add('export_resources', active=False,
828
879
                     title=_('Resources'), progress=0.0)
 
880
        progress.add('export_levels', active=False,
 
881
                     title=_('Grade Levels'), progress=0.0)
829
882
        progress.add('export_persons', active=False,
830
883
                     title=_('Persons'), progress=0.0)
831
884
        progress.add('export_contacts', active=False,
834
887
                     title=_('Courses'), progress=0.0)
835
888
        progress.add('export_sections', active=False,
836
889
                     title=_('Sections'), progress=0.0)
837
 
        progress.add('export_section_enrollment', active=False,
 
890
        progress.add('export_sections_enrollment', active=False,
838
891
                     title=_('Section Enrollment'), progress=0.0)
839
892
        progress.add('export_section_timetables', active=False,
840
893
                     title=_('Section Schedules'), progress=0.0)
864
917
        self.export_terms(wb)
865
918
        self.export_school_timetables(wb)
866
919
        self.export_resources(wb)
 
920
        self.export_levels(wb)
867
921
        self.export_persons(wb)
868
922
        self.export_contacts(wb)
869
923
        self.export_courses(wb)
870
924
        self.export_sections(wb)
871
 
        self.export_section_enrollment(wb)
 
925
        self.export_sections_enrollment(wb)
872
926
        self.export_section_timetables(wb)
873
927
        self.export_groups(wb)
874
928
        self.task_progress.title = _("Export complete")
928
982
 
929
983
class ImportLinkViewlet(flourish.page.LinkViewlet):
930
984
    pass
 
985
 
 
986
 
 
987
class RemoteImportLinkViewlet(flourish.page.ModalFormLinkViewlet):
 
988
 
 
989
    dialog_title = _('Upload XLS File')