~ubuntu-branches/ubuntu/utopic/taskcoach/utopic-proposed

« back to all changes in this revision

Viewing changes to .pc/replace_thirdparty_with_deb_packages.diff/taskcoachlib/gui/wizard/csvimport.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2014-08-15 14:54:44 UTC
  • mfrom: (8.1.7 sid)
  • Revision ID: package-import@ubuntu.com-20140815145444-qm2ya2zvy1pyrcl5
Tags: 1.4.0-1ubuntu1
* Merge with Debian; remaining changes:
  - Let the autopkg test depend on language-pack-en.
  - Build-depend on language-pack-en.
* Build using python-wxgtk3.0.
* Don't use /usr/share/pyshared anymore, it's gone.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
'''
2
 
Task Coach - Your friendly task manager
3
 
Copyright (C) 2011 Task Coach developers <developers@taskcoach.org>
4
 
 
5
 
Task Coach is free software: you can redistribute it and/or modify
6
 
it under the terms of the GNU General Public License as published by
7
 
the Free Software Foundation, either version 3 of the License, or
8
 
(at your option) any later version.
9
 
 
10
 
Task Coach is distributed in the hope that it will be useful,
11
 
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
GNU General Public License for more details.
14
 
 
15
 
You should have received a copy of the GNU General Public License
16
 
along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
 
'''
18
 
 
19
 
 
20
 
from taskcoachlib import meta
21
 
from taskcoachlib.i18n import _
22
 
from taskcoachlib.thirdparty import chardet
23
 
import wx
24
 
import csv
25
 
import tempfile
26
 
import wx.grid as gridlib
27
 
import wx.wizard as wiz
28
 
 
29
 
 
30
 
class CSVDialect(csv.Dialect):
31
 
    def __init__(self, delimiter=',', quotechar='"', doublequote=True, escapechar=''):
32
 
        self.delimiter = delimiter
33
 
        self.quotechar = quotechar
34
 
        self.quoting = csv.QUOTE_MINIMAL
35
 
        self.lineterminator = '\r\n'
36
 
        self.doublequote = doublequote
37
 
        self.escapechar = escapechar
38
 
 
39
 
        csv.Dialect.__init__(self)
40
 
 
41
 
 
42
 
class CSVImportOptionsPage(wiz.WizardPageSimple):
43
 
    def __init__(self, filename, *args, **kwargs):
44
 
        super(CSVImportOptionsPage, self).__init__(*args, **kwargs)
45
 
 
46
 
        self.delimiter = wx.Choice(self, wx.ID_ANY)
47
 
        self.delimiter.Append(_('Comma'))
48
 
        self.delimiter.Append(_('Tab'))
49
 
        self.delimiter.Append(_('Space'))
50
 
        self.delimiter.Append(_('Colon'))
51
 
        self.delimiter.Append(_('Semicolon'))
52
 
        self.delimiter.Append(_('Pipe'))
53
 
        self.delimiter.SetSelection(0)
54
 
 
55
 
        self.date = wx.Choice(self)
56
 
        self.date.Append(_('DD/MM (day first)'))
57
 
        self.date.Append(_('MM/DD (month first)'))
58
 
        self.date.SetSelection(0)
59
 
        
60
 
        self.quoteChar = wx.Choice(self, wx.ID_ANY)
61
 
        self.quoteChar.Append(_('Simple quote'))
62
 
        self.quoteChar.Append(_('Double quote'))
63
 
        self.quoteChar.SetSelection(1)
64
 
 
65
 
        self.quotePanel = wx.Panel(self, wx.ID_ANY)
66
 
        self.doubleQuote = wx.RadioButton(self.quotePanel, wx.ID_ANY, _('Double it'))
67
 
        self.doubleQuote.SetValue(True)
68
 
        self.escapeQuote = wx.RadioButton(self.quotePanel, wx.ID_ANY, _('Escape with'))
69
 
        self.escapeChar = wx.TextCtrl(self.quotePanel, wx.ID_ANY, '\\', size=(50, -1))
70
 
        self.escapeChar.Enable(False)
71
 
        self.escapeChar.SetMaxLength(1)
72
 
 
73
 
        hsizer = wx.BoxSizer(wx.HORIZONTAL)
74
 
        hsizer.Add(self.doubleQuote, 1, wx.ALL, 3)
75
 
        hsizer.Add(self.escapeQuote, 1, wx.ALL, 3)
76
 
        hsizer.Add(self.escapeChar, 1, wx.ALL, 3)
77
 
        self.quotePanel.SetSizer(hsizer)
78
 
        
79
 
        self.importSelectedRowsOnly = wx.CheckBox(self, wx.ID_ANY, _('Import only the selected rows'))
80
 
        self.importSelectedRowsOnly.SetValue(False)
81
 
 
82
 
        self.hasHeaders = wx.CheckBox(self, wx.ID_ANY, _('First line describes fields'))
83
 
        self.hasHeaders.SetValue(True)
84
 
 
85
 
        self.grid = gridlib.Grid(self)
86
 
        self.grid.SetRowLabelSize(0)
87
 
        self.grid.SetColLabelSize(0)
88
 
        self.grid.CreateGrid(0, 0)
89
 
        self.grid.EnableEditing(False)
90
 
        self.grid.SetSelectionMode(self.grid.wxGridSelectRows)
91
 
 
92
 
        vsizer = wx.BoxSizer(wx.VERTICAL)
93
 
        gridSizer = wx.FlexGridSizer(0, 2)
94
 
 
95
 
        gridSizer.Add(wx.StaticText(self, wx.ID_ANY, _('Delimiter')), 0, 
96
 
                      wx.ALIGN_CENTRE_VERTICAL | wx.ALL, 3)
97
 
        gridSizer.Add(self.delimiter, 0, wx.ALL, 3)
98
 
        
99
 
        gridSizer.Add(wx.StaticText(self, wx.ID_ANY, _('Date format')), 0, 
100
 
                      wx.ALIGN_CENTER_VERTICAL | wx.ALL, 3)
101
 
        gridSizer.Add(self.date, 0, wx.ALL, 3)
102
 
        
103
 
        gridSizer.Add(wx.StaticText(self, wx.ID_ANY, _('Quote character')), 0, 
104
 
                      wx.ALIGN_CENTRE_VERTICAL | wx.ALL, 3)
105
 
        gridSizer.Add(self.quoteChar, 0, wx.ALL, 3)
106
 
 
107
 
        gridSizer.Add(wx.StaticText(self, wx.ID_ANY, _('Escape quote')), 0, 
108
 
                      wx.ALIGN_CENTRE_VERTICAL | wx.ALL, 3)
109
 
        gridSizer.Add(self.quotePanel, 0, wx.ALL, 3)
110
 
 
111
 
        gridSizer.Add(self.importSelectedRowsOnly, 0, wx.ALL, 3)
112
 
        gridSizer.Add((0, 0))
113
 
 
114
 
        gridSizer.Add(self.hasHeaders, 0, wx.ALL, 3)
115
 
        gridSizer.Add((0, 0))
116
 
 
117
 
        gridSizer.AddGrowableCol(1)
118
 
        vsizer.Add(gridSizer, 0, wx.EXPAND | wx.ALL, 3)
119
 
 
120
 
        vsizer.Add(self.grid, 1, wx.EXPAND | wx.ALL, 3)
121
 
 
122
 
        self.SetSizer(vsizer)
123
 
 
124
 
        self.headers = None
125
 
 
126
 
        self.filename = filename
127
 
        self.encoding = chardet.detect(file(filename, 'rb').read())['encoding']
128
 
        self.OnOptionChanged(None)
129
 
 
130
 
        wx.EVT_CHOICE(self.delimiter, wx.ID_ANY, self.OnOptionChanged)
131
 
        wx.EVT_CHOICE(self.quoteChar, wx.ID_ANY, self.OnOptionChanged)
132
 
        wx.EVT_CHECKBOX(self.importSelectedRowsOnly, wx.ID_ANY, self.OnOptionChanged)
133
 
        wx.EVT_CHECKBOX(self.hasHeaders, wx.ID_ANY, self.OnOptionChanged)
134
 
        wx.EVT_RADIOBUTTON(self.doubleQuote, wx.ID_ANY, self.OnOptionChanged)
135
 
        wx.EVT_RADIOBUTTON(self.escapeQuote, wx.ID_ANY, self.OnOptionChanged)
136
 
        wx.EVT_TEXT(self.escapeChar, wx.ID_ANY, self.OnOptionChanged)
137
 
 
138
 
    def OnOptionChanged(self, event):  # pylint: disable=W0613
139
 
        self.escapeChar.Enable(self.escapeQuote.GetValue())
140
 
 
141
 
        if self.filename is None:
142
 
            self.grid.SetRowLabelSize(0)
143
 
            self.grid.SetColLabelSize(0)
144
 
            if self.grid.GetNumberCols():
145
 
                self.grid.DeleteRows(0, self.grid.GetNumberRows())
146
 
                self.grid.DeleteCols(0, self.grid.GetNumberCols())
147
 
        else:
148
 
            if self.doubleQuote.GetValue():
149
 
                doublequote = True
150
 
                escapechar = ''
151
 
            else:
152
 
                doublequote = False
153
 
                escapechar = self.escapeChar.GetValue().encode('UTF-8')
154
 
            self.dialect = CSVDialect(delimiter={0: ',', 1: '\t', 2: ' ', 3: ':', 4: ';', 5: '|'}[self.delimiter.GetSelection()],
155
 
                                      quotechar={0: "'", 1: '"'}[self.quoteChar.GetSelection()],
156
 
                                      doublequote=doublequote, escapechar=escapechar)
157
 
 
158
 
            fp = tempfile.TemporaryFile()
159
 
            try:
160
 
                fp.write(file(self.filename, 'rU').read().decode(self.encoding).encode('UTF-8'))
161
 
                fp.seek(0)
162
 
 
163
 
                reader = csv.reader(fp, dialect=self.dialect)
164
 
 
165
 
                if self.hasHeaders.GetValue():
166
 
                    self.headers = [header.decode('UTF-8') for header in reader.next()]
167
 
                else:
168
 
                    # In some cases, empty fields are omitted if they're at the end...
169
 
                    hsize = 0
170
 
                    for line in reader:
171
 
                        hsize = max(hsize, len(line))
172
 
                    self.headers = [_('Field #%d') % idx for idx in xrange(hsize)]
173
 
                    fp.seek(0)
174
 
                    reader = csv.reader(fp, dialect=self.dialect)
175
 
 
176
 
                if self.grid.GetNumberCols():
177
 
                    self.grid.DeleteRows(0, self.grid.GetNumberRows())
178
 
                    self.grid.DeleteCols(0, self.grid.GetNumberCols())
179
 
                self.grid.InsertCols(0, len(self.headers))
180
 
 
181
 
                self.grid.SetColLabelSize(20)
182
 
                for idx, header in enumerate(self.headers):
183
 
                    self.grid.SetColLabelValue(idx, header)
184
 
 
185
 
                lineno = 0
186
 
                for line in reader:
187
 
                    self.grid.InsertRows(lineno, 1)
188
 
                    for idx, value in enumerate(line):
189
 
                        if idx < self.grid.GetNumberCols():
190
 
                            self.grid.SetCellValue(lineno, idx, value.decode('UTF-8'))
191
 
                    lineno += 1
192
 
            finally:
193
 
                fp.close()
194
 
 
195
 
    def GetOptions(self):
196
 
        return dict(dialect=self.dialect,
197
 
                    dayfirst=self.date.GetSelection() == 0,
198
 
                    importSelectedRowsOnly=self.importSelectedRowsOnly.GetValue(),
199
 
                    selectedRows=self.GetSelectedRows(),
200
 
                    hasHeaders=self.hasHeaders.GetValue(),
201
 
                    filename=self.filename,
202
 
                    encoding=self.encoding,
203
 
                    fields=self.headers)
204
 
        
205
 
    def GetSelectedRows(self):
206
 
        startRows = [row for row, dummy_column in self.grid.GetSelectionBlockTopLeft()]
207
 
        stopRows = [row for row, dummy_column in self.grid.GetSelectionBlockBottomRight()]
208
 
        selectedRows = []
209
 
        for startRow, stopRow in zip(startRows, stopRows):
210
 
            selectedRows.extend(range(startRow, stopRow + 1))
211
 
        return selectedRows
212
 
 
213
 
    def CanGoNext(self):
214
 
        if self.filename is not None:
215
 
            self.GetNext().SetOptions(self.GetOptions())
216
 
            return True, None
217
 
        return False, _('Please select a file.')
218
 
 
219
 
 
220
 
class CSVImportMappingPage(wiz.WizardPageSimple):
221
 
    def __init__(self, *args, **kwargs):
222
 
        super(CSVImportMappingPage, self).__init__(*args, **kwargs)
223
 
 
224
 
        # (field name, multiple values allowed)
225
 
 
226
 
        self.fields = [
227
 
            (_('None'), True),
228
 
            (_('ID'), False),
229
 
            (_('Subject'), False),
230
 
            (_('Description'), True),
231
 
            (_('Category'), True),
232
 
            (_('Priority'), False),
233
 
            (_('Planned start date'), False),
234
 
            (_('Due date'), False),
235
 
            (_('Actual start date'), False),
236
 
            (_('Completion date'), False),
237
 
            (_('Reminder date'), False),
238
 
            (_('Budget'), False),
239
 
            (_('Fixed fee'), False),
240
 
            (_('Hourly fee'), False),
241
 
            (_('Percent complete'), False),
242
 
            ]
243
 
        self.choices = []
244
 
        self.interior = wx.ScrolledWindow(self)
245
 
        self.interior.EnableScrolling(False, True)
246
 
        self.interior.SetScrollRate(10, 10)
247
 
 
248
 
        sizer = wx.BoxSizer()
249
 
        sizer.Add(self.interior, 1, wx.EXPAND)
250
 
        self.SetSizer(sizer)
251
 
 
252
 
    def SetOptions(self, options):
253
 
        self.options = options
254
 
 
255
 
        if self.interior.GetSizer():
256
 
            self.interior.GetSizer().Clear(True)
257
 
 
258
 
        for child in self.interior.GetChildren():
259
 
            self.interior.RemoveChild(child)
260
 
        self.choices = []
261
 
 
262
 
        gsz = wx.FlexGridSizer(0, 2, 4, 2)
263
 
        
264
 
        gsz.Add(wx.StaticText(self.interior, wx.ID_ANY, _('Column header in CSV file')))
265
 
        gsz.Add(wx.StaticText(self.interior, wx.ID_ANY, _('%s attribute') % meta.name))
266
 
        gsz.AddSpacer((3, 3))
267
 
        gsz.AddSpacer((3, 3))
268
 
        tcFieldNames = [field[0] for field in self.fields]
269
 
        for fieldName in options['fields']:
270
 
            gsz.Add(wx.StaticText(self.interior, wx.ID_ANY, fieldName), flag=wx.ALIGN_CENTER_VERTICAL)
271
 
            
272
 
            choice = wx.Choice(self.interior, wx.ID_ANY)
273
 
            for tcFieldName in tcFieldNames:
274
 
                choice.Append(tcFieldName)
275
 
            choice.SetSelection(self.findFieldName(fieldName, tcFieldNames))
276
 
            self.choices.append(choice)
277
 
 
278
 
            gsz.Add(choice, flag=wx.ALIGN_CENTER_VERTICAL)
279
 
 
280
 
        gsz.AddGrowableCol(1)
281
 
        self.interior.SetSizer(gsz)
282
 
        gsz.Layout()
283
 
        
284
 
    def findFieldName(self, fieldName, fieldNames):
285
 
        def fieldNameIndex(fieldName, fieldNames):
286
 
            return fieldNames.index(fieldName) if fieldName in fieldNames else 0
287
 
        
288
 
        index = fieldNameIndex(fieldName, fieldNames)
289
 
        return index if index else fieldNameIndex(fieldName[:6], [fieldName[:6] for fieldName in fieldNames])
290
 
        
291
 
    def CanGoNext(self):
292
 
        wrongFields = []
293
 
        countNotNone = 0
294
 
 
295
 
        for index, (fieldName, canMultiple) in enumerate(self.fields):
296
 
            count = 0
297
 
            for choice in self.choices:
298
 
                if choice.GetSelection() == index:
299
 
                    count += 1
300
 
                if choice.GetSelection() != 0:
301
 
                    countNotNone += 1
302
 
            if count > 1 and not canMultiple:
303
 
                wrongFields.append(fieldName)
304
 
 
305
 
        if countNotNone == 0:
306
 
            return False, _('No field mapping.')
307
 
 
308
 
        if len(wrongFields) == 1:
309
 
            return False, _('The "%s" field cannot be selected several times.') % wrongFields[0]
310
 
 
311
 
        if len(wrongFields):
312
 
            return False, _('The fields %s cannot be selected several times.') % ', '.join(['"%s"' % fieldName for fieldName in wrongFields])
313
 
 
314
 
        return True, None
315
 
 
316
 
    def GetOptions(self):
317
 
        options = dict(self.options)
318
 
        options['mappings'] = [self.fields[choice.GetSelection()][0] for choice in self.choices]
319
 
        return options
320
 
 
321
 
 
322
 
class CSVImportWizard(wiz.Wizard):
323
 
    def __init__(self, filename, *args, **kwargs):
324
 
        kwargs['style'] = wx.RESIZE_BORDER | wx.DEFAULT_DIALOG_STYLE
325
 
        super(CSVImportWizard, self).__init__(*args, **kwargs)
326
 
 
327
 
        self.optionsPage = CSVImportOptionsPage(filename, self)
328
 
        self.mappingPage = CSVImportMappingPage(self)
329
 
        self.optionsPage.SetNext(self.mappingPage)
330
 
        self.mappingPage.SetPrev(self.optionsPage)
331
 
 
332
 
        self.SetPageSize((600, -1))  # I know this is obsolete but it's the only one that works...
333
 
 
334
 
        wiz.EVT_WIZARD_PAGE_CHANGING(self, wx.ID_ANY, self.OnPageChanging)
335
 
        wiz.EVT_WIZARD_PAGE_CHANGED(self, wx.ID_ANY, self.OnPageChanged)
336
 
 
337
 
    def OnPageChanging(self, event):
338
 
        if event.GetDirection():
339
 
            can, msg = event.GetPage().CanGoNext()
340
 
            if not can:
341
 
                wx.MessageBox(msg, _('Information'), wx.OK)
342
 
                event.Veto()
343
 
 
344
 
    def OnPageChanged(self, event):
345
 
        if event.GetPage() == self.optionsPage:
346
 
            pass  # XXXTODO
347
 
 
348
 
    def RunWizard(self):
349
 
        return super(CSVImportWizard, self).RunWizard(self.optionsPage)
350
 
 
351
 
    def GetOptions(self):
352
 
        return self.mappingPage.GetOptions()