root/trunk/camelot/view/controls/tableview.py @ 1183

Revision 1183, 26.8 KB (checked in by erikj, 6 months ago)

readonly admin also applies on related admins, search takes precision of floats into accounts, tableview supports filters with an initial value

Line 
1#  ============================================================================
2#
3#  Copyright (C) 2007-2008 Conceptive Engineering bvba. All rights reserved.
4#  www.conceptive.be / project-camelot@conceptive.be
5#
6#  This file is part of the Camelot Library.
7#
8#  This file may be used under the terms of the GNU General Public
9#  License version 2.0 as published by the Free Software Foundation
10#  and appearing in the file LICENSE.GPL included in the packaging of
11#  this file.  Please review the following information to ensure GNU
12#  General Public Licensing requirements will be met:
13#  http://www.trolltech.com/products/qt/opensource.html
14#
15#  If you are unsure which license is appropriate for your use, please
16#  review the following information:
17#  http://www.trolltech.com/products/qt/licensing.html or contact
18#  project-camelot@conceptive.be.
19#
20#  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
21#  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22#
23#  For use of this library in commercial applications, please contact
24#  project-camelot@conceptive.be
25#
26#  ============================================================================
27
28"""Tableview"""
29
30import logging
31logger = logging.getLogger( 'camelot.view.controls.tableview' )
32
33from PyQt4 import QtCore, QtGui
34from PyQt4.QtGui import QSizePolicy
35from PyQt4.QtCore import SIGNAL
36from PyQt4.QtCore import Qt
37
38from camelot.view.proxy.queryproxy import QueryTableProxy
39from camelot.view.controls.view import AbstractView
40from camelot.view.controls.user_translatable_label import UserTranslatableLabel
41from camelot.view.model_thread import model_function, gui_function, post
42from camelot.core.utils import ugettext as _
43
44from search import SimpleSearchControl
45
46class TableWidget( QtGui.QTableView):
47    """A widget displaying a table, to be used within a TableView"""
48 
49    def __init__( self, parent = None ):
50        QtGui.QTableView.__init__( self, parent )
51        logger.debug( 'create TableWidget' )
52        self.setSelectionBehavior( QtGui.QAbstractItemView.SelectRows )
53        self.setEditTriggers( QtGui.QAbstractItemView.SelectedClicked | QtGui.QAbstractItemView.DoubleClicked )
54        self.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding )
55        # set to false while sorting is not implemented in CollectionProxy
56        self.horizontalHeader().setClickable( True )
57        self._header_font_required = QtGui.QApplication.font()
58        self._header_font_required.setBold( True )
59        self._minimal_row_height = QtGui.QFontMetrics(QtGui.QApplication.font()).lineSpacing() + 10
60        self.verticalHeader().setDefaultSectionSize( self._minimal_row_height )
61        self.connect( self.horizontalHeader(), QtCore.SIGNAL('sectionClicked(int)'), self.horizontal_section_clicked )
62   
63    def horizontal_section_clicked( self, logical_index ):
64        """Update the sorting of the model and the header"""
65        header = self.horizontalHeader()
66        order = Qt.AscendingOrder
67        if not header.isSortIndicatorShown():
68            header.setSortIndicatorShown( True )
69        elif header.sortIndicatorSection()==logical_index:
70            # apparently, the sort order on the header is allready switched when the section
71            # was clicked, so there is no need to reverse it
72            order = header.sortIndicatorOrder()
73        header.setSortIndicator( logical_index, order )
74        self.model().sort( logical_index, order )
75       
76    def setModel( self, model ):
77        QtGui.QTableView.setModel( self, model )
78        self.connect( self.selectionModel(), SIGNAL( 'currentChanged(const QModelIndex&,const QModelIndex&)' ), self.activated )
79   
80    def activated( self, selectedIndex, previousSelectedIndex ):
81        option = QtGui.QStyleOptionViewItem()
82        newSize = self.itemDelegate( selectedIndex ).sizeHint( option, selectedIndex )
83        row = selectedIndex.row()
84        if previousSelectedIndex.row() >= 0:
85            oldSize = self.itemDelegate( previousSelectedIndex ).sizeHint( option, selectedIndex )
86            previousRow = previousSelectedIndex.row()
87            self.setRowHeight( previousRow, oldSize.height() )
88        self.setRowHeight( row, newSize.height() )
89   
90class RowsWidget( QtGui.QLabel ):
91    """Widget that is part of the header widget, displaying the number of rows
92    in the table view"""
93 
94    _number_of_rows_font = QtGui.QApplication.font()
95 
96    def __init__( self, parent ):
97        QtGui.QLabel.__init__( self, parent )
98        self.setFont( self._number_of_rows_font )
99   
100    def setNumberOfRows( self, rows ):
101        self.setText( _('(%i rows)')%rows )
102   
103class HeaderWidget( QtGui.QWidget ):
104    """HeaderWidget for a tableview, containing the title, the search widget,
105    and the number of rows in the table"""
106 
107    search_widget = SimpleSearchControl
108    rows_widget = RowsWidget
109 
110    _title_font = QtGui.QApplication.font()
111    _title_font.setBold( True )
112 
113    def __init__( self, parent, admin ):
114        QtGui.QWidget.__init__( self, parent )
115        widget_layout = QtGui.QHBoxLayout()
116        self.search = self.search_widget( self )
117        title = UserTranslatableLabel( admin.get_verbose_name_plural(), self )
118        title.setFont( self._title_font )
119        widget_layout.addWidget( title )
120        widget_layout.addWidget( self.search )
121        if self.rows_widget:
122            self.number_of_rows = self.rows_widget( self )
123            widget_layout.addWidget( self.number_of_rows )
124     
125        else:
126            self.number_of_rows = None
127        self.setLayout( widget_layout )
128        self.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed )
129        self.setNumberOfRows( 0 )
130   
131    @gui_function
132    def setNumberOfRows( self, rows ):
133        if self.number_of_rows:
134            self.number_of_rows.setNumberOfRows( rows )
135     
136class TableView( AbstractView  ):
137    """A generic tableview widget that puts together some other widgets.  The behaviour of this class and
138  the resulting interface can be tuned by specifying specific class attributes which define the underlying
139  widgets used ::
140 
141    class MovieRentalTableView(TableView):
142      title_format = 'Grand overview of recent movie rentals'
143 
144  The attributes that can be specified are :
145 
146  .. attribute:: header_widget
147 
148  The widget class to be used as a header in the table view::
149   
150    header_widget = HeaderWidget
151   
152  .. attribute:: table_widget
153 
154  The widget class used to display a table within the table view ::
155 
156  table_widget = TableWidget
157 
158  .. attribute:: title_format
159 
160  A string used to format the title of the view ::
161 
162  title_format = '%(verbose_name_plural)s'
163 
164  .. attribute:: table_model
165 
166  A class implementing QAbstractTableModel that will be used as a model for the table view ::
167 
168    table_model = QueryTableProxy
169 
170  - emits the row_selected signal when a row has been selected
171  """
172 
173    header_widget = HeaderWidget
174    table_widget = TableWidget
175 
176    #
177    # The proxy class to use
178    #
179    table_model = QueryTableProxy
180    #
181    # Format to use as the window title
182    #
183    title_format = '%(verbose_name_plural)s'
184 
185    def __init__( self, admin, search_text = None, parent = None ):
186        AbstractView.__init__( self, parent )
187        self.admin = admin
188        post( self.get_title, self.change_title )
189        widget_layout = QtGui.QVBoxLayout()
190        if self.header_widget:
191            self.header = self.header_widget( self, admin )
192            widget_layout.addWidget( self.header )
193            self.connect( self.header.search, SIGNAL( 'search' ), self.startSearch )
194            self.connect( self.header.search, SIGNAL( 'cancel' ), self.cancelSearch )
195            if search_text:
196                self.header.search.search( search_text )
197        else:
198            self.header = None
199        widget_layout.setSpacing( 0 )
200        widget_layout.setMargin( 0 )
201        self.splitter = QtGui.QSplitter( self )
202        widget_layout.addWidget( self.splitter )
203        table_widget = QtGui.QWidget( self )
204        filters_widget = QtGui.QWidget( self )
205        self.table_layout = QtGui.QVBoxLayout()
206        self.table_layout.setSpacing( 0 )
207        self.table_layout.setMargin( 0 )
208        self.table = None
209        self.filters_layout = QtGui.QVBoxLayout()
210        self.filters_layout.setSpacing( 0 )
211        self.filters_layout.setMargin( 0 )     
212        self.filters = None
213        self.actions = None
214        self._table_model = None
215        table_widget.setLayout( self.table_layout )
216        filters_widget.setLayout( self.filters_layout )
217        #filters_widget.hide()
218        self.set_admin( admin )
219        self.splitter.addWidget( table_widget )
220        self.splitter.addWidget( filters_widget )
221        self.setLayout( widget_layout )
222        self.closeAfterValidation = QtCore.SIGNAL( 'closeAfterValidation()' )
223        self.search_filter = lambda q: q
224        shortcut = QtGui.QShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Find), self)
225        self.connect( shortcut, QtCore.SIGNAL( 'activated()' ), self.activate_search )
226        # give the table widget focus to prevent the header and its search control to
227        # receive default focus, as this would prevent the displaying of 'Search...' in the
228        # search control, but this conflicts with the MDI, resulting in the window not
229        # being active and the menus not to work properly
230        #table_widget.setFocus( QtCore.Qt.OtherFocusReason )
231        #self.setFocusProxy(table_widget)
232        #self.setFocus( QtCore.Qt.OtherFocusReason )
233        post( self.admin.get_subclass_tree, self.setSubclassTree )
234   
235    def activate_search(self):
236        self.header.search.setFocus(QtCore.Qt.ShortcutFocusReason)
237       
238    @model_function
239    def get_title( self ):
240        return self.title_format % {'verbose_name_plural':self.admin.get_verbose_name_plural()}
241   
242    @gui_function
243    def setSubclassTree( self, subclasses ):
244        if len( subclasses ) > 0:
245            from inheritance import SubclassTree
246            class_tree = SubclassTree( self.admin, self.splitter )
247            self.splitter.insertWidget( 0, class_tree )
248            self.connect( class_tree, SIGNAL( 'subclassClicked' ), self.set_admin )
249     
250    def sectionClicked( self, section ):
251        """emits a row_selected signal"""
252        self.emit( SIGNAL( 'row_selected' ), section )
253   
254    def copy_selected_rows( self ):
255        """Copy the selected rows in this tableview"""
256        logger.debug( 'delete selected rows called' )
257        if self.table and self._table_model:
258            for row in set( map( lambda x: x.row(), self.table.selectedIndexes() ) ):
259                self._table_model.copy_row( row )
260           
261    def create_table_model( self, admin ):
262        """Create a table model for the given admin interface"""
263        return self.table_model( admin,
264                                 admin.get_query,
265                                 admin.get_columns )
266   
267    @gui_function
268    def set_admin( self, admin ):
269        """Switch to a different subclass, where admin is the admin object of the
270        subclass"""
271        logger.debug('set_admin called')
272        self.admin = admin
273        if self.table:
274            self.disconnect(self._table_model, QtCore.SIGNAL( 'layoutChanged()' ), self.tableLayoutChanged )
275            self.table_layout.removeWidget(self.table)
276            self.table.deleteLater()
277            self._table_model.deleteLater()
278        self.table = self.table_widget( self.splitter )
279        self._table_model = self.create_table_model( admin )
280        self.table.setModel( self._table_model )
281        self.connect( self.table.verticalHeader(),
282                      SIGNAL( 'sectionClicked(int)' ),
283                      self.sectionClicked )
284        self.connect( self._table_model, QtCore.SIGNAL( 'layoutChanged()' ), self.tableLayoutChanged )
285        self.tableLayoutChanged()
286        self.table_layout.insertWidget( 1, self.table )
287   
288        def get_filters_and_actions():
289            return ( admin.get_filters(), admin.get_list_actions() )
290     
291        post( get_filters_and_actions,  self.set_filters_and_actions )
292        post( admin.get_list_charts, self.setCharts )
293   
294    @gui_function
295    def tableLayoutChanged( self ):
296        logger.debug('tableLayoutChanged')
297        if self.header:
298            self.header.setNumberOfRows( self._table_model.rowCount() )
299        item_delegate = self._table_model.getItemDelegate()
300        if item_delegate:
301            self.table.setItemDelegate( item_delegate )
302        for i in range( self._table_model.columnCount() ):
303            self.table.setColumnWidth( i, max( self._table_model.headerData( i, Qt.Horizontal, Qt.SizeHintRole ).toSize().width(),
304                                               self.table.columnWidth( i ) ) )
305     
306    @gui_function
307    def setCharts( self, charts ):
308        """creates and display charts"""
309        pass
310#    if charts:
311#
312#      from matplotlib.figure import Figure
313#      from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as \
314#                                                     FigureCanvas
315#
316#      chart = charts[0]
317#
318#      def getData():
319#        """fetches data for chart"""
320#        from sqlalchemy.sql import select, func
321#        from elixir import session
322#        xcol = getattr( self.admin.entity, chart['x'] )
323#        ycol = getattr( self.admin.entity, chart['y'] )
324#        session.bind = self.admin.entity.table.metadata.bind
325#        result = session.execute( select( [xcol, func.sum( ycol )] ).group_by( xcol ) )
326#        summary = result.fetchall()
327#        return [s[0] for s in summary], [s[1] for s in summary]
328#
329#      class MyMplCanvas( FigureCanvas ):
330#        """Ultimately, this is a QWidget (as well as a FigureCanvasAgg)"""
331#
332#        def __init__( self, parent = None, width = 5, height = 4, dpi = 100 ):
333#          fig = Figure( figsize = ( width, height ), dpi = dpi, facecolor = 'w' )
334#          self.axes = fig.add_subplot( 111, axisbg = 'w' )
335#          # We want the axes cleared every time plot() is called
336#          self.axes.hold( False )
337#          self.compute_initial_figure()
338#          FigureCanvas.__init__( self, fig )
339#          self.setParent( parent )
340#          FigureCanvas.setSizePolicy( self,
341#                                     QSizePolicy.Expanding,
342#                                     QSizePolicy.Expanding )
343#          FigureCanvas.updateGeometry( self )
344#
345#
346#        def compute_initial_figure( self ):
347#          pass
348#
349#      def setData( data ):
350#        """set chart data"""
351#
352#        class MyStaticMplCanvas( MyMplCanvas ):
353#          """simple canvas with a sine plot"""
354#
355#          def compute_initial_figure( self ):
356#            """computes initial figure"""
357#            x, y = data
358#            bar_positions = [i - 0.25 for i in range( 1, len( x ) + 1 )]
359#            width = 0.5
360#            self.axes.bar( bar_positions, y, width, color = 'b' )
361#            self.axes.set_xlabel( 'Year' )
362#            self.axes.set_ylabel( 'Sales' )
363#            self.axes.set_xticks( range( len( x ) + 1 ) )
364#            self.axes.set_xticklabels( [''] + [str( d ) for d in x] )
365#
366#        sc = MyStaticMplCanvas( self, width = 5, height = 4, dpi = 100 )
367#        self.table_layout.addWidget( sc )
368#
369#      self.admin.mt.post( getData, setData )
370
371    def deleteSelectedRows( self ):
372        """delete the selected rows in this tableview"""
373        logger.debug( 'delete selected rows called' )
374        confirmation_message = self.admin.get_confirm_delete()
375        confirmed = True
376        if confirmation_message:
377            if QtGui.QMessageBox.question(self,
378                                          _('Please confirm'),
379                                          unicode(confirmation_message),
380                                          QtGui.QMessageBox.Yes,
381                                          QtGui.QMessageBox.No) == QtGui.QMessageBox.No:
382                confirmed = False
383        if confirmed:
384            for row in set( map( lambda x: x.row(), self.table.selectedIndexes() ) ):
385                self._table_model.removeRow( row )
386     
387    @gui_function
388    def newRow( self ):
389        """Create a new row in the tableview"""
390        from camelot.view.workspace import get_workspace
391        workspace = get_workspace()
392        form = self.admin.create_new_view( workspace,
393                                           oncreate = lambda o:self._table_model.insertEntityInstance( 0, o ),
394                                           onexpunge = lambda o:self._table_model.removeEntityInstance( o ) )
395        workspace.addSubWindow( form )
396        form.show()
397   
398
399   
400#    @gui_function
401#    def set_filters_and_actions( self, filters_and_actions ):
402#        """sets filters for the tableview"""
403#        filters, actions = filters_and_actions
404#        from filterlist import FilterList
405#        from actionsbox import ActionsBox
406#        logger.debug( 'setting filters for tableview' )
407#        if self.filters:
408#          self.disconnect( self.filters, SIGNAL( 'filters_changed' ), self.rebuildQuery )
409#          self.filters.deleteLater()
410#          self.filters = None
411#        if self.actions:
412#          self.actions.deleteLater()
413#          self.actions = None         
414#        if filters:
415#          self.filters = FilterList( filters, parent=self )
416#          self.splitter.insertWidget( 2, self.filters )
417#          self.connect( self.filters, SIGNAL( 'filters_changed' ), self.rebuildQuery )
418#        if actions:
419#          self.actions = ActionsBox( self, self._table_model.collection_getter, lambda:[] )
420#          self.actions.setActions( actions )
421#          self.splitter.insertWidget( 2, self.filters )
422
423    def toHtml( self ):
424        """generates html of the table"""
425        table = [[getattr( row, col[0] ) for col in self.admin.getColumns()]
426                 for row in self.admin.entity.query.all()]
427        context = {
428          'title': self.admin.get_verbose_name_plural(),
429          'table': table,
430          'columns': [c[0] for c in self.admin.getColumns()],
431        }
432        from camelot.view.templates import loader
433        from jinja import Environment, FileSystemLoader
434        env = Environment( loader = loader )
435        tp = env.get_template( 'table_view.html' )
436        return tp.render( context )
437
438    def closeEvent( self, event ):
439        """reimplements close event"""
440        logger.debug( 'tableview closed' )
441        # remove all references we hold, to enable proper garbage collection
442        del self.table_layout
443        del self.table
444        del self.filters
445        del self._table_model
446        event.accept()
447   
448    def importWizard(self, attributes):
449        from camelot.view.wizard.import_data import ImportWizard
450        #object_attributes = ['title', 'releasedate', 'name', 'description' ]
451        object_attributes = self.admin.entity().Admin.form_display.get_fields()
452        importWizard = ImportWizard( self, object_attributes )
453        importWizard.start()
454        data = importWizard.getImportedData()   
455   
456    def selectTableRow( self, row ):
457        """selects the specified row"""
458        self.table.selectRow( row )
459   
460    def makeImport():
461        pass
462#        for row in data:
463#            o = self.admin.entity()
464#            #For example, setattr(x, 'foobar', 123) is equivalent to x.foobar = 123
465#            # if you want to import all attributes, you must link them to other objects
466#            #for example: a movie has a director, this isn't a primitive like a string
467#            # but a object fetched from the db
468#            setattr(o, object_attributes[0], row[0])
469#            name = row[2].split( ' ' ) #director
470#            o.short_description = "korte beschrijving"
471#            o.genre = ""
472#            from sqlalchemy.orm.session import Session
473#            Session.object_session(o).flush([o])
474   
475    post( makeImport )
476   
477    def selectedTableIndexes( self ):
478        """returns a list of selected rows indexes"""
479        return self.table.selectedIndexes()
480   
481    def getColumns( self ):
482        """return the columns to be displayed in the table view"""
483        return self.admin.get_columns()
484   
485    def getData( self ):
486        """generator for data queried by table model"""
487        for d in self._table_model.getData():
488            yield d
489     
490    def getTitle( self ):
491        """return the name of the entity managed by the admin attribute"""
492        return self.admin.get_verbose_name()
493   
494    def viewFirst( self ):
495        """selects first row"""
496        self.selectTableRow( 0 )
497   
498    def viewLast( self ):
499        """selects last row"""
500        self.selectTableRow( self._table_model.rowCount() - 1 )
501   
502    def viewNext( self ):
503        """selects next row"""
504        first = self.selectedTableIndexes()[0]
505        next = ( first.row() + 1 ) % self._table_model.rowCount()
506        self.selectTableRow( next )
507   
508    def viewPrevious( self ):
509        """selects previous row"""
510        first = self.selectedTableIndexes()[0]
511        prev = ( first.row() - 1 ) % self._table_model.rowCount()
512        self.selectTableRow( prev )
513   
514    def rebuildQuery( self ):
515        """resets the table model query"""
516   
517        def rebuild_query():
518            query = self.admin.entity.query
519            if self.filters:
520                query = self.filters.decorate_query( query )
521            if self.search_filter:
522                query = self.search_filter( query )
523            query_getter = lambda:query
524            return query_getter
525     
526        post( rebuild_query, self._table_model.setQuery )
527   
528    def startSearch( self, text ):
529        """rebuilds query based on filtering text"""
530        from camelot.view.search import create_entity_search_query_decorator
531        logger.debug( 'search %s' % text )
532        self.search_filter = create_entity_search_query_decorator( self.admin, text )
533        self.rebuildQuery()
534   
535    def cancelSearch( self ):
536        """resets search filtering to default"""
537        logger.debug( 'cancel search' )
538        self.search_filter = lambda q: q
539        self.rebuildQuery()
540       
541    @gui_function
542    def set_filters_and_actions( self, filters_and_actions ):
543        """sets filters for the tableview"""
544        filters, actions = filters_and_actions
545        from filterlist import FilterList
546        from actionsbox import ActionsBox
547        logger.debug( 'setting filters for tableview' )
548       
549        if self.filters:
550            self.disconnect( self.filters, SIGNAL( 'filters_changed' ), self.rebuildQuery )
551            self.filters_layout.removeWidget(self.filters)
552            self.filters.deleteLater()
553            self.filters = None
554        if self.actions:
555            self.filters_layout.removeWidget(self.actions)
556            self.actions.deleteLater()
557            self.actions = None           
558        if filters:
559            self.filters = FilterList( filters, parent=self.splitter )
560            self.filters_layout.addWidget( self.filters )
561            self.connect( self.filters, SIGNAL( 'filters_changed' ), self.rebuildQuery )
562            #
563            # filters might have default values, so we need to rebuild the queries
564            #
565            self.rebuildQuery()
566        if actions:
567           
568            def selection_getter():
569                selection = []
570                for row in set( map( lambda x: x.row(), self.table.selectedIndexes() ) ):
571                    selection.append( self._table_model._get_object(row) )
572                return selection
573           
574            self.actions = ActionsBox( self,
575                                       self._table_model.get_collection_getter(),
576                                       selection_getter )
577           
578            self.actions.setActions( actions )
579            self.filters_layout.addWidget( self.actions )
580     
581    def toHtml( self ):
582        """generates html of the table"""
583        table = [[getattr( row, col[0] ) for col in self.admin.get_columns()]
584                 for row in self.admin.entity.query.all()]
585        context = {
586          'title': self.admin.get_verbose_name_plural(),
587          'table': table,
588          'columns': [field_attributes['name'] for _field, field_attributes in self.admin.get_columns()],
589        }
590        from camelot.view.templates import loader
591        from jinja import Environment
592        env = Environment( loader = loader )
593        tp = env.get_template( 'table_view.html' )
594        return tp.render( context )
595   
596    def closeEvent( self, event ):
597        """reimplements close event"""
598        logger.debug( 'tableview closed' )
599        # remove all references we hold, to enable proper garbage collection
600        del self.table_layout
601        del self.table
602        del self.filters
603        del self._table_model
604        event.accept()
605   
606    def importWizard(self, attributes):
607        from camelot.view.wizard.import_data import ImportWizard
608        object_attributes = ['title', 'releasedate', 'name', 'description' ]
609        importWizard = ImportWizard( self, object_attributes )
610        importWizard.start()
611        data = importWizard.getImportedData()   
612        def makeImport():
613            for row in data:
614                # get all possible fields (=attributes) from this object
615                #attributes = o.Admin.form_display.get_fields()
616                #object_attributes = ['title', 'releasedate', 'name', 'description' ]           
617                #print attributes
618                # set title
619                o = self.admin.entity()
620                #For example, setattr(x, 'foobar', 123) is equivalent to x.foobar = 123
621                setattr(o, object_attributes[0], row[0])
622                #movie.title = row[0]
623                name = row[2].split( ' ' ) #director
624                #director = Person()
625                #director.first_name = name[0] 
626                #director.last_name = name[1]
627                #movie.director = director
628                o.short_description = "korte beschrijving"
629                #date = row[1].split('/') # date 12/03/2009
630                #o.releasedate = datetime.date(year=int(date[2]), month=int(date[1]), day=int(date[0]))
631                o.genre = ""
632                #print o[attributes[0]]
633                from sqlalchemy.orm.session import Session
634                Session.object_session(o).flush([o])
635   
636        post( makeImport )
637           
638    def importFromFile( self ):
639        """"import data : the data will be imported in the activeMdiChild """
640        logger.info( 'call import method' )
641        from camelot.view.wizard.importwizard import ImportWizard
642        wizard = ImportWizard(self, self.admin)
643        wizard.exec_()
Note: See TracBrowser for help on using the browser.