Merge pull request #256 from abf/rosa-build:251-combine-build-lists-for-build-tasks

#251: Combine build lists for build tasks
This commit is contained in:
avm 2013-08-06 14:49:30 +04:00
commit 3cd88dfb25
23 changed files with 485 additions and 126 deletions

View File

@ -64,6 +64,9 @@ gem 'angularjs-rails'
gem 'ng-rails-csrf'
gem 'momentjs-rails'
gem 'angular-i18n', '0.1.2'
gem 'js-routes'
gem 'time_diff'
group :assets do
gem 'sass-rails', '~> 3.2.5'

View File

@ -170,6 +170,8 @@ GEM
jquery-rails (2.0.3)
railties (>= 3.1.0, < 5.0)
thor (~> 0.14)
js-routes (0.9.3)
rails (>= 3.2)
json (1.8.0)
jwt (0.1.8)
multi_json (>= 1.5)
@ -392,6 +394,9 @@ GEM
rack (>= 1.0.0)
thor (0.18.1)
tilt (1.4.1)
time_diff (0.3.0)
activesupport
i18n
treetop (1.4.14)
polyglot
polyglot (>= 0.3.1)
@ -455,6 +460,7 @@ DEPENDENCIES
hirb
jbuilder (~> 1.4.2)
jquery-rails (~> 2.0.2)
js-routes
mailcatcher
meta-tags (~> 1.2.5)
meta_request
@ -494,6 +500,7 @@ DEPENDENCIES
state_machine
therubyracer (~> 0.10.2)
therubyrhino (~> 1.73.1)
time_diff
trinidad (~> 1.0.2)
turbo-sprockets-rails3
uglifier (~> 1.2.4)

View File

@ -0,0 +1,121 @@
RosaABF.controller('BuildListsController', ['$scope', '$http', '$location', '$timeout', function($scope, $http, $location, $timeout) {
$scope.params = null;
$scope.first_run = true;
$scope.server_status = null;
$scope.build_lists = [];
$scope.isRequest = false; // Disable 'Search' button
$scope.pages = [];
// Fixes: redirect to page after form submit
$("#monitoring_filter").on('submit', function(){ return false; });
$scope.getBuildLists = function() {
// Disable 'Search' button
$scope.isRequest = true;
$http.get(Routes.build_lists_path({format: 'json'}), {params: $location.search()}).success(function(results) {
// Render Server status
$scope.server_status = results.server_status;
// TMP fields
var dictionary = results.dictionary;
var build_lists = [];
var groups = {};
// Grouping of build_lists
_.each(results.build_lists, function(r){
var bl = new BuildList(r, dictionary);
var key = bl.project_id + '-' + bl.commit_hash + '-' + bl.user_id;
if (groups[key]) {
groups[key].addRelated(bl);
} else {
groups[key] = bl;
build_lists.push(bl);
}
});
// Adds all build_lists into the table (group by group)
$scope.build_lists = [];
_.each(build_lists, function(bl){
_.each(bl.related, function(b){ $scope.build_lists.push(b); });
});
// Render pagination
$scope.pages = results.pages;
// Enable 'Search' button
$scope.isRequest = false;
}).error(function(data, status, headers, config) {
// Enable 'Search' button
$scope.isRequest = false;
});;
}
$scope.showRelated = function(build_list) {
build_list.relatedHidden = false;
_.each(build_list.related, function(bl){
bl.show = true;
// Waits for render of build_lists
$timeout(function() {
$('#build-list-' + bl.id + ' td:visible').effect('highlight', {}, 1000);
}, 100);
});
}
$scope.hideRelated = function(build_list) {
build_list.relatedHidden = true;
_.each(build_list.related, function(bl){ bl.show = false; });
build_list.show = true;
}
$scope.cancelRefresh = null;
$scope.refresh = function(force) {
if ($('#autoreload').is(':checked') || force) {
var params = {};
_.each($("#monitoring_filter").serializeArray(), function(a){
if (a.value) { params[a.name] = a.value; }
});
$location.search(params);
$scope.first_run = false;
$scope.getBuildLists();
}
if (!force) {
$scope.cancelRefresh = $timeout($scope.refresh, 60000);
}
}
$scope.$on('$locationChangeSuccess', function(event) {
$scope.updateParams();
if (!$scope.first_run) { $scope.getBuildLists(); }
});
$scope.updateParams = function() {
var params = $location.search();
$scope.params = {
page: params.page || 1,
per_page: params.per_page || 25,
filter: {
ownership: params['filter[ownership]'] || 'owned',
status: params['filter[status]'],
platform_id: params['filter[platform_id]'],
arch_id: params['filter[arch_id]'],
mass_build_id: params['filter[mass_build_id]'],
updated_at_start: params['filter[updated_at_start]'],
updated_at_end: params['filter[updated_at_end]'],
project_name: params['filter[project_name]'],
id: params['filter[id]']
}
}
}
$scope.goToPage = function(number) {
$location.search('page', number);
$scope.getBuildLists();
}
$scope.updateParams();
// Waits for render of filters
$timeout($scope.refresh, 100);
}]);

View File

@ -1,4 +1,5 @@
var _locales = {
<%I18n.locale = :ru%>
'ru-ru': {
'project.total_branches': [
'Всего %1 ветка',
@ -9,8 +10,10 @@ var _locales = {
'Всего %1 тег',
'Всего %1 тега',
'Всего %1 тегов'
]
],
<%= BuildList::STATUSES.map{|s| "'build_list.status.#{s}':'#{BuildList.human_status(s)}'"}.join(',') %>
},
<%I18n.locale = :en%>
'en-us': {
'project.total_branches': [
'Total %1 branch',
@ -19,6 +22,7 @@ var _locales = {
'project.total_tags': [
'Total %1 tag',
'Total %1 tags'
]
],
<%= BuildList::STATUSES.map{|s| "'build_list.status.#{s}':'#{BuildList.human_status(s)}'"}.join(',') %>
}
};

View File

@ -0,0 +1,77 @@
// dictionary - optional
var BuildList = function(atts, dictionary) {
var self = this;
var initialSettings = atts || {};
//initial settings if passed in
for(var setting in initialSettings){
if(initialSettings.hasOwnProperty(setting))
self[setting] = initialSettings[setting];
};
// Fields:
if (dictionary) {
self.user = _.find(dictionary.users, function(i){ return i.id == self.user_id; });
self.arch = _.find(dictionary.arches, function(i){ return i.id == self.arch_id; });
self.project = _.find(dictionary.projects, function(i){ return i.id == self.project_id; });
self.save_to_platform = _.find(dictionary.platforms, function(i){ return i.id == self.save_to_platform_id; });
self.build_for_platform = _.find(dictionary.platforms, function(i){ return i.id == self.build_for_platform_id; });
self.save_to_repository = _.find(dictionary.repositories, function(i){ return i.id == self.save_to_repository_id; });
}
self.save_to_repository_name = self.save_to_platform.name + '/' + self.save_to_repository.name;
if (self.save_to_platform.personal) {
self.save_to_repository_name += ' (' + self.build_for_platform.name + ')'
}
if (self.project) {
if (self.last_published_commit_hash) {
self.version_link_text = self.last_published_commit_hash + '...' + self.commit_hash;
self.version_link_url = Routes.diff_path(self.project.owner, self.project.name, self.version_link_text);
} else {
self.version_link_text = self.commit_hash || self.project_version;
self.version_link_url = Routes.commit_path(self.project.owner, self.project.name, self.version_link_text);
}
self.project.url = Routes.project_path(self.project.owner, self.project.name);
self.project.name_with_owner = self.project.owner + '/' + self.project.name;
}
self.save_to_repository_url = Routes.platform_repository_path(self.save_to_platform_id, self.save_to_repository_id);
self.user.url = Routes.user_path(self.user.uname);
self.human_status = 'build_list.status.' + self.status;
self.url = Routes.build_list_path(self.id);
switch (self.status) { // See: app/helpers/build_lists_helper.rb
case <%=BuildList::BUILD_PUBLISHED%>:
case <%=BuildList::SUCCESS%>: self.status_color = 'success'; break
case <%=BuildList::BUILD_ERROR%>:
case <%=BuildList::FAILED_PUBLISH%>:
case <%=BuildList::REJECTED_PUBLISH%>: self.status_color = 'error'; break
case <%=BuildList::TESTS_FAILED%>: self.status_color = 'warning'; break
default: self.status_color = 'nocolor';
}
// == Logic
// private fields
self.lastRelated = false;
// public fields
self.related = [self];
self.show = true;
self.relatedHidden = true;
self.hasRelated = false;
// public methods
self.addRelated = function(bl) {
bl.show = false;
bl.lastRelated = true;
self.related.slice(-1)[0].lastRelated = false;
self.related.push(bl);
self.hasRelated = true;
}
//return the scope-safe instance
return self;
};

View File

@ -14,15 +14,15 @@ var ProjectRef = function(atts) {
self.ui_container = false;
self.path = function(project) {
return '/' + project.fullname + '/tree/' + self.ref;
return Routes.tree_path(project.owner.uname, project.name, self.ref);
}
self.diff_path = function(project, current_ref) {
return '/' + project.fullname + '/diff/' + current_ref + '...' + self.ref;
return Routes.diff_path(project.owner.uname, project.name, current_ref + '...' + self.ref);
}
self.archive_path = function(project, type) {
return '/' + project.fullname + '/archive/' + project.name + '-' + self.ref + '.' + type;
return Routes.archive_path(project.owner.uname, project.name, project.name + '-' + self.ref, {format: type});
}
//return the scope-safe instance

View File

@ -14,6 +14,7 @@
//= require backbone_datalink
//= require backbone/rosa
//= require js-routes
// require angular
//= require unstable/angular
// require angular-resource

View File

@ -2061,4 +2061,48 @@ a.button.reject_publish, a.button.create_container {
}
}
}
table tbody {
td.build-list-statuses {
background: #FFF;
.status {
float: left;
border: 1px solid #DDD;
margin: 1px 2px;
}
.error { background: #fedede; }
.success { background: #e3edb7; }
.warning { background: #FCF8E3; }
.nocolor { background: #FFF; }
.expand {
position: absolute;
margin: -9px 0 0 -23px;
border: 1px solid #DDD;
border-right-color: #FFF;
padding: 1px;
cursor: pointer;
}
}
tr.group-start td {
border-top: 2px solid #125687;
}
tr.group-end td {
border-bottom: 2px solid #125687;
}
}
#will_paginate .pagination {
div {
display: inline-block;
margin: 0 2px;
}
a {
text-decoration: none;
}
.disabled a {
color: #292929;
cursor: default;
}
}

View File

@ -13,7 +13,7 @@ class Projects::BaseController < ApplicationController
end
def find_project
@project = Project.find_by_owner_and_name!(params[:owner_name], params[:project_name]) if params[:owner_name] && params[:project_name]
@project = Project.find_by_owner_and_name!(params[:owner_name], params[:project_name]) if params[:owner_name].present? && params[:project_name].present?
end
def init_statistics

View File

@ -1,9 +1,9 @@
# -*- encoding : utf-8 -*-
class Projects::BuildListsController < Projects::BaseController
NESTED_ACTIONS = [:search, :index, :new, :create]
NESTED_ACTIONS = [:index, :new, :create]
before_filter :authenticate_user!
skip_before_filter :authenticate_user!, :only => [:show, :index, :search, :log] if APP_CONFIG['anonymous_access']
skip_before_filter :authenticate_user!, :only => [:show, :index, :log] if APP_CONFIG['anonymous_access']
before_filter :find_build_list, :only => [:show, :publish, :cancel, :update, :log, :create_container]
@ -11,36 +11,31 @@ class Projects::BuildListsController < Projects::BaseController
load_and_authorize_resource :build_list, :through => :project, :only => NESTED_ACTIONS, :shallow => true
load_and_authorize_resource :except => NESTED_ACTIONS
def search
new_params = {:filter => {}}
params[:filter].each do |k,v|
new_params[:filter][k] = v unless v.empty?
end
new_params[:per_page] = params[:per_page] if params[:per_page].present?
redirect_to @project ? project_build_lists_path(@project, new_params) : build_lists_path(new_params)
end
def index
@action_url = @project ? search_project_build_lists_path(@project) : search_build_lists_path
@filter = BuildList::Filter.new(@project, current_user, current_ability, params[:filter] || {})
params[:filter].each{|k,v| params[:filter].delete(k) if v.blank? } if params[:filter]
@per_page = BuildList::Filter::PER_PAGE.include?(params[:per_page].to_i) ? params[:per_page].to_i : 25
@bls = @filter.find.recent.paginate(
:page => (params[:page].to_i == 0 ? nil : params[:page]),
:per_page => @per_page
)
@build_lists = BuildList.where(:id => @bls.pluck(:id)).recent
.includes(
:save_to_platform,
:save_to_repository,
:build_for_platform,
:arch,
:user,
:source_packages,
:project => [:owner]
)
respond_to do |format|
format.html
format.json do
@filter = BuildList::Filter.new(@project, current_user, current_ability, params[:filter] || {})
@bls = @filter.find.recent
.paginate(
:page => (params[:page].to_i == 0 ? nil : params[:page]),
:per_page => BuildList::Filter::PER_PAGE.include?(params[:per_page].to_i) ? params[:per_page].to_i : 25
)
@build_lists = BuildList.where(:id => @bls.pluck(:id)).recent
.includes(
:save_to_platform,
:save_to_repository,
:build_for_platform,
:user,
:source_packages,
:project
)
@build_server_status = AbfWorker::StatusInspector.projects_status
@build_server_status = AbfWorker::StatusInspector.projects_status
end
end
end
def new

View File

@ -1,5 +1,7 @@
# -*- encoding : utf-8 -*-
module BuildListsHelper
# See: app/assets/javascripts/angularjs/models/build_list.js.erb
def build_list_status_color(status)
case status
when BuildList::BUILD_PUBLISHED, BuildList::SUCCESS
@ -33,12 +35,11 @@ module BuildListsHelper
end
end
def mass_build_options(selected_id)
def mass_build_options
options_from_collection_for_select(
MassBuild.recent.limit(15),
:id,
:name,
selected_id
:name
)
end

View File

@ -10,4 +10,14 @@ module PaginateHelper
{:page => page, :per_page => per_page}
end
def angularjs_will_paginate(collection_or_options = nil, options = {})
if collection_or_options.is_a? Hash
options, collection_or_options = collection_or_options, nil
end
options.merge!(renderer: AngularjsLinkRenderer) unless options[:renderer]
options.merge!(next_label: I18n.t('datatables.next_label')) unless options[:next_label]
options.merge!(previous_label: I18n.t('datatables.previous_label')) unless options[:previous_label]
will_paginate *[collection_or_options, options].compact
end
end

View File

@ -18,7 +18,6 @@ class Ability
can(:refs_list, Project) {|project| can? :show, project}
can :read, Issue, :project => {:visibility => 'open'}
can [:read, :commits, :files], PullRequest, :to_project => {:visibility => 'open'}
can :search, BuildList
can [:read, :log, :everything], BuildList, :project => {:visibility => 'open'}
can [:read, :log], ProductBuildList#, :product => {:platform => {:visibility => 'open'}} # double nested hash don't work
can [:read, :search], Advisory

View File

@ -2,6 +2,8 @@
class BuildList::Filter
PER_PAGE = [25, 50, 100]
attr_reader :options
def initialize(project, user, current_ability, options = {})
@project, @user, @current_ability = project, user, current_ability
set_options(options)

View File

@ -1,10 +1,11 @@
-html_options = {:class => 'sel80 medium input_cleanse', :tabindex => 2}
.top.box
.filter
= form_for :filter, :url => @action_url, :html => { :method => :post, :class => :form, :id => 'monitoring_filter' } do |f|
= form_for :filter, :html => {:class => :form, :id => 'monitoring_filter', 'ng-submit' => 'refresh(true)'}, :authenticity_token => false do |f|
.column
= render 'server_status'
= hidden_field_tag :servertime, Time.now.utc.to_i
= hidden_field_tag :owner_name, @project.try(:owner).try(:uname)
= hidden_field_tag :project_name, @project.try(:name)
.reloader
= label_tag :autoreload do
= check_box_tag :autoreload, true, true
@ -13,50 +14,51 @@
- if current_user
.bordered.nopadding
%h3.medium= t("layout.build_lists.ownership.header")
=f.hidden_field :ownership
=f.hidden_field :ownership, :value => '{{params.filter.ownership}}'
.btn-group
- ['owned', (@project ? nil : 'related'), 'everything'].compact.each do |ownership|
-options = {:class => @filter.ownership == ownership ? 'active' : '', :value => ownership, :style => (@project ? 'width:50%;' : '')}
-options = {'ng-class' =>"{active: params.filter.ownership == '#{ownership}'}",:value => ownership, :style => (@project ? 'width:50%;' : '')}
%button.btn.ownership{options}= t "layout.build_lists.ownership.#{ownership}"
%h3.medium= t 'number_rows'
=hidden_field_tag :per_page, @per_page
=hidden_field_tag :per_page, '{{params.per_page}}'
=hidden_field_tag :page, '{{params.page}}'
.btn-group
-BuildList::Filter::PER_PAGE.each do |num|
%button.btn.per_page{:value => num, :class => @per_page == num ? 'active' : ''}=num
%button.btn.per_page{:value => num, 'ng-class' => "{active: params.per_page == #{num}}"}=num
%h3.medium= t 'activerecord.attributes.build_list.status'
.lineForm.aside
= f.select :status, BuildList::STATUSES.collect{|status| [BuildList.human_status(status), status]}, {:include_blank => true, :selected => @filter.status},
html_options.merge(:id => 'status')
= f.select :status, BuildList::STATUSES.collect{|status| [BuildList.human_status(status), status]}, {:include_blank => true},
html_options.merge(:id => 'status', 'ng-model' => 'params.filter.status')
.both
%br/
.column
%h3.medium= t 'activerecord.models.platform'
.lineForm.aside
= f.select :platform_id, availables_main_platforms.collect{|pl| [pl.name, pl.id]}, {:include_blank => true, :selected => @filter.platform_id}, html_options.merge(:id => 'platform')
= f.select :platform_id, availables_main_platforms.collect{|pl| [pl.name, pl.id]}, {:include_blank => true}, html_options.merge(:id => 'platform', 'ng-model' => 'params.filter.platform_id')
%h3.medium= t 'activerecord.attributes.build_list.arch'
.lineForm.aside
= f.select :arch_id, Arch.recent.collect{|arch| [arch.name, arch.id]}, {:include_blank => true, :selected => @filter.arch_id}, html_options.merge(:id => 'architecture')
= f.select :arch_id, Arch.recent.collect{|arch| [arch.name, arch.id]}, {:include_blank => true}, html_options.merge(:id => 'architecture', 'ng-model' => 'params.filter.arch_id')
%h3.medium= t 'activerecord.models.mass_build'
.lineForm.aside
= f.select :mass_build_id, mass_build_options(@filter.mass_build_id), {:include_blank => true, :selected => @filter.mass_build_id},
html_options.merge(:id => 'mass_build')
= f.select :mass_build_id, mass_build_options, {:include_blank => true},
html_options.merge(:id => 'mass_build', 'ng-model' => 'params.filter.mass_build_id')
.column
-[:updated_at_start, :updated_at_end].each do |attr|
-class_name, message = attr == :updated_at_start ? %w[floatleft _on] : %w[floatright until]
%div{:class => class_name}
%h3.medium= t message
-date = @filter.send(attr) ? @filter.send(attr).strftime('%d/%m/%Y') : nil
=f.text_field attr, :readonly => "readonly", :size => 10, :class => 'mediumheight min input_cleanse', :value => date
=f.text_field attr, :readonly => "readonly", :size => 10, :class => 'mediumheight min input_cleanse', :value => "{{params.filter.#{attr}}}"
=link_to image_tag('x.png', :alt => 'x', :class => 'semi'), "#filter_#{attr}", :id => 'updated_at_clear'
.both
-%w[project_name id].each do |filter|
%h3.medium= t "layout.build_lists.#{filter}_search"
%input.mediumheight.input_cleanse{:id => "filter_#{filter}", :name => "filter[#{filter}]", :size => "30", :type => "text", :value => @filter.send(filter)}
%input.mediumheight.input_cleanse{:id => "filter_#{filter}", :name => "filter[#{filter}]", :size => "30", :type => "text", :value => "{{params.filter.#{filter}}}"}
%br/
%br/
.butgrp
= f.submit t('layout.search.header'), :data => {'disable-with' => t('layout.processing')}
= f.submit t('layout.search.header'), 'ng-hide' => 'isRequest'
= f.submit t('layout.processing'), :disabled => true, 'ng-show' => 'isRequest'
= f.submit t('reset'), :id => 'filter_clear'
-if @project and can?(:create, @project.build_lists.build)
%input{:id => 'filter_new_build', :type => 'button', :onclick => "location.href='#{new_project_build_list_path(@project)}'", :value => t('layout.build_lists.new_header')}

View File

@ -6,7 +6,7 @@
.both
.table
.lefter= t("layout.build_lists.build_server_status.workers")
.righter= @build_server_status[:rpm][:workers]
.righter {{server_status.rpm.workers}}
.both
.table
.lefter= t("layout.build_lists.build_server_status.tasks")
@ -14,7 +14,7 @@
- [['user_tasks', :default_tasks], ['mass_build_tasks', :low_tasks], ['build_tasks', :build_tasks]].each do |label, metric|
.table
.lefter= t("layout.build_lists.build_server_status.#{label}")
.righter= @build_server_status[:rpm][metric]
.righter= "{{server_status.rpm.#{metric}}}"
.both
%br
@ -24,7 +24,7 @@
- [:workers, :tasks, :build_tasks].each do |metric|
.table
.lefter= t("layout.build_lists.build_server_status.#{metric}")
.righter= @build_server_status[:publish][metric]
.righter= "{{server_status.publish.#{metric}}}"
.both
%br

View File

@ -1,73 +1,73 @@
-set_meta_tags :title => t('.title')
= render 'filter'
%div{'ng-controller' => 'BuildListsController'}
%table.tablesorter{:cellpadding => "0", :cellspacing => "0"}
%thead
%tr
%th.lpadding16= t("activerecord.attributes.build_list.id")
%th.lpadding16= t("activerecord.attributes.build_list.status")
%th.lpadding16= t("activerecord.attributes.build_list.project")
%th.lpadding16= t("diff")
%th.lpadding16= t("activerecord.attributes.build_list.project_version")
%th.lpadding16= t("activerecord.attributes.build_list.save_to_repository")
%th.lpadding6= t("activerecord.attributes.build_list.arch_short")
%th.lpadding16= t("activerecord.attributes.build_list.user")
%th.lpadding6= t("activerecord.attributes.build_list.updated_at")
%tbody= render :partial => 'projects/build_lists/build_list', :collection => @build_lists
.both
= render 'filter'
:javascript
$(function(){
// from jQuery.timeago
var parseIso8601 = function(iso8601) {
var s = $.trim(iso8601);
s = s.replace(/\.\d+/,""); // remove milliseconds
s = s.replace(/-/,"/").replace(/-/,"/");
s = s.replace(/T/," ").replace(/Z/," UTC");
s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
return new Date(s);
}
var strTimeBetween = function(dt, dt1) {
%table.tablesorter{:cellpadding => "0", :cellspacing => "0"}
%thead
%tr
%th.lpadding16= t("activerecord.attributes.build_list.id")
%th.lpadding16= t("activerecord.attributes.build_list.status")
%th.lpadding16= t("activerecord.attributes.build_list.project")
%th.lpadding16= t("diff")
%th.lpadding16= t("activerecord.attributes.build_list.project_version")
%th.lpadding16= t("activerecord.attributes.build_list.save_to_repository")
%th.lpadding6= t("activerecord.attributes.build_list.arch_short")
%th.lpadding16= t("activerecord.attributes.build_list.user")
%th.lpadding6= t("activerecord.attributes.build_list.updated_at")
%tbody
%tr{'ng-repeat' => 'bl in build_lists', :class => '{{bl.status_color}}', :id => 'build-list-{{bl.id}}', 'ng-class' => "{'group-start': !bl.relatedHidden, 'group-end': bl.lastRelated}", 'ng-show' => 'bl.show'}
var formatNumber = function(num) {
return (num < 10) ? "0%d".replace("%d", num) : num
}
/ id
%td.build-list-statuses
%a.expand{'ng-show' => 'bl.hasRelated'}
%span.icon-chevron-down{'ng-show' => 'bl.relatedHidden', 'ng-click' => 'showRelated(bl)'}
%span.icon-chevron-up{'ng-hide' => 'bl.relatedHidden', 'ng-click' => 'hideRelated(bl)'}
%a{'ng-href' => '{{bl.url}}' } {{bl.id}}
%div{'ng-show' => 'bl.hasRelated'}
.status{'ng-repeat' => 'related in bl.related', :class => '{{related.status_color}}'} &nbsp;
var seconds = Math.abs(dt1 - dt) / 1000;
var minutes = seconds / 60;
var hours = minutes / 60;
minutes = Math.round(minutes % 60);
hours = Math.floor(hours);
/ status
%td
{{bl.human_status | i18n}}
%br
%time{'ng-show' => 'bl.duration'}
{{bl.duration}}
%time{'ng-show' => 'bl.average_build_time'}
\/{{bl.average_build_time}}
return "%d:%d".replace("%d", formatNumber(hours))
.replace("%d", formatNumber(minutes));
}
// TODO Very, very ugly method.
var now = $('#servertime').val() * 1000;
$("time.js-relative-date").each(function() {
var time = parseIso8601($(this).attr('datetime')).getTime();
$(this).text(strTimeBetween(time, now));
});
/ project
%td.centered{'ng-hide' => 'bl.project', :colspan => 2}
= t('layout.projects.unexisted_project')
%td{'ng-show' => 'bl.project'}
%a{'ng-href' => '{{bl.project.url}}' } {{bl.project.name_with_owner}}
var t = null;
/ diff
%td
%a{'ng-href' => '{{bl.version_link_url}}', 'ng-show' => 'bl.project'}
{{bl.version_link_text}}
var reloadChange = function() {
if ($(this).is(':checked')) {
//reload page every 1 minute
t = setTimeout(function() {
window.location.reload();
}, 60000);
} else {
clearTimeout(t);
}
}
/ project_version
%td {{bl.version_release}}
$('#autoreload').on('change', reloadChange)
.trigger('change');
/ save_to_repository
%td
%a{'ng-href' => '{{bl.save_to_repository_url}}' } {{bl.save_to_repository_name}}
});
/ arch_short
%td{'ng-show' => 'bl.arch'} {{bl.arch.name}}
%td{'ng-hide' => 'bl.arch'}= t('layout.arches.unexisted_arch')
= will_paginate @bls
/ user
%td
%a{'ng-href' => '{{bl.user.url}}' } {{bl.user.fullname}}
/ updated_at
%td {{bl.updated_at}}
.both
= render 'shared/angularjs_will_paginate'
= render @project ? 'projects/base/submenu' : 'projects/build_lists/submenu'

View File

@ -0,0 +1,38 @@
now = Time.now.utc
json.build_lists @build_lists do |build_list|
json.(build_list, :id, :status, :project_id, :project_version, :save_to_platform_id, :save_to_repository_id, :user_id, :project_id, :build_for_platform_id, :arch_id)
json.commit_hash build_list.commit_hash.first(5)
json.last_published_commit_hash build_list.last_published_commit_hash.first(5) if build_list.last_published_commit_hash
if BuildList::HUMAN_STATUSES[build_list.status].in? [:build_pending, :build_started, :build_publish]
json.duration Time.diff(now, build_list.updated_at, '%h:%m')[:diff]
json.average_build_time build_list.formatted_average_build_time if build_list.build_started? && (build_list.average_build_time > 0)
end
json.version_release get_version_release(build_list)
json.updated_at build_list.updated_at.strftime('%d/%m/%Y')
end
json.dictionary do
json.users @build_lists.map(&:user).uniq do |user|
json.(user, :id, :uname, :fullname)
end
json.projects @build_lists.map(&:project).uniq.compact do |project|
json.(project, :id, :name)
json.owner project.name_with_owner.gsub(/\/.*/, '')
end
json.platforms (@build_lists.map(&:build_for_platform) | @build_lists.map(&:save_to_platform)).uniq do |platform|
json.(platform, :id, :name)
json.personal platform.personal?
end
json.repositories @build_lists.map(&:save_to_repository).uniq do |repository|
json.(repository, :id, :name)
end
json.arches Arch.all do |arch|
json.(arch, :id, :name)
end
end
json.server_status @build_server_status
json.filter @filter.try(:options)
json.pages angularjs_will_paginate(@bls)

View File

@ -0,0 +1,14 @@
#will_paginate{'ng-show' => 'pages'}
.pagination
%div{'ng-class' => "{'disabled': !page.active}", 'ng-repeat' => 'page in pages', 'ng-switch' => 'page.type'}
%a{'ng-switch-when' => 'previous_page', 'ng-click' => 'goToPage(page.number)', :href => ''}
= t('datatables.previous_label')
%a{'ng-switch-when' => 'first', :href => ''}
{{page.number}}
%a{'ng-switch-when' => 'page', 'ng-click' => 'goToPage(page.number)', :href => ''}
{{page.number}}
%a{'ng-switch-when' => 'more', 'ng-click' => 'goToPage(page.number)', :href => ''} …
%a{'ng-switch-when' => 'last', 'ng-click' => 'goToPage(page.number)', :href => ''}
{{page.number}}
%a{'ng-switch-when' => 'next_page', 'ng-click' => 'goToPage(page.number)', :href => ''}
= t('datatables.next_label')

View File

@ -0,0 +1,3 @@
JsRoutes.setup do |config|
config.exclude = [/token/, /admin/, /api/, /key/]
end

View File

@ -263,9 +263,6 @@ Rosa::Application.routes.draw do
put :publish
put :reject_publish
end
collection {
post :search
}
end
resources :projects, :only => [:index, :new, :create]
@ -304,9 +301,7 @@ Rosa::Application.routes.draw do
end
post "/labels/:label_id" => "issues#destroy_label", :as => :issues_delete_label
post "/labels/:label_id/update" => "issues#update_label", :as => :issues_update_label
resources :build_lists, :only => [:index, :new, :create] do
collection { post :search }
end
resources :build_lists, :only => [:index, :new, :create]
resources :collaborators do
get :find, :on => :collection
end

View File

@ -0,0 +1,43 @@
class Array
def html_safe
self
end
end
class AngularjsLinkRenderer < WillPaginate::ActionView::LinkRenderer
def to_html
pagination.map do |item|
item.is_a?(Fixnum) ? page_number(item) : send(item)
end
end
protected
def page_number(page)
unless page == current_page
{:active => true, :number => page, :type => :page}
else
{:active => false, :number => page, :type => :first}
end
end
def gap
nil
end
def next_page
num = @collection.current_page < @collection.total_pages && @collection.current_page + 1
previous_or_next_page(num, @options[:next_label], :next_page)
end
def previous_or_next_page(page, text, classname)
if page
{:active => true, :number => page, :type => classname}
else
{:active => false, :number => page, :type => classname}
end
end
end

View File

@ -406,7 +406,7 @@ describe Projects::BuildListsController do
end
it 'should filter by id' do
get :index, :filter => {:id => @build_list1.id, :project_name => 'fdsfdf', :any_other_field => 'do not matter'}
get :index, :filter => {:id => @build_list1.id, :project_name => 'fdsfdf', :any_other_field => 'do not matter'}, :format => :json
assigns[:build_lists].should include(@build_list1)
assigns[:build_lists].should_not include(@build_list2)
assigns[:build_lists].should_not include(@build_list3)
@ -414,7 +414,7 @@ describe Projects::BuildListsController do
it 'should filter by project_name' do
# Project.where(:id => build_list2.project.id).update_all(:name => 'project_name')
get :index, :filter => {:project_name => @build_list2.project.name, :ownership => 'everything'}
get :index, :filter => {:project_name => @build_list2.project.name, :ownership => 'everything'}, :format => :json
assigns[:build_lists].should_not include(@build_list1)
assigns[:build_lists].should include(@build_list2)
assigns[:build_lists].should_not include(@build_list3)
@ -422,7 +422,7 @@ describe Projects::BuildListsController do
it 'should filter by project_name and update_date' do
get :index, :filter => {:project_name => @build_list3.project.name, :ownership => 'everything',
"updated_at_start" => @build_list3.updated_at.strftime('%d/%m/%Y')}
"updated_at_start" => @build_list3.updated_at.strftime('%d/%m/%Y')}, :format => :json
assigns[:build_lists].should_not include(@build_list1)
assigns[:build_lists].should_not include(@build_list2)
assigns[:build_lists].should include(@build_list3)