Exercises: retry implied on move; add handlers to sortable
[edumed.git] / catalogue / management / commands / importlessons.py
1 # -*- coding: utf-8 -*-
2 # This file is part of EduMed, licensed under GNU Affero GPLv3 or later.
3 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
4 #
5 import os
6 import sys
7 import time
8 from optparse import make_option
9 from django.conf import settings
10 from django.core.management.base import BaseCommand
11 from django.core.management.color import color_style
12 from django.core.files import File
13
14 from librarian import IOFile
15 from catalogue.models import Lesson, Section
16
17 #from search import Index
18
19
20 class Command(BaseCommand):
21     option_list = BaseCommand.option_list + (
22         make_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
23             help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'),
24         make_option('-a', '--attachments', dest='attachments', metavar="PATH", default='materialy',
25             help='Attachments dir path.'),
26     )
27     help = 'Imports lessons from the specified directories.'
28     args = 'directory [directory ...]'
29
30     def import_book(self, file_path, options, attachments):
31         verbose = options.get('verbose')
32         iofile = IOFile.from_filename(file_path)
33         iofile.attachments = attachments
34         lesson = Lesson.publish(iofile)
35
36     @staticmethod
37     def all_attachments(path):
38         files = {}
39
40         def read_dir(path):
41             for name in os.listdir(path):
42                 fullname = os.path.join(path, name)
43                 if os.path.isdir(fullname):
44                     read_dir(fullname)
45                 else:
46                     f = IOFile.from_filename(fullname)
47                     files[name] = f
48                     files.setdefault(name.replace(" ", ""), f)
49
50         read_dir(path)
51         return files
52
53
54     def handle(self, *directories, **options):
55         from django.db import transaction
56
57         self.style = color_style()
58         
59         verbose = options.get('verbose')
60
61         # Start transaction management.
62         transaction.commit_unless_managed()
63         transaction.enter_transaction_management()
64         transaction.managed(True)
65
66         files_imported = 0
67         files_skipped = 0
68
69         for dir_name in directories:
70             if not os.path.isdir(dir_name):
71                 print self.style.ERROR("%s: Not a directory. Skipping." % dir_name)
72             else:
73                 att_dir = os.path.join(dir_name, options['attachments'])
74                 attachments = self.all_attachments(att_dir)
75
76                 # files queue
77                 files = sorted(os.listdir(dir_name))
78                 postponed = {}
79                 while files:
80                     file_name = files.pop(0)
81                     file_path = os.path.join(dir_name, file_name)
82                     file_base, ext = os.path.splitext(file_path)
83
84                     # Skip files that are not XML files
85                     if not ext == '.xml':
86                         continue
87
88                     if verbose > 0:
89                         print "Parsing '%s'" % file_path
90                     else:
91                         sys.stdout.write('.')
92                         sys.stdout.flush()
93
94                     # Import book files
95                     try:
96                         self.import_book(file_path, options, attachments)
97                         files_imported += 1
98                         transaction.commit()
99                     except Section.IncompleteError, e:
100                         if file_name not in postponed or postponed[file_name] < files_imported:
101                             # Push it back into the queue, maybe the missing lessons will show up.
102                             if verbose > 0:
103                                 print self.style.NOTICE('Waiting for missing lessons.')
104                             files.append(file_name)
105                             postponed[file_name] = files_imported
106                         else:
107                             # We're in a loop, nothing's being imported - some lesson is really missing.
108                             raise e
109                     except BaseException, e:
110                         import traceback
111                         traceback.print_exc()
112                         files_skipped += 1
113
114         # Print results
115         print
116         print "Results: %d files imported, %d skipped, %d total." % (
117             files_imported, files_skipped, files_imported + files_skipped)
118         print
119
120         transaction.commit()
121         transaction.leave_transaction_management()