root/galaxy-central/lib/galaxy/webapps/community/controllers/common.py

リビジョン 2, 31.1 KB (コミッタ: hatakeyama, 14 年 前)

import galaxy-central

行番号 
1import tarfile
2from galaxy.web.base.controller import *
3from galaxy.webapps.community import model
4from galaxy.model.orm import *
5from galaxy.web.framework.helpers import time_ago, iff, grids
6from galaxy.web.form_builder import SelectField
7from galaxy.model.item_attrs import UsesItemRatings
8import logging
9log = logging.getLogger( __name__ )
10
11# States for passing messages
12SUCCESS, INFO, WARNING, ERROR = "done", "info", "warning", "error"
13
14class ItemRatings( UsesItemRatings ):
15    """Overrides rate_item method since we also allow for comments"""
16    def rate_item( self, trans, user, item, rating, comment='' ):
17        """ Rate an item. Return type is <item_class>RatingAssociation. """
18        item_rating = self.get_user_item_rating( trans.sa_session, user, item, webapp_model=trans.model )
19        if not item_rating:
20            # User has not yet rated item; create rating.
21            item_rating_assoc_class = self._get_item_rating_assoc_class( item, webapp_model=trans.model )
22            item_rating = item_rating_assoc_class()
23            item_rating.user = trans.user
24            item_rating.set_item( item )
25            item_rating.rating = rating
26            item_rating.comment = comment
27            trans.sa_session.add( item_rating )
28            trans.sa_session.flush()
29        elif item_rating.rating != rating or item_rating.comment != comment:
30            # User has previously rated item; update rating.
31            item_rating.rating = rating
32            item_rating.comment = comment
33            trans.sa_session.flush()
34        return item_rating
35
36class ToolListGrid( grids.Grid ):
37    class NameColumn( grids.TextColumn ):
38        def get_value( self, trans, grid, tool ):
39            return tool.name
40    class TypeColumn( grids.BooleanColumn ):
41        def get_value( self, trans, grid, tool ):
42            if tool.is_suite:
43                return 'Suite'
44            return 'Tool'
45    class VersionColumn( grids.TextColumn ):
46        def get_value( self, trans, grid, tool ):
47            return tool.version
48    class DescriptionColumn( grids.TextColumn ):
49        def get_value( self, trans, grid, tool ):
50            return tool.description
51    class CategoryColumn( grids.TextColumn ):
52        def get_value( self, trans, grid, tool ):
53            rval = '<ul>'
54            if tool.categories:
55                for tca in tool.categories:
56                    rval += '<li><a href="browse_tools?operation=tools_by_category&id=%s&webapp=community">%s</a></li>' \
57                        % ( trans.security.encode_id( tca.category.id ), tca.category.name )
58            else:
59                rval += '<li>not set</li>'
60            rval += '</ul>'
61            return rval
62    class ToolCategoryColumn( grids.GridColumn ):
63        def filter( self, trans, user, query, column_filter ):
64            """Modify query to filter by category."""
65            if column_filter == "All":
66                pass
67            return query.filter( model.Category.name == column_filter )
68    class UserColumn( grids.TextColumn ):
69        def get_value( self, trans, grid, tool ):
70            if tool.user:
71                return tool.user.username
72            return 'no user'
73    class EmailColumn( grids.TextColumn ):
74        def filter( self, trans, user, query, column_filter ):
75            if column_filter == 'All':
76                return query
77            return query.filter( and_( model.Tool.table.c.user_id == model.User.table.c.id,
78                                       model.User.table.c.email == column_filter ) )
79    # Grid definition
80    title = "Tools"
81    model_class = model.Tool
82    template='/webapps/community/tool/grid.mako'
83    default_sort_key = "name"
84    columns = [
85        NameColumn( "Name",
86                    key="Tool.name",
87                    link=( lambda item: dict( operation="view_tool", id=item.id, webapp="community" ) ),
88                    attach_popup=False
89                    ),
90        TypeColumn( "Type",
91                     key="suite",
92                     attach_popup=False ),
93        VersionColumn( "Version",
94                       key="version",
95                       attach_popup=False,
96                       filterable="advanced" ),
97        DescriptionColumn( "Description",
98                           key="description",
99                           attach_popup=False
100                           ),
101        CategoryColumn( "Category",
102                        model_class=model.Category,
103                        key="Category.name",
104                        attach_popup=False ),
105        UserColumn( "Uploaded By",
106                     model_class=model.User,
107                     link=( lambda item: dict( operation="tools_by_user", id=item.id, webapp="community" ) ),
108                     attach_popup=False,
109                     key="username" ),
110        grids.CommunityRatingColumn( "Average Rating",
111                                     key="rating" ),
112        # Columns that are valid for filtering but are not visible.
113        EmailColumn( "Email",
114                     model_class=model.User,
115                     key="email",
116                     visible=False ),
117        ToolCategoryColumn( "Category",
118                            model_class=model.Category,
119                            key="Category.name",
120                            visible=False )
121    ]
122    columns.append( grids.MulticolFilterColumn( "Search",
123                                                cols_to_filter=[ columns[0], columns[1], columns[2] ],
124                                                key="free-text-search",
125                                                visible=False,
126                                                filterable="standard" ) )
127    operations = []
128    standard_filters = []
129    default_filter = {}
130    num_rows_per_page = 50
131    preserve_state = False
132    use_paging = True
133    def build_initial_query( self, trans, **kwd ):
134        return trans.sa_session.query( self.model_class ) \
135                               .join( model.User.table ) \
136                               .join( model.ToolEventAssociation.table ) \
137                               .join( model.Event.table ) \
138                               .outerjoin( model.ToolCategoryAssociation.table ) \
139                               .outerjoin( model.Category.table )
140
141class CategoryListGrid( grids.Grid ):
142    class NameColumn( grids.TextColumn ):
143        def get_value( self, trans, grid, category ):
144            return category.name
145    class DescriptionColumn( grids.TextColumn ):
146        def get_value( self, trans, grid, category ):
147            return category.description
148    class ToolsColumn( grids.TextColumn ):
149        def get_value( self, trans, grid, category ):
150            if category.tools:
151                viewable_tools = 0
152                for tca in category.tools:
153                    viewable_tools += 1
154                return viewable_tools
155            return 0
156
157    # Grid definition
158    webapp = "community"
159    title = "Categories"
160    model_class = model.Category
161    template='/webapps/community/category/grid.mako'
162    default_sort_key = "name"
163    columns = [
164        NameColumn( "Name",
165                    key="name",
166                    link=( lambda item: dict( operation="tools_by_category", id=item.id, webapp="community" ) ),
167                    attach_popup=False,
168                    filterable="advanced"
169                  ),
170        DescriptionColumn( "Description",
171                    key="description",
172                    attach_popup=False,
173                    filterable="advanced"
174                  ),
175        # Columns that are valid for filtering but are not visible.
176        grids.DeletedColumn( "Deleted",
177                             key="deleted",
178                             visible=False,
179                             filterable="advanced" ),
180        ToolsColumn( "Tools",
181                     model_class=model.Tool,
182                     attach_popup=False )
183    ]
184    columns.append( grids.MulticolFilterColumn( "Search",
185                                                cols_to_filter=[ columns[0], columns[1] ],
186                                                key="free-text-search",
187                                                visible=False,
188                                                filterable="standard" ) )
189
190    # Override these
191    global_actions = []
192    operations = []
193    standard_filters = []
194    num_rows_per_page = 50
195    preserve_state = False
196    use_paging = True
197               
198class CommonController( BaseController, ItemRatings ):
199    @web.expose
200    def edit_tool( self, trans, cntrller, **kwd ):
201        params = util.Params( kwd )
202        message = util.restore_text( params.get( 'message', ''  ) )
203        status = params.get( 'status', 'done' )
204        id = params.get( 'id', None )
205        if not id:
206            return trans.response.send_redirect( web.url_for( controller=cntrller,
207                                                              action='browse_tools',
208                                                              message='Select a tool to edit',
209                                                              status='error' ) )
210        tool = get_tool( trans, id )
211        can_edit = trans.app.security_agent.can_edit( trans.user, trans.user_is_admin(), cntrller, tool )
212        if not can_edit:
213            return trans.response.send_redirect( web.url_for( controller=cntrller,
214                                                              action='browse_tools',
215                                                              message='You are not allowed to edit this tool',
216                                                              status='error' ) )
217        if params.get( 'edit_tool_button', False ):
218            if params.get( 'in_categories', False ):
219                in_categories = [ trans.sa_session.query( trans.app.model.Category ).get( x ) for x in util.listify( params.in_categories ) ]
220                trans.app.security_agent.set_entity_category_associations( tools=[ tool ], categories=in_categories )
221            else:
222                # There must not be any categories associated with the tool
223                trans.app.security_agent.set_entity_category_associations( tools=[ tool ], categories=[] )
224            user_description = util.restore_text( params.get( 'user_description', '' ) )
225            if user_description:
226                tool.user_description = user_description
227            else:
228                tool.user_description = ''
229            trans.sa_session.add( tool )
230            trans.sa_session.flush()
231            message = "Tool '%s' description and category associations have been saved" % tool.name
232            return trans.response.send_redirect( web.url_for( controller='common',
233                                                              action='edit_tool',
234                                                              cntrller=cntrller,
235                                                              id=id,
236                                                              message=message,
237                                                              status='done' ) )
238        elif params.get( 'approval_button', False ):
239            user_description = util.restore_text( params.get( 'user_description', '' ) )
240            if user_description:
241                tool.user_description = user_description
242                if params.get( 'in_categories', False ):
243                    in_categories = [ trans.sa_session.query( trans.app.model.Category ).get( x ) for x in util.listify( params.in_categories ) ]
244                    trans.app.security_agent.set_entity_category_associations( tools=[ tool ], categories=in_categories )
245                else:
246                    # There must not be any categories associated with the tool
247                    trans.app.security_agent.set_entity_category_associations( tools=[ tool ], categories=[] )
248                trans.sa_session.add( tool )
249                trans.sa_session.flush()
250                # Move the state from NEW to WAITING
251                event = trans.app.model.Event( state=trans.app.model.Tool.states.WAITING )
252                tea = trans.app.model.ToolEventAssociation( tool, event )
253                trans.sa_session.add_all( ( event, tea ) )
254                trans.sa_session.flush()
255                message = "Tool '%s' has been submitted for approval and can no longer be modified" % ( tool.name )
256                return trans.response.send_redirect( web.url_for( controller='common',
257                                                                  action='view_tool',
258                                                                  cntrller=cntrller,
259                                                                  id=id,
260                                                                  message=message,
261                                                                  status='done' ) )
262            else:
263                # The user_description field is required when submitting for approval
264                message = 'A user description is required prior to approval.'
265                status = 'error'
266        in_categories = []
267        out_categories = []
268        for category in get_categories( trans ):
269            if category in [ x.category for x in tool.categories ]:
270                in_categories.append( ( category.id, category.name ) )
271            else:
272                out_categories.append( ( category.id, category.name ) )
273        if tool.is_rejected:
274            # Include the comments regarding the reason for rejection
275            reason_for_rejection = tool.latest_event.comment
276        else:
277            reason_for_rejection = ''
278        can_approve_or_reject = trans.app.security_agent.can_approve_or_reject( trans.user, trans.user_is_admin(), cntrller, tool )
279        can_delete = trans.app.security_agent.can_delete( trans.user, trans.user_is_admin(), cntrller, tool )
280        can_download = trans.app.security_agent.can_download( trans.user, trans.user_is_admin(), cntrller, tool )
281        can_purge = trans.app.security_agent.can_purge( trans.user, trans.user_is_admin(), cntrller )
282        can_upload_new_version = trans.app.security_agent.can_upload_new_version( trans.user, tool )
283        can_view = trans.app.security_agent.can_view( trans.user, trans.user_is_admin(), cntrller, tool )
284        return trans.fill_template( '/webapps/community/tool/edit_tool.mako',
285                                    cntrller=cntrller,
286                                    tool=tool,
287                                    id=id,
288                                    in_categories=in_categories,
289                                    out_categories=out_categories,
290                                    can_approve_or_reject=can_approve_or_reject,
291                                    can_delete=can_delete,
292                                    can_download=can_download,
293                                    can_edit=can_edit,
294                                    can_purge=can_purge,
295                                    can_upload_new_version=can_upload_new_version,
296                                    can_view=can_view,
297                                    reason_for_rejection=reason_for_rejection,
298                                    message=message,
299                                    status=status )
300    @web.expose
301    def view_tool( self, trans, cntrller, **kwd ):
302        params = util.Params( kwd )
303        message = util.restore_text( params.get( 'message', ''  ) )
304        status = params.get( 'status', 'done' )
305        id = params.get( 'id', None )
306        if not id:
307            return trans.response.send_redirect( web.url_for( controller=cntrller,
308                                                              action='browse_tools',
309                                                              message='Select a tool to view',
310                                                              status='error' ) )
311        tool = get_tool( trans, id )
312        can_view = trans.app.security_agent.can_view( trans.user, trans.user_is_admin(), cntrller, tool )
313        if not can_view:
314            return trans.response.send_redirect( web.url_for( controller=cntrller,
315                                                              action='browse_tools',
316                                                              message='You are not allowed to view this tool',
317                                                              status='error' ) )
318        avg_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, tool, webapp_model=trans.model )
319        can_approve_or_reject = trans.app.security_agent.can_approve_or_reject( trans.user, trans.user_is_admin(), cntrller, tool )
320        can_delete = trans.app.security_agent.can_delete( trans.user, trans.user_is_admin(), cntrller, tool )
321        can_download = trans.app.security_agent.can_download( trans.user, trans.user_is_admin(), cntrller, tool )
322        can_edit = trans.app.security_agent.can_edit( trans.user, trans.user_is_admin(), cntrller, tool )
323        can_purge = trans.app.security_agent.can_purge( trans.user, trans.user_is_admin(), cntrller )
324        can_rate = trans.app.security_agent.can_rate( trans.user, trans.user_is_admin(), cntrller, tool )
325        can_upload_new_version = trans.app.security_agent.can_upload_new_version( trans.user, tool )
326        categories = [ tca.category for tca in tool.categories ]
327        display_reviews = util.string_as_bool( params.get( 'display_reviews', False ) )
328        tool_file_contents = tarfile.open( tool.file_name, 'r' ).getnames()
329        tra = self.get_user_item_rating( trans.sa_session, trans.user, tool, webapp_model=trans.model )
330        visible_versions = trans.app.security_agent.get_visible_versions( trans.user, trans.user_is_admin(), cntrller, tool )
331        if tool.is_rejected:
332            # Include the comments regarding the reason for rejection
333            reason_for_rejection = tool.latest_event.comment
334        else:
335            reason_for_rejection = ''
336        return trans.fill_template( '/webapps/community/tool/view_tool.mako',
337                                    avg_rating=avg_rating,
338                                    categories=categories,
339                                    can_approve_or_reject=can_approve_or_reject,
340                                    can_delete=can_delete,
341                                    can_download=can_download,
342                                    can_edit=can_edit,
343                                    can_purge=can_purge,
344                                    can_rate=can_rate,
345                                    can_upload_new_version=can_upload_new_version,
346                                    can_view=can_view,
347                                    cntrller=cntrller,
348                                    display_reviews=display_reviews,
349                                    num_ratings=num_ratings,
350                                    reason_for_rejection=reason_for_rejection,
351                                    tool=tool,
352                                    tool_file_contents=tool_file_contents,
353                                    tra=tra,
354                                    visible_versions=visible_versions,
355                                    message=message,
356                                    status=status )
357    @web.expose
358    def delete_tool( self, trans, cntrller, **kwd ):
359        params = util.Params( kwd )
360        message = util.restore_text( params.get( 'message', ''  ) )
361        status = params.get( 'status', 'done' )
362        id = params.get( 'id', None )
363        if not id:
364            message='Select a tool to delete'
365            status='error'
366        else:
367            tool = get_tool( trans, id )
368            if not trans.app.security_agent.can_delete( trans.user, trans.user_is_admin(), cntrller, tool ):
369                return trans.response.send_redirect( web.url_for( controller=cntrller,
370                                                                  action='browse_tools',
371                                                                  message='You are not allowed to delete this tool',
372                                                                  status='error' ) )
373            # Create a new event
374            event = trans.model.Event( state=trans.model.Tool.states.DELETED )
375            # Flush so we can get an event id
376            trans.sa_session.add( event )
377            trans.sa_session.flush()
378            # Associate the tool with the event
379            tea = trans.model.ToolEventAssociation( tool=tool, event=event )
380            # Delete the tool, keeping state for categories, events and versions
381            tool.deleted = True
382            trans.sa_session.add_all( ( tool, tea ) )
383            trans.sa_session.flush()
384            # TODO: What if the tool has versions, should they all be deleted?
385            message = "Tool '%s' has been marked deleted" % tool.name
386            status = 'done'
387        return trans.response.send_redirect( web.url_for( controller=cntrller,
388                                                          action='browse_tools',
389                                                          message=message,
390                                                          status=status ) )
391    @web.expose
392    def download_tool( self, trans, cntrller, **kwd ):
393        params = util.Params( kwd )
394        id = params.get( 'id', None )
395        if not id:
396            return trans.response.send_redirect( web.url_for( controller='tool',
397                                                              action='browse_tools',
398                                                              message='Select a tool to download',
399                                                              status='error' ) )
400        tool = get_tool( trans, id )
401        if not trans.app.security_agent.can_download( trans.user, trans.user_is_admin(), cntrller, tool ):
402            return trans.response.send_redirect( web.url_for( controller=cntrller,
403                                                              action='browse_tools',
404                                                              message='You are not allowed to download this tool',
405                                                              status='error' ) )
406        trans.response.set_content_type( tool.mimetype )
407        trans.response.headers['Content-Length'] = int( os.stat( tool.file_name ).st_size )
408        trans.response.headers['Content-Disposition'] = 'attachment; filename=%s' % tool.download_file_name
409        return open( tool.file_name )
410    @web.expose
411    def upload_new_tool_version( self, trans, cntrller, **kwd ):
412        params = util.Params( kwd )
413        message = util.restore_text( params.get( 'message', ''  ) )
414        status = params.get( 'status', 'done' )
415        id = params.get( 'id', None )
416        if not id:
417            return trans.response.send_redirect( web.url_for( controller=cntrller,
418                                                              action='browse_tools',
419                                                              message='Select a tool to upload a new version',
420                                                              status='error' ) )
421        tool = get_tool( trans, id )
422        if not trans.app.security_agent.can_upload_new_version( trans.user, tool ):
423            return trans.response.send_redirect( web.url_for( controller=cntrller,
424                                                              action='browse_tools',
425                                                              message='You are not allowed to upload a new version of this tool',
426                                                              status='error' ) )
427        return trans.response.send_redirect( web.url_for( controller='upload',
428                                                          action='upload',
429                                                          message=message,
430                                                          status=status,
431                                                          replace_id=id ) )
432    @web.expose
433    @web.require_login( "view tool history" )
434    def view_tool_history( self, trans, cntrller, **kwd ):
435        params = util.Params( kwd )
436        message = util.restore_text( params.get( 'message', ''  ) )
437        status = params.get( 'status', 'done' )
438        id = params.get( 'id', None )
439        if not id:
440            return trans.response.send_redirect( web.url_for( controller=cntrller,
441                                                              action='browse_tools',
442                                                              message='Select a tool to view its history',
443                                                              status='error' ) )
444        tool = get_tool( trans, id )
445        can_view = trans.app.security_agent.can_view( trans.user, trans.user_is_admin(), cntrller, tool )
446        if not can_view:
447            return trans.response.send_redirect( web.url_for( controller=cntrller,
448                                                              action='browse_tools',
449                                                              message="You are not allowed to view this tool's history",
450                                                              status='error' ) )
451        can_approve_or_reject = trans.app.security_agent.can_approve_or_reject( trans.user, trans.user_is_admin(), cntrller, tool )
452        can_edit = trans.app.security_agent.can_edit( trans.user, trans.user_is_admin(), cntrller, tool )
453        can_delete = trans.app.security_agent.can_delete( trans.user, trans.user_is_admin(), cntrller, tool )
454        can_download = trans.app.security_agent.can_download( trans.user, trans.user_is_admin(), cntrller, tool )
455        events = [ tea.event for tea in tool.events ]
456        events = [ ( event.state, time_ago( event.update_time ), event.comment ) for event in events ]
457        return trans.fill_template( '/webapps/community/common/view_tool_history.mako',
458                                    cntrller=cntrller,
459                                    events=events,
460                                    tool=tool,
461                                    can_approve_or_reject=can_approve_or_reject,
462                                    can_edit=can_edit,
463                                    can_delete=can_delete,
464                                    can_download=can_download,
465                                    can_view=can_view,
466                                    message=message,
467                                    status=status )
468    @web.expose
469    @web.require_login( "rate tools" )
470    def rate_tool( self, trans, cntrller, **kwd ):
471        """ Rate a tool and return updated rating data. """
472        params = util.Params( kwd )
473        message = util.restore_text( params.get( 'message', ''  ) )
474        status = params.get( 'status', 'done' )
475        id = params.get( 'id', None )
476        if not id:
477            return trans.response.send_redirect( web.url_for( controller=cntrller,
478                                                              action='browse_tools',
479                                                              message='Select a tool to rate',
480                                                              status='error' ) )
481        tool = get_tool( trans, id )
482        can_rate = trans.app.security_agent.can_rate( trans.user, trans.user_is_admin(), cntrller, tool )
483        if not can_rate:
484            return trans.response.send_redirect( web.url_for( controller=cntrller,
485                                                              action='browse_tools',
486                                                              message="You are not allowed to rate this tool",
487                                                              status='error' ) )
488        if params.get( 'rate_button', False ):
489            rating = int( params.get( 'rating', '0' ) )
490            comment = util.restore_text( params.get( 'comment', '' ) )
491            rating = self.rate_item( trans, trans.user, tool, rating, comment )
492        avg_rating, num_ratings = self.get_ave_item_rating_data( trans.sa_session, tool, webapp_model=trans.model )
493        can_approve_or_reject = trans.app.security_agent.can_approve_or_reject( trans.user, trans.user_is_admin(), cntrller, tool )
494        can_edit = trans.app.security_agent.can_edit( trans.user, trans.user_is_admin(), cntrller, tool )
495        can_delete = trans.app.security_agent.can_delete( trans.user, trans.user_is_admin(), cntrller, tool )
496        can_download = trans.app.security_agent.can_download( trans.user, trans.user_is_admin(), cntrller, tool )
497        display_reviews = util.string_as_bool( params.get( 'display_reviews', False ) )
498        tra = self.get_user_item_rating( trans.sa_session, trans.user, tool, webapp_model=trans.model )
499        return trans.fill_template( '/webapps/community/common/rate_tool.mako',
500                                    cntrller=cntrller,
501                                    tool=tool,
502                                    avg_rating=avg_rating,
503                                    can_approve_or_reject=can_approve_or_reject,
504                                    can_edit=can_edit,
505                                    can_delete=can_delete,
506                                    can_download=can_download,
507                                    can_rate=can_rate,
508                                    display_reviews=display_reviews,
509                                    num_ratings=num_ratings,
510                                    tra=tra,
511                                    message=message,
512                                    status=status )
513
514## ---- Utility methods -------------------------------------------------------
515
516def get_versions( item ):
517    """Get all versions of item"""
518    versions = [ item ]
519    this_item = item
520    while item.newer_version:
521        versions.insert( 0, item.newer_version )
522        item = item.newer_version
523    item = this_item
524    while item.older_version:
525        versions.append( item.older_version[ 0 ] )
526        item = item.older_version[ 0 ]
527    return versions
528def get_categories( trans ):
529    """Get all categories from the database"""
530    return trans.sa_session.query( trans.model.Category ) \
531                           .filter( trans.model.Category.table.c.deleted==False ) \
532                           .order_by( trans.model.Category.table.c.name ).all()
533def get_category( trans, id ):
534    """Get a category from the database"""
535    return trans.sa_session.query( trans.model.Category ).get( trans.security.decode_id( id ) )
536def get_tool( trans, id ):
537    """Get a tool from the database"""
538    return trans.sa_session.query( trans.model.Tool ).get( trans.security.decode_id( id ) )
539def get_latest_versions_of_tools( trans ):
540    """Get only the latest version of each tool from the database"""
541    return trans.sa_session.query( trans.model.Tool ) \
542                           .filter( trans.model.Tool.newer_version_id == None ) \
543                           .order_by( trans.model.Tool.name )
544def get_approved_tools( trans ):
545    """Get the tools from the database whose state is APPROVED"""
546    approved_tools = []
547    for tool in get_latest_versions_of_tools( trans ):
548        if tool.state == trans.model.Tool.states.APPROVED:
549            approved_tools.append( tool )
550    return approved_tools
551def get_event( trans, id ):
552    """Get an event from the databse"""
553    return trans.sa_session.query( trans.model.Event ).get( trans.security.decode_id( id ) )
554def get_user( trans, id ):
555    """Get a user from the database"""
556    return trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( id ) )
Note: リポジトリブラウザについてのヘルプは TracBrowser を参照してください。