diff --git a/lib/galaxy/webapps/tool_shed/controllers/repository_review.py b/lib/galaxy/webapps/tool_shed/controllers/repository_review.py
index b058e4adef8..e9b90728d99 100644
--- a/lib/galaxy/webapps/tool_shed/controllers/repository_review.py
+++ b/lib/galaxy/webapps/tool_shed/controllers/repository_review.py
@@ -2,15 +2,12 @@ import os, logging
from galaxy import web, util
from galaxy.web.base.controller import BaseUIController
from galaxy.web.form_builder import SelectField, CheckboxField
-from galaxy.webapps.tool_shed import model
-from galaxy.web.framework.helpers import grids
-from galaxy.model.orm import and_
from sqlalchemy.sql.expression import func
from galaxy.webapps.tool_shed.util import common_util
from galaxy.webapps.tool_shed.util.container_util import STRSEP
-from tool_shed.grids.repository_grids import RepositoryGrid
import tool_shed.util.shed_util_common as suc
from galaxy.util.odict import odict
+import tool_shed.grids.repository_review_grids as repository_review_grids
from galaxy import eggs
eggs.require('mercurial')
@@ -18,259 +15,15 @@ from mercurial import hg, ui, patch, commands
log = logging.getLogger( __name__ )
-class ComponentGrid( grids.Grid ):
- class NameColumn( grids.TextColumn ):
- def get_value( self, trans, grid, component ):
- return component.name
- class DescriptionColumn( grids.TextColumn ):
- def get_value( self, trans, grid, component ):
- return component.description
- title = "Repository review components"
- model_class = model.Component
- template='/webapps/tool_shed/repository_review/grid.mako'
- default_sort_key = "name"
- columns = [
- NameColumn( "Name",
- key="Component.name",
- link=( lambda item: dict( operation="edit", id=item.id ) ),
- attach_popup=False ),
- DescriptionColumn( "Description",
- key="Component.description",
- attach_popup=False )
- ]
- default_filter = {}
- global_actions = [
- grids.GridAction( "Add new component",
- dict( controller='repository_review', action='manage_components', operation='create' ) )
- ]
- operations = []
- standard_filters = []
- num_rows_per_page = 50
- preserve_state = False
- use_paging = True
-
-class RepositoriesWithReviewsGrid( RepositoryGrid ):
- # This grid filters out repositories that have been marked as either deprecated or deleted.
- class WithReviewsRevisionColumn( grids.GridColumn ):
- def get_value( self, trans, grid, repository ):
- # Restrict to revisions that have been reviewed.
- if repository.reviews:
- rval = ''
- repo = hg.repository( suc.get_configured_ui(), repository.repo_path( trans.app ) )
- for review in repository.reviews:
- changeset_revision = review.changeset_revision
- rev, label = suc.get_rev_label_from_changeset_revision( repo, changeset_revision )
- rval += '%s
' % ( trans.security.encode_id( repository.id ), changeset_revision, label )
- return rval
- return ''
- class WithoutReviewsRevisionColumn( grids.GridColumn ):
- def get_value( self, trans, grid, repository ):
- # Restrict the options to revisions that have not yet been reviewed.
- repository_metadata_revisions = suc.get_repository_metadata_revisions_for_review( repository, reviewed=False )
- if repository_metadata_revisions:
- rval = ''
- for repository_metadata in repository_metadata_revisions:
- rev, label, changeset_revision = suc.get_rev_label_changeset_revision_from_repository_metadata( trans,
- repository_metadata,
- repository=repository )
- rval += '%s
' % ( trans.security.encode_id( repository.id ), changeset_revision, label )
- return rval
- return ''
- class ReviewersColumn( grids.TextColumn ):
- def get_value( self, trans, grid, repository ):
- rval = ''
- if repository.reviewers:
- for user in repository.reviewers:
- rval += '' % trans.security.encode_id( user.id )
- rval += '%s | ' % user.username
- rval = rval.rstrip( ' | ' )
- return rval
- title = "All reviewed repositories"
- model_class = model.Repository
- template='/webapps/tool_shed/repository_review/grid.mako'
- default_sort_key = "Repository.name"
- columns = [
- RepositoryGrid.NameColumn( "Repository name",
- key="name",
- link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ),
- attach_popup=True ),
- WithReviewsRevisionColumn( "Reviewed revisions" ),
- WithoutReviewsRevisionColumn( "Revisions for review" ),
- RepositoryGrid.UserColumn( "Owner", attach_popup=False ),
- ReviewersColumn( "Reviewers", attach_popup=False )
- ]
- columns.append( grids.MulticolFilterColumn( "Search repository name",
- cols_to_filter=[ columns[0] ],
- key="free-text-search",
- visible=False,
- filterable="standard" ) )
- operations = [
- grids.GridOperation( "Inspect repository revisions",
- allow_multiple=False,
- condition=( lambda item: not item.deleted ),
- async_compatible=False )
- ]
- def build_initial_query( self, trans, **kwd ):
- return trans.sa_session.query( model.Repository ) \
- .filter( and_( model.Repository.table.c.deleted == False,
- model.Repository.table.c.deprecated == False ) ) \
- .join( ( model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \
- .join( ( model.User.table, model.User.table.c.id == model.Repository.table.c.user_id ) ) \
- .outerjoin( ( model.ComponentReview.table, model.ComponentReview.table.c.repository_review_id == model.RepositoryReview.table.c.id ) ) \
- .outerjoin( ( model.Component.table, model.Component.table.c.id == model.ComponentReview.table.c.component_id ) )
-
-class RepositoriesWithoutReviewsGrid( RepositoriesWithReviewsGrid ):
- # This grid filters out repositories that have been marked as either deprecated or deleted.
- title = "Repositories with no reviews"
- columns = [
- RepositoriesWithReviewsGrid.NameColumn( "Repository name",
- key="name",
- link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ),
- attach_popup=True ),
- RepositoriesWithReviewsGrid.DescriptionColumn( "Synopsis",
- key="description",
- attach_popup=False ),
- RepositoriesWithReviewsGrid.WithoutReviewsRevisionColumn( "Revisions for review" ),
- RepositoriesWithReviewsGrid.UserColumn( "Owner",
- attach_popup=False )
- ]
- columns.append( grids.MulticolFilterColumn( "Search repository name, description",
- cols_to_filter=[ columns[0], columns[1] ],
- key="free-text-search",
- visible=False,
- filterable="standard" ) )
- operations = [ grids.GridOperation( "Inspect repository revisions",
- allow_multiple=False,
- condition=( lambda item: not item.deleted ),
- async_compatible=False ) ]
- def build_initial_query( self, trans, **kwd ):
- return trans.sa_session.query( model.Repository ) \
- .filter( and_( model.Repository.table.c.deleted == False,
- model.Repository.table.c.deprecated == False,
- model.Repository.reviews == None ) ) \
- .join( model.User.table )
-
-class RepositoriesReviewedByMeGrid( RepositoriesWithReviewsGrid ):
- # This grid filters out repositories that have been marked as either deprecated or deleted.
- def build_initial_query( self, trans, **kwd ):
- return trans.sa_session.query( model.Repository ) \
- .filter( and_( model.Repository.table.c.deleted == False,
- model.Repository.table.c.deprecated == False ) ) \
- .join( ( model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \
- .filter( model.RepositoryReview.table.c.user_id == trans.user.id ) \
- .join( ( model.User.table, model.User.table.c.id == model.RepositoryReview.table.c.user_id ) ) \
- .outerjoin( ( model.ComponentReview.table, model.ComponentReview.table.c.repository_review_id == model.RepositoryReview.table.c.id ) ) \
- .outerjoin( ( model.Component.table, model.Component.table.c.id == model.ComponentReview.table.c.component_id ) )
-
-class RepositoryReviewsByUserGrid( grids.Grid ):
- # This grid filters out repositories that have been marked as deprecated.
- class RepositoryNameColumn( grids.TextColumn ):
- def get_value( self, trans, grid, review ):
- return review.repository.name
- class RepositoryDescriptionColumn( grids.TextColumn ):
- def get_value( self, trans, grid, review ):
- return review.repository.description
- class RevisionColumn( grids.TextColumn ):
- def get_value( self, trans, grid, review ):
- encoded_review_id = trans.security.encode_id( review.id )
- rval = '%s' % ( encoded_review_id, suc.get_revision_label( trans, review.repository, review.changeset_revision ) )
- return rval
- class RatingColumn( grids.TextColumn ):
- def get_value( self, trans, grid, review ):
- if review.rating:
- rval = ''
- return rval
- return ''
- title = "Reviews by user"
- model_class = model.RepositoryReview
- template='/webapps/tool_shed/repository_review/grid.mako'
- default_sort_key = 'repository_id'
- columns = [
- RepositoryNameColumn( "Repository Name",
- model_class=model.Repository,
- key="Repository.name",
- link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ),
- attach_popup=True ),
- RepositoryDescriptionColumn( "Description",
- model_class=model.Repository,
- key="Repository.description",
- attach_popup=False ),
- RevisionColumn( "Revision", attach_popup=False ),
- RatingColumn( "Rating", attach_popup=False ),
- ]
- # Override these
- default_filter = {}
- global_actions = []
- operations = [
- grids.GridOperation( "Inspect repository revisions",
- allow_multiple=False,
- condition=( lambda item: not item.deleted ),
- async_compatible=False )
- ]
- standard_filters = []
- num_rows_per_page = 50
- preserve_state = False
- use_paging = True
- def build_initial_query( self, trans, **kwd ):
- user_id = trans.security.decode_id( kwd[ 'id' ] )
- return trans.sa_session.query( model.RepositoryReview ) \
- .filter( and_( model.RepositoryReview.table.c.deleted == False, \
- model.RepositoryReview.table.c.user_id == user_id ) ) \
- .join( ( model.Repository.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \
- .filter( model.Repository.table.c.deprecated == False )
-
-class ReviewedRepositoriesIOwnGrid( RepositoriesWithReviewsGrid ):
- title = "Reviewed repositories I own"
- columns = [
- RepositoriesWithReviewsGrid.NameColumn( "Repository name",
- key="name",
- link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ),
- attach_popup=True ),
- RepositoriesWithReviewsGrid.WithReviewsRevisionColumn( "Reviewed revisions" ),
- RepositoriesWithReviewsGrid.WithoutReviewsRevisionColumn( "Revisions for review" ),
- RepositoriesWithReviewsGrid.ReviewersColumn( "Reviewers", attach_popup=False ),
- RepositoryGrid.DeprecatedColumn( "Deprecated" )
- ]
- columns.append( grids.MulticolFilterColumn( "Search repository name",
- cols_to_filter=[ columns[0] ],
- key="free-text-search",
- visible=False,
- filterable="standard" ) )
- operations = [
- grids.GridOperation( "Inspect repository revisions",
- allow_multiple=False,
- condition=( lambda item: not item.deleted ),
- async_compatible=False )
- ]
- def build_initial_query( self, trans, **kwd ):
- return trans.sa_session.query( model.Repository ) \
- .join( ( model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \
- .filter( model.Repository.table.c.user_id == trans.user.id ) \
- .join( ( model.User.table, model.User.table.c.id == model.RepositoryReview.table.c.user_id ) ) \
- .outerjoin( ( model.ComponentReview.table, model.ComponentReview.table.c.repository_review_id == model.RepositoryReview.table.c.id ) ) \
- .outerjoin( ( model.Component.table, model.Component.table.c.id == model.ComponentReview.table.c.component_id ) )
class RepositoryReviewController( BaseUIController, common_util.ItemRatings ):
- component_grid = ComponentGrid()
- repositories_reviewed_by_me_grid = RepositoriesReviewedByMeGrid()
- repositories_with_reviews_grid = RepositoriesWithReviewsGrid()
- repositories_without_reviews_grid = RepositoriesWithoutReviewsGrid()
- repository_reviews_by_user_grid = RepositoryReviewsByUserGrid()
- reviewed_repositories_i_own_grid = ReviewedRepositoriesIOwnGrid()
+ component_grid = repository_review_grids.ComponentGrid()
+ repositories_reviewed_by_me_grid = repository_review_grids.RepositoriesReviewedByMeGrid()
+ repositories_with_reviews_grid = repository_review_grids.RepositoriesWithReviewsGrid()
+ repositories_without_reviews_grid = repository_review_grids.RepositoriesWithoutReviewsGrid()
+ repository_reviews_by_user_grid = repository_review_grids.RepositoryReviewsByUserGrid()
+ reviewed_repositories_i_own_grid = repository_review_grids.ReviewedRepositoriesIOwnGrid()
@web.expose
@web.require_login( "approve repository review" )
diff --git a/lib/tool_shed/grids/repository_review_grids.py b/lib/tool_shed/grids/repository_review_grids.py
new file mode 100644
index 00000000000..34930c77721
--- /dev/null
+++ b/lib/tool_shed/grids/repository_review_grids.py
@@ -0,0 +1,258 @@
+import os, logging
+from galaxy.webapps.tool_shed import model
+from galaxy.web.framework.helpers import grids
+from galaxy.model.orm import and_
+from tool_shed.grids.repository_grids import RepositoryGrid
+import tool_shed.util.shed_util_common as suc
+
+from galaxy import eggs
+eggs.require('mercurial')
+from mercurial import hg, ui, patch, commands
+
+log = logging.getLogger( __name__ )
+
+class ComponentGrid( grids.Grid ):
+ class NameColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, component ):
+ return component.name
+ class DescriptionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, component ):
+ return component.description
+ title = "Repository review components"
+ model_class = model.Component
+ template='/webapps/tool_shed/repository_review/grid.mako'
+ default_sort_key = "name"
+ columns = [
+ NameColumn( "Name",
+ key="Component.name",
+ link=( lambda item: dict( operation="edit", id=item.id ) ),
+ attach_popup=False ),
+ DescriptionColumn( "Description",
+ key="Component.description",
+ attach_popup=False )
+ ]
+ default_filter = {}
+ global_actions = [
+ grids.GridAction( "Add new component",
+ dict( controller='repository_review', action='manage_components', operation='create' ) )
+ ]
+ operations = []
+ standard_filters = []
+ num_rows_per_page = 50
+ preserve_state = False
+ use_paging = True
+
+class RepositoriesWithReviewsGrid( RepositoryGrid ):
+ # This grid filters out repositories that have been marked as either deprecated or deleted.
+ class WithReviewsRevisionColumn( grids.GridColumn ):
+ def get_value( self, trans, grid, repository ):
+ # Restrict to revisions that have been reviewed.
+ if repository.reviews:
+ rval = ''
+ repo = hg.repository( suc.get_configured_ui(), repository.repo_path( trans.app ) )
+ for review in repository.reviews:
+ changeset_revision = review.changeset_revision
+ rev, label = suc.get_rev_label_from_changeset_revision( repo, changeset_revision )
+ rval += '%s
' % ( trans.security.encode_id( repository.id ), changeset_revision, label )
+ return rval
+ return ''
+ class WithoutReviewsRevisionColumn( grids.GridColumn ):
+ def get_value( self, trans, grid, repository ):
+ # Restrict the options to revisions that have not yet been reviewed.
+ repository_metadata_revisions = suc.get_repository_metadata_revisions_for_review( repository, reviewed=False )
+ if repository_metadata_revisions:
+ rval = ''
+ for repository_metadata in repository_metadata_revisions:
+ rev, label, changeset_revision = suc.get_rev_label_changeset_revision_from_repository_metadata( trans,
+ repository_metadata,
+ repository=repository )
+ rval += '%s
' % ( trans.security.encode_id( repository.id ), changeset_revision, label )
+ return rval
+ return ''
+ class ReviewersColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, repository ):
+ rval = ''
+ if repository.reviewers:
+ for user in repository.reviewers:
+ rval += '' % trans.security.encode_id( user.id )
+ rval += '%s | ' % user.username
+ rval = rval.rstrip( ' | ' )
+ return rval
+ title = "All reviewed repositories"
+ model_class = model.Repository
+ template='/webapps/tool_shed/repository_review/grid.mako'
+ default_sort_key = "Repository.name"
+ columns = [
+ RepositoryGrid.NameColumn( "Repository name",
+ key="name",
+ link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ),
+ attach_popup=True ),
+ WithReviewsRevisionColumn( "Reviewed revisions" ),
+ WithoutReviewsRevisionColumn( "Revisions for review" ),
+ RepositoryGrid.UserColumn( "Owner", attach_popup=False ),
+ ReviewersColumn( "Reviewers", attach_popup=False )
+ ]
+ columns.append( grids.MulticolFilterColumn( "Search repository name",
+ cols_to_filter=[ columns[0] ],
+ key="free-text-search",
+ visible=False,
+ filterable="standard" ) )
+ operations = [
+ grids.GridOperation( "Inspect repository revisions",
+ allow_multiple=False,
+ condition=( lambda item: not item.deleted ),
+ async_compatible=False )
+ ]
+ def build_initial_query( self, trans, **kwd ):
+ return trans.sa_session.query( model.Repository ) \
+ .filter( and_( model.Repository.table.c.deleted == False,
+ model.Repository.table.c.deprecated == False ) ) \
+ .join( ( model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \
+ .join( ( model.User.table, model.User.table.c.id == model.Repository.table.c.user_id ) ) \
+ .outerjoin( ( model.ComponentReview.table, model.ComponentReview.table.c.repository_review_id == model.RepositoryReview.table.c.id ) ) \
+ .outerjoin( ( model.Component.table, model.Component.table.c.id == model.ComponentReview.table.c.component_id ) )
+
+class RepositoriesWithoutReviewsGrid( RepositoriesWithReviewsGrid ):
+ # This grid filters out repositories that have been marked as either deprecated or deleted.
+ title = "Repositories with no reviews"
+ columns = [
+ RepositoriesWithReviewsGrid.NameColumn( "Repository name",
+ key="name",
+ link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ),
+ attach_popup=True ),
+ RepositoriesWithReviewsGrid.DescriptionColumn( "Synopsis",
+ key="description",
+ attach_popup=False ),
+ RepositoriesWithReviewsGrid.WithoutReviewsRevisionColumn( "Revisions for review" ),
+ RepositoriesWithReviewsGrid.UserColumn( "Owner",
+ attach_popup=False )
+ ]
+ columns.append( grids.MulticolFilterColumn( "Search repository name, description",
+ cols_to_filter=[ columns[0], columns[1] ],
+ key="free-text-search",
+ visible=False,
+ filterable="standard" ) )
+ operations = [ grids.GridOperation( "Inspect repository revisions",
+ allow_multiple=False,
+ condition=( lambda item: not item.deleted ),
+ async_compatible=False ) ]
+ def build_initial_query( self, trans, **kwd ):
+ return trans.sa_session.query( model.Repository ) \
+ .filter( and_( model.Repository.table.c.deleted == False,
+ model.Repository.table.c.deprecated == False,
+ model.Repository.reviews == None ) ) \
+ .join( model.User.table )
+
+class RepositoriesReviewedByMeGrid( RepositoriesWithReviewsGrid ):
+ # This grid filters out repositories that have been marked as either deprecated or deleted.
+ def build_initial_query( self, trans, **kwd ):
+ return trans.sa_session.query( model.Repository ) \
+ .filter( and_( model.Repository.table.c.deleted == False,
+ model.Repository.table.c.deprecated == False ) ) \
+ .join( ( model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \
+ .filter( model.RepositoryReview.table.c.user_id == trans.user.id ) \
+ .join( ( model.User.table, model.User.table.c.id == model.RepositoryReview.table.c.user_id ) ) \
+ .outerjoin( ( model.ComponentReview.table, model.ComponentReview.table.c.repository_review_id == model.RepositoryReview.table.c.id ) ) \
+ .outerjoin( ( model.Component.table, model.Component.table.c.id == model.ComponentReview.table.c.component_id ) )
+
+class RepositoryReviewsByUserGrid( grids.Grid ):
+ # This grid filters out repositories that have been marked as deprecated.
+ class RepositoryNameColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, review ):
+ return review.repository.name
+ class RepositoryDescriptionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, review ):
+ return review.repository.description
+ class RevisionColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, review ):
+ encoded_review_id = trans.security.encode_id( review.id )
+ rval = '%s' % ( encoded_review_id, suc.get_revision_label( trans, review.repository, review.changeset_revision ) )
+ return rval
+ class RatingColumn( grids.TextColumn ):
+ def get_value( self, trans, grid, review ):
+ if review.rating:
+ rval = ''
+ return rval
+ return ''
+ title = "Reviews by user"
+ model_class = model.RepositoryReview
+ template='/webapps/tool_shed/repository_review/grid.mako'
+ default_sort_key = 'repository_id'
+ columns = [
+ RepositoryNameColumn( "Repository Name",
+ model_class=model.Repository,
+ key="Repository.name",
+ link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ),
+ attach_popup=True ),
+ RepositoryDescriptionColumn( "Description",
+ model_class=model.Repository,
+ key="Repository.description",
+ attach_popup=False ),
+ RevisionColumn( "Revision", attach_popup=False ),
+ RatingColumn( "Rating", attach_popup=False ),
+ ]
+ # Override these
+ default_filter = {}
+ global_actions = []
+ operations = [
+ grids.GridOperation( "Inspect repository revisions",
+ allow_multiple=False,
+ condition=( lambda item: not item.deleted ),
+ async_compatible=False )
+ ]
+ standard_filters = []
+ num_rows_per_page = 50
+ preserve_state = False
+ use_paging = True
+ def build_initial_query( self, trans, **kwd ):
+ user_id = trans.security.decode_id( kwd[ 'id' ] )
+ return trans.sa_session.query( model.RepositoryReview ) \
+ .filter( and_( model.RepositoryReview.table.c.deleted == False, \
+ model.RepositoryReview.table.c.user_id == user_id ) ) \
+ .join( ( model.Repository.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \
+ .filter( model.Repository.table.c.deprecated == False )
+
+class ReviewedRepositoriesIOwnGrid( RepositoriesWithReviewsGrid ):
+ title = "Reviewed repositories I own"
+ columns = [
+ RepositoriesWithReviewsGrid.NameColumn( "Repository name",
+ key="name",
+ link=( lambda item: dict( operation="view_or_manage_repository", id=item.id ) ),
+ attach_popup=True ),
+ RepositoriesWithReviewsGrid.WithReviewsRevisionColumn( "Reviewed revisions" ),
+ RepositoriesWithReviewsGrid.WithoutReviewsRevisionColumn( "Revisions for review" ),
+ RepositoriesWithReviewsGrid.ReviewersColumn( "Reviewers", attach_popup=False ),
+ RepositoryGrid.DeprecatedColumn( "Deprecated" )
+ ]
+ columns.append( grids.MulticolFilterColumn( "Search repository name",
+ cols_to_filter=[ columns[0] ],
+ key="free-text-search",
+ visible=False,
+ filterable="standard" ) )
+ operations = [
+ grids.GridOperation( "Inspect repository revisions",
+ allow_multiple=False,
+ condition=( lambda item: not item.deleted ),
+ async_compatible=False )
+ ]
+ def build_initial_query( self, trans, **kwd ):
+ return trans.sa_session.query( model.Repository ) \
+ .join( ( model.RepositoryReview.table, model.RepositoryReview.table.c.repository_id == model.Repository.table.c.id ) ) \
+ .filter( model.Repository.table.c.user_id == trans.user.id ) \
+ .join( ( model.User.table, model.User.table.c.id == model.RepositoryReview.table.c.user_id ) ) \
+ .outerjoin( ( model.ComponentReview.table, model.ComponentReview.table.c.repository_review_id == model.RepositoryReview.table.c.id ) ) \
+ .outerjoin( ( model.Component.table, model.Component.table.c.id == model.ComponentReview.table.c.component_id ) )
+
\ No newline at end of file