style
[redakcja.git] / apps / catalogue / management / commands / assign_from_redmine.py
1 # -*- coding: utf-8 -*-
2
3 import csv
4 from optparse import make_option
5 import re
6 import urllib
7 import urllib2
8
9 from django.contrib.auth.models import User
10 from django.core.management.base import BaseCommand
11 from django.core.management.color import color_style
12 from django.db import transaction
13
14 from slughifi import slughifi
15 from catalogue.models import Chunk
16
17
18 REDMINE_CSV = 'http://redmine.nowoczesnapolska.org.pl/projects/wl-publikacje/issues.csv'
19 REDAKCJA_URL = 'http://redakcja.wolnelektury.pl/documents/'
20
21
22 class Command(BaseCommand):
23     option_list = BaseCommand.option_list + (
24         make_option(
25             '-r', '--redakcja', dest='redakcja', metavar='URL',
26             help='Base URL of Redakcja documents',
27             default=REDAKCJA_URL),
28         make_option(
29             '-q', '--quiet', action='store_false', dest='verbose', default=True,
30             help='Less output'),
31         make_option(
32             '-f', '--force', action='store_true', dest='force', default=False,
33             help='Force assignment overwrite'),
34     )
35     help = 'Imports ticket assignments from Redmine.'
36     args = '[redmine-csv-url]'
37
38     def handle(self, *redmine_csv, **options):
39
40         self.style = color_style()
41
42         redakcja = options.get('redakcja')
43         verbose = options.get('verbose')
44         force = options.get('force')
45
46         if not redmine_csv:
47             if verbose:
48                 print "Using default Redmine CSV URL:", REDMINE_CSV
49             redmine_csv = REDMINE_CSV
50
51         # Start transaction management.
52         transaction.commit_unless_managed()
53         transaction.enter_transaction_management()
54         transaction.managed(True)
55
56         redakcja_link = re.compile(re.escape(redakcja) + r'([-_.:?&%/a-zA-Z0-9]*)')
57
58         all_tickets = 0
59         all_chunks = 0
60         done_tickets = 0
61         done_chunks = 0
62         empty_users = 0
63         unknown_users = {}
64         unknown_books = []
65         forced = []
66
67         if verbose:
68             print 'Downloading CSV file'
69         for r in csv.reader(urllib2.urlopen(redmine_csv)):
70             if r[0] == '#':
71                 continue
72             all_tickets += 1
73
74             username = r[6]
75             if not username:
76                 if verbose:
77                     print "Empty user, skipping"
78                 empty_users += 1
79                 continue
80
81             first_name, last_name = unicode(username, 'utf-8').rsplit(u' ', 1)
82             try:
83                 user = User.objects.get(first_name=first_name, last_name=last_name)
84             except User.DoesNotExist:
85                 print self.style.ERROR('Unknown user: ' + username)
86                 unknown_users.setdefault(username, 0)
87                 unknown_users[username] += 1
88                 continue
89
90             ticket_done = False
91             for fname in redakcja_link.findall(r[-1]):
92                 fname = unicode(urllib.unquote(fname), 'utf-8', 'ignore')
93                 if fname.endswith('.xml'):
94                     fname = fname[:-4]
95                 fname = fname.replace(' ', '_')
96                 fname = slughifi(fname)
97
98                 chunks = Chunk.objects.filter(book__slug=fname)
99                 if not chunks:
100                     print self.style.ERROR('Unknown book: ' + fname)
101                     unknown_books.append(fname)
102                     continue
103                 all_chunks += chunks.count()
104
105                 for chunk in chunks:
106                     if chunk.user:
107                         if chunk.user == user:
108                             continue
109                         else:
110                             forced.append((chunk, chunk.user, user))
111                             if force:
112                                 print self.style.WARNING(
113                                     '%s assigned to %s, forcing change to %s.' %
114                                     (chunk.pretty_name(), chunk.user, user))
115                             else:
116                                 print self.style.WARNING(
117                                     '%s assigned to %s not to %s, skipping.' %
118                                     (chunk.pretty_name(), chunk.user, user))
119                                 continue
120                     chunk.user = user
121                     chunk.save()
122                     ticket_done = True
123                     done_chunks += 1
124
125             if ticket_done:
126                 done_tickets += 1
127
128         # Print results
129         print
130         print "Results:"
131         print "Assignments imported from %d/%d tickets to %d/%d relevalt chunks." % (
132                 done_tickets, all_tickets, done_chunks, all_chunks)
133         if empty_users:
134             print "%d tickets were unassigned." % empty_users
135         if forced:
136             print "%d assignments conficts (%s):" % (
137                 len(forced), "changed" if force else "left")
138             for chunk, orig, user in forced:
139                 print "  %s: \t%s \t->  %s" % (
140                     chunk.pretty_name(), orig.username, user.username)
141         if unknown_books:
142             print "%d unknown books:" % len(unknown_books)
143             for fname in unknown_books:
144                 print "  %s" % fname
145         if unknown_users:
146             print "%d unknown users:" % len(unknown_users)
147             for name in unknown_users:
148                 print "  %s (%d tickets)" % (name, unknown_users[name])
149         print
150
151         transaction.commit()
152         transaction.leave_transaction_management()