diff --git a/Gemfile b/Gemfile index 8fb1ff4af..14dd1a367 100644 --- a/Gemfile +++ b/Gemfile @@ -65,6 +65,8 @@ gem 'ng-rails-csrf' gem 'momentjs-rails' gem 'angular-i18n', '0.1.2' +gem 'time_diff' + group :assets do gem 'sass-rails', '~> 3.2.5' gem 'coffee-rails', '~> 3.2.2' diff --git a/Gemfile.lock b/Gemfile.lock index e07a577be..42f270c96 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -392,6 +392,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) @@ -494,6 +497,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) diff --git a/app/assets/javascripts/angularjs/controllers/build_lists_controller.js b/app/assets/javascripts/angularjs/controllers/build_lists_controller.js new file mode 100644 index 000000000..6215f3719 --- /dev/null +++ b/app/assets/javascripts/angularjs/controllers/build_lists_controller.js @@ -0,0 +1,74 @@ +RosaABF.controller('BuildListsController', ['$scope', '$http', '$location', '$timeout', function($scope, $http, $location, $timeout) { + + $scope.filter = null; + $scope.first_run = true; + $scope.server_status = null; + $scope.build_lists = []; + $scope.isRequest = false; + + + + $scope.getBuildLists = function() { + $scope.isRequest = true; + $http.get('/build_lists.json', {params: $location.search()}).success(function(results) { + $scope.server_status = results.server_status; + $scope.build_lists = []; + _.each(results.build_lists, function(bl){ + $scope.build_lists.push(new BuildList(bl)); + }); + $scope.isRequest = false; + }).error(function(data, status, headers, config) { + console.log(config); + $scope.isRequest = false; + });; + } + + $scope.search = function() { + if ($.isEmptyObject($location.search()) || !$scope.first_run) { + var array = $("#monitoring_filter").serializeArray(); + var params = {}; + for(i=0; i (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, diff --git a/app/helpers/build_lists_helper.rb b/app/helpers/build_lists_helper.rb index ef6664129..111c1cd2b 100644 --- a/app/helpers/build_lists_helper.rb +++ b/app/helpers/build_lists_helper.rb @@ -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 diff --git a/app/models/build_list/filter.rb b/app/models/build_list/filter.rb index 4ff017eac..1c58a483c 100644 --- a/app/models/build_list/filter.rb +++ b/app/models/build_list/filter.rb @@ -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) diff --git a/app/views/projects/build_lists/_filter.html.haml b/app/views/projects/build_lists/_filter.html.haml index 13d5c697f..8d789507a 100644 --- a/app/views/projects/build_lists/_filter.html.haml +++ b/app/views/projects/build_lists/_filter.html.haml @@ -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' => 'search()'}, :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,33 +14,33 @@ - if current_user .bordered.nopadding %h3.medium= t("layout.build_lists.ownership.header") - =f.hidden_field :ownership + =f.hidden_field :ownership, :value => '{{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: 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, '{{filter.per_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: filter.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' => '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' => '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' => '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(@filter.mass_build_id), {:include_blank => true}, + html_options.merge(:id => 'mass_build', 'ng-model' => 'filter.mass_build_id') .column -[:updated_at_start, :updated_at_end].each do |attr| @@ -47,16 +48,17 @@ %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 => "{{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 => "{{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')} diff --git a/app/views/projects/build_lists/_server_status.html.haml b/app/views/projects/build_lists/_server_status.html.haml index b33d69231..25a29c36d 100644 --- a/app/views/projects/build_lists/_server_status.html.haml +++ b/app/views/projects/build_lists/_server_status.html.haml @@ -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 diff --git a/app/views/projects/build_lists/index.html.haml b/app/views/projects/build_lists/index.html.haml index d3e8c5f65..b23d0e980 100644 --- a/app/views/projects/build_lists/index.html.haml +++ b/app/views/projects/build_lists/index.html.haml @@ -1,73 +1,56 @@ -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}}'} + %td + %a{'ng-href' => '{{bl.url}}' } {{bl.id}} + %td + {{bl.human_status}} + %br + %time{'ng-show' => 'bl.duration'} + {{bl.duration}} + %time{'ng-show' => 'bl.average_build_time'} + \/{{bl.average_build_time}} - var formatNumber = function(num) { - return (num < 10) ? "0%d".replace("%d", num) : num - } + %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}} + %td{'ng-show' => 'bl.project', 'ng-bind-html-unsafe' => 'bl.project.version_link'} - var seconds = Math.abs(dt1 - dt) / 1000; - var minutes = seconds / 60; - var hours = minutes / 60; - minutes = Math.round(minutes % 60); - hours = Math.floor(hours); + %td {{bl.project_version}} + %td + %a{'ng-href' => '{{bl.save_to_repository.url}}' } {{bl.save_to_repository.name}} - 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)); - }); + %td{'ng-show' => 'bl.arch'} {{bl.arch}} + %td{'ng-hide' => 'bl.arch'}= t("layout.arches.unexisted_arch") - var t = null; + %td + %a{'ng-href' => '{{bl.user.url}}' } {{bl.user.fullname}} - var reloadChange = function() { - if ($(this).is(':checked')) { - //reload page every 1 minute - t = setTimeout(function() { - window.location.reload(); - }, 60000); - } else { - clearTimeout(t); - } - } + %td {{bl.updated_at}} - $('#autoreload').on('change', reloadChange) - .trigger('change'); - }); -= will_paginate @bls + .both + + + = will_paginate @bls = render @project ? 'projects/base/submenu' : 'projects/build_lists/submenu' diff --git a/app/views/projects/build_lists/index.json.jbuilder b/app/views/projects/build_lists/index.json.jbuilder new file mode 100644 index 000000000..169042871 --- /dev/null +++ b/app/views/projects/build_lists/index.json.jbuilder @@ -0,0 +1,39 @@ +now = Time.now.utc +json.build_lists @build_lists do |build_list| + json.(build_list, :id, :status, :human_status) + json.url build_list_path(build_list) + + 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.status_color build_list_status_color(build_list.status) + + json.project do + json.name_with_owner build_list.project.name_with_owner + json.url project_path(build_list.project) + json.version_link build_list_version_link(build_list).html_safe + end if build_list.project.present? + + json.project_version get_version_release(build_list) + + json.save_to_repository do + build_for = " (#{build_list.build_for_platform.name})" if build_list.build_for_platform && build_list.save_to_platform.personal? + + json.name "#{build_list.save_to_platform.name}/#{build_list.save_to_repository.name}#{build_for}" + + json.url platform_repository_path(build_list.save_to_platform, build_list.save_to_repository) + end + + json.arch build_list.arch.try(:name) + + json.user do + json.fullname build_list.user.try(:fullname) + json.url user_path(build_list.user) + end + + json.updated_at build_list.updated_at.strftime('%d/%m/%Y') +end + +json.server_status @build_server_status +json.filter @filter.try(:options) diff --git a/config/routes.rb b/config/routes.rb index ea1cfcdb3..69b0f9058 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -264,7 +264,7 @@ Rosa::Application.routes.draw do put :reject_publish end collection { - post :search + post :search } end