mirror of
https://github.com/odoo/runbot.git
synced 2025-03-15 23:45:44 +07:00
[IMP] runbot: add a simpler bundle page
* add the possibility to tag bundles * add a owner to the bundle A new bundle page is added in order to filter and search bundles easily. The bundles can be filtered by Pull Requests being open or closed. They can be filtered by teams and tags and searched by names.
This commit is contained in:
parent
d72b17862e
commit
0ea9c4aefe
@ -29,6 +29,7 @@
|
||||
'templates/build.xml',
|
||||
'templates/build_stats.xml',
|
||||
'templates/bundle.xml',
|
||||
'templates/bundle_list.xml',
|
||||
'templates/commit.xml',
|
||||
'templates/dashboard.xml',
|
||||
'templates/frontend.xml',
|
||||
|
@ -197,6 +197,56 @@ class Runbot(Controller):
|
||||
|
||||
return request.render('runbot.bundle', context)
|
||||
|
||||
@route(['/runbot/bundle', '/runbot/bundle/page/<int:page>'], website=True, auth='user', type='http', sitemap=False)
|
||||
def bundle_list(self, project_id=None, is_base='0', pr_filter=None, team_id=None, tag_id=None, search=None, page=1, **kwargs):
|
||||
if not project_id:
|
||||
project_id = request.env.ref('runbot.main_project').id
|
||||
|
||||
is_base = bool(int(is_base))
|
||||
|
||||
pr_filters = {
|
||||
'all': { 'text': 'All', 'domain': False},
|
||||
'has_pr': { 'text': 'With Pull Request(s)', 'domain': ('pr_count', '>', 0)},
|
||||
'has_no_pr': { 'text': 'Without Pull Request(s)', 'domain': ('pr_count', '=', 0)},
|
||||
'has_open_pr': { 'text': 'With Open Pull Request(s)', 'domain': ('pr_open_count', '>', 0)},
|
||||
'has_no_open_pr': { 'text': 'Without Open Pull Request(s)', 'domain': ('pr_open_count', '=', 0)},
|
||||
}
|
||||
teams = request.env['runbot.team'].search([], order='name')
|
||||
tags = request.env['runbot.bundle.tag'].search([], order='name')
|
||||
domain = [('project_id', '=', project_id), ('is_base', '=', is_base)]
|
||||
|
||||
if pr_filter:
|
||||
domain.append(pr_filters.get(pr_filter, {}).get('domain', ()))
|
||||
if team_id:
|
||||
team = request.env['runbot.team'].browse(int(team_id))
|
||||
domain.append(('owner_id', 'in', team.user_ids.ids))
|
||||
if tag_id:
|
||||
tag_name = request.env['runbot.bundle.tag'].browse(int(tag_id)).name
|
||||
domain.append(('tags', 'like', f'%{tag_name}%'))
|
||||
|
||||
if search:
|
||||
search = search if len(search) < 60 else search[:60]
|
||||
domain.append(('name', 'like', f'%{search}%'))
|
||||
|
||||
bundle_count = request.env['runbot.bundle.pr'].search_count(domain)
|
||||
pager = request.website.pager(
|
||||
url='/runbot/bundle',
|
||||
url_args={'is_base': '1' if is_base else 0, 'pr_filter': pr_filter, 'team_id': team_id},
|
||||
total=bundle_count,
|
||||
page=page,
|
||||
step=20,
|
||||
)
|
||||
bundle_ids = request.env['runbot.bundle.pr'].search(domain, limit=20, offset=pager.get('offset', 0), order='id desc').ids
|
||||
|
||||
context = {
|
||||
'pager': pager,
|
||||
'pr_filters': pr_filters,
|
||||
'teams': teams,
|
||||
'tags': tags,
|
||||
'bundles': request.env['runbot.bundle'].browse(bundle_ids),
|
||||
}
|
||||
return request.render('runbot.bundle_list', context)
|
||||
|
||||
@o_route([
|
||||
'/runbot/bundle/<model("runbot.bundle"):bundle>/force',
|
||||
'/runbot/bundle/<model("runbot.bundle"):bundle>/force/<int:auto_rebase>',
|
||||
|
@ -50,6 +50,10 @@ class Bundle(models.Model):
|
||||
commit_limit = fields.Integer("Commit limit")
|
||||
file_limit = fields.Integer("File limit")
|
||||
|
||||
# Administrative fields
|
||||
owner_id = fields.Many2one('res.users', 'Bundle Owner')
|
||||
tag_ids = fields.Many2many('runbot.bundle.tag', string='Tags')
|
||||
|
||||
@api.depends('name')
|
||||
def _compute_host_id(self):
|
||||
assigned_only = None
|
||||
@ -241,3 +245,64 @@ class Bundle(models.Model):
|
||||
for branch in self.branch_ids.sorted(key=lambda b: (b.is_pr)):
|
||||
branch_groups[branch.remote_id.repo_id].append(branch)
|
||||
return branch_groups
|
||||
|
||||
|
||||
class BundleTag(models.Model):
|
||||
|
||||
_name = "runbot.bundle.tag"
|
||||
_description = "Bundle tag"
|
||||
|
||||
_sql_constraints = [
|
||||
('unique_bundle_tag_nam', 'unique (name)', 'avoid duplicate tags'),
|
||||
]
|
||||
|
||||
name = fields.Char('Tag', )
|
||||
bundle_ids = fields.Many2many('runbot.bundle', string='Bundles')
|
||||
|
||||
|
||||
class BundlePr(models.Model):
|
||||
|
||||
_name = "runbot.bundle.pr"
|
||||
_description = "Bundle PR Link"
|
||||
_auto = False
|
||||
_order = 'id desc'
|
||||
|
||||
id = fields.Many2one('runbot.bundle', string='Bundle', readonly=True)
|
||||
name = fields.Char(string='Bundle Name', readonly=True)
|
||||
project_id = fields.Many2one('runbot.project', readonly=True)
|
||||
is_base = fields.Boolean('Is base', readonly=True)
|
||||
owner_id = fields.Many2one('res.users', string='Bundle Owner', readonly=True)
|
||||
tags = fields.Char('Comma Separated Tags', readonly=True)
|
||||
branch_count = fields.Integer('Branch count', readonly=True)
|
||||
pr_count = fields.Integer('PR count', readonly=True)
|
||||
pr_open_count = fields.Integer('Open PR count', readonly=True)
|
||||
pr_closed_count = fields.Integer('Closed PR count', readonly=True)
|
||||
|
||||
def init(self):
|
||||
""" Create an SQL view for bundle """
|
||||
tools.drop_view_if_exists(self._cr, 'runbot_bundle_pr')
|
||||
self._cr.execute("""
|
||||
CREATE VIEW runbot_bundle_pr AS (
|
||||
SELECT
|
||||
runbot_bundle.id AS id,
|
||||
runbot_bundle.name AS name,
|
||||
runbot_bundle.project_id AS project_id,
|
||||
runbot_bundle.is_base AS is_base,
|
||||
runbot_bundle.owner_id AS owner_id,
|
||||
STRING_AGG(runbot_bundle_tag.name,',') AS tags,
|
||||
count(runbot_branch.id) AS branch_count,
|
||||
SUM(CASE WHEN runbot_branch.is_pr THEN 1 ELSE 0 END) AS pr_count,
|
||||
SUM(CASE WHEN runbot_branch.is_pr AND runbot_branch.alive THEN 1 ELSE 0 END) AS pr_open_count,
|
||||
SUM(CASE WHEN runbot_branch.is_pr AND NOT runbot_branch.alive THEN 1 ELSE 0 END) AS pr_closed_count
|
||||
FROM
|
||||
runbot_bundle
|
||||
JOIN
|
||||
runbot_branch ON runbot_branch.bundle_id = runbot_bundle.id
|
||||
LEFT JOIN
|
||||
runbot_bundle_runbot_bundle_tag_rel ON runbot_bundle_runbot_bundle_tag_rel.runbot_bundle_id = runbot_bundle.id
|
||||
LEFT JOIN
|
||||
runbot_bundle_tag ON runbot_bundle_tag.id = runbot_bundle_runbot_bundle_tag_rel.runbot_bundle_tag_id
|
||||
GROUP BY
|
||||
runbot_bundle.id
|
||||
)"""
|
||||
)
|
||||
|
@ -73,6 +73,10 @@ access_runbot_project_runbot_admin,access_runbot_project_runbot_admin,runbot.mod
|
||||
access_runbot_bundle_user,access_runbot_bundle_user,runbot.model_runbot_bundle,runbot.group_user,1,0,0,0
|
||||
access_runbot_bundle_runbot_admin,access_runbot_bundle_runbot_admin,runbot.model_runbot_bundle,runbot.group_runbot_admin,1,1,1,1
|
||||
|
||||
access_runbot_bundle_pr_user,access_runbot_bundle_pr_usr,runbot.model_runbot_bundle_pr,runbot.group_user,1,0,0,0
|
||||
access_runbot_bundle_tag_admin,access_runbot_bundle_tag_admin,runbot.model_runbot_bundle_tag,runbot.group_runbot_admin,1,1,1,1
|
||||
access_runbot_bundle_tag_user,access_runbot_bundle_tag_user,runbot.model_runbot_bundle_tag,runbot.group_user,1,0,0,0
|
||||
|
||||
access_runbot_batch_user,access_runbot_batch_user,runbot.model_runbot_batch,runbot.group_user,1,0,0,0
|
||||
access_runbot_batch_runbot_admin,access_runbot_batch_runbot_admin,runbot.model_runbot_batch,runbot.group_runbot_admin,1,1,1,1
|
||||
|
||||
|
|
123
runbot/templates/bundle_list.xml
Normal file
123
runbot/templates/bundle_list.xml
Normal file
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<template id="runbot.bundle_list">
|
||||
<t t-call="runbot.layout">
|
||||
<t t-call="runbot.bundle_list_top_bar" />
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="row bundle_row" t-foreach="bundles" t-as="bundle">
|
||||
<div class="col-xl-2 align-self-center">
|
||||
<div class="one_line">
|
||||
<a t-esc="bundle.name" t-attf-href="/runbot/bundle/{{bundle.id}}"/>
|
||||
</div>
|
||||
<t t-foreach="bundle.tag_ids" t-as="bundle_tag">
|
||||
<span class="badge badge-info"><t t-esc="bundle_tag.name"/></span>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-xl-10">
|
||||
<div class="row no-gutters">
|
||||
<div class="col-xl-7 align-self-center">
|
||||
<t t-foreach="bundle.branch_groups().items()" t-as="group">
|
||||
<t t-foreach="group[1]" t-as="branch">
|
||||
<small>
|
||||
<div class="btn-toolbar mb-1" role="toolbar">
|
||||
<div class="btn-group btn-group-ssm" role="group">
|
||||
<a t-att-href="branch.branch_url" class="btn btn-default text-left" title="View Branch on Github"><i class="fa fa-github"/></a>
|
||||
<a groups="runbot.group_runbot_admin" class="btn btn-default fa fa-list text-left" t-attf-href="/web/#id={{branch.id}}&view_type=form&model=runbot.branch" target="new" title="View Branch in Backend"/>
|
||||
<a href="#" t-esc="branch.remote_id.short_name" class="btn btn-default disabled text-left"/>
|
||||
<a t-attf-href="/runbot/branch/{{branch.id}}" class="btn btn-default text-left" title="View Branch Details"><span t-att-class="'' if branch.alive else 'line-through'" t-esc="branch.name"/> <i t-if="not branch.alive" title="deleted/closed" class="fa fa-ban text-danger"/></a>
|
||||
</div>
|
||||
</div>
|
||||
</small>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
<div class="col-xl-5 align-self-center">
|
||||
<t t-set="batch" t-value="bundle.last_batch"/>
|
||||
<t t-call="runbot.batch_tile"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbar navbar-default">
|
||||
<span class="pull-right">
|
||||
<t t-call="website.pager" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="bundle_list_top_bar" name="Topbar">
|
||||
<nav class="navbar navbar-light border-top shadow-sm d-print-none">
|
||||
<div class="container-fluid">
|
||||
<div class="d-flex flex-column flex-sm-row justify-content-between w-100">
|
||||
<t t-call="website.pager" />
|
||||
<span class="navbar-brand h4 my-0 mr-auto">Filters</span>
|
||||
<ul class="nav">
|
||||
<li class="nav-item dropdown mr-2 my-1">
|
||||
<a href="#" role="button" class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-filter"/>PR
|
||||
</a>
|
||||
<div class="dropdown-menu">
|
||||
<t t-foreach="pr_filters" t-as="pr_filter">
|
||||
<a class="dropdown-item d-flex align-items-center justify-content-between"
|
||||
t-attf-href="/runbot/bundle?{{ keep_query('*', pr_filter=pr_filter if pr_filter != 'all' else None) }}">
|
||||
<span t-esc="pr_filters[pr_filter].get('text')"/>
|
||||
</a>
|
||||
</t>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown mr-2 my-1">
|
||||
<a href="#" role="button" class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-users"/>Team
|
||||
</a>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item d-flex align-items-center justify-content-between"
|
||||
t-attf-href="/runbot/bundle?{{ keep_query('*', team_id=None) }}">
|
||||
<span>Any</span>
|
||||
</a>
|
||||
<t t-foreach="teams" t-as="team">
|
||||
<a class="dropdown-item d-flex align-items-center justify-content-between"
|
||||
t-attf-href="/runbot/bundle?{{ keep_query('*', team_id=team.id) }}">
|
||||
<span t-esc="team.name"/>
|
||||
</a>
|
||||
</t>
|
||||
</div>
|
||||
</li>
|
||||
<li class="nav-item dropdown mr-2 my-1">
|
||||
<a href="#" role="button" class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-tags"/>Tags
|
||||
</a>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item d-flex align-items-center justify-content-between"
|
||||
t-attf-href="/runbot/bundle?{{ keep_query('*', tag_id=None) }}">
|
||||
<span>Any</span>
|
||||
</a>
|
||||
<t t-foreach="tags" t-as="tag">
|
||||
<a class="dropdown-item d-flex align-items-center justify-content-between"
|
||||
t-attf-href="/runbot/bundle?{{ keep_query('*', tag_id=tag.id) }}">
|
||||
<span t-esc="tag.name"/>
|
||||
</a>
|
||||
</t>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="col-md-4 d-none d-md-flex flex-row align-items-center justify-content-end">
|
||||
<t t-call="website.website_search_box_input">
|
||||
<t t-set="_form_classes" t-valuef="mt8 float-right"/>
|
||||
<t t-set="action" t-valuef="/runbot/bundle"/>
|
||||
<t t-set="placeholder">Search Bundle Names</t>
|
||||
<t t-set="search" t-value="search"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
</data>
|
||||
</odoo>
|
@ -90,7 +90,7 @@
|
||||
<t t-esc="batch.get_formated_age()"/>
|
||||
<i class="fa fa-exclamation-triangle" t-if="batch.has_warning"/>
|
||||
</span>
|
||||
<span class="float-right header_hover">View batch...</span>
|
||||
<span class="float-right header_hover" t-field="batch.create_date" t-options="{'format': 'yyyy-MM-dd HH:mmZ'}"/>
|
||||
</div>
|
||||
</a>
|
||||
<t t-if="batch.state=='preparing'">
|
||||
|
@ -37,6 +37,8 @@
|
||||
</div>
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="owner_id"/>
|
||||
<field name="tag_ids" widget="many2many_tags"/>
|
||||
<field name="project_id"/>
|
||||
<field name="sticky" readonly="0"/>
|
||||
<field name="to_upgrade" readonly="0"/>
|
||||
@ -122,6 +124,7 @@
|
||||
<field name="no_build"/>
|
||||
<field name="branch_ids"/>
|
||||
<field name="version_id"/>
|
||||
<field name="owner_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
Loading…
Reference in New Issue
Block a user