Hi,
sending updated version of selection model.

TODO:
- styling (multicolumns, loadmasks)
- (maybe?) grouping packages by groups (as in previous model)
- pagination
- type-ahead search enhancement of previous suggestion (quite difficult)

Jan
From 3d3c8f006ca55b9c35f31c5a81e31e94fbff9b19 Mon Sep 17 00:00:00 2001
From: Jan Provaznik <[email protected]>
Date: Wed, 20 Oct 2010 11:00:10 +0200
Subject: [PATCH aggregator] selection model - first shoot

Rerun "rake dc:prepare_repos" after applying. Note that this not yet final
version, should be applied on patcheset "rebased pending patches".
---
 src/app/controllers/templates_controller.rb        |   38 +++++++++++--
 src/app/util/repository_manager.rb                 |   59 ++++++++++++++++++++
 .../util/repository_manager/comps_repository.rb    |   58 +++++++++++++++++---
 src/app/util/repository_manager/pulp_repository.rb |   25 ++++++++-
 src/app/views/templates/_collections.haml          |    6 ++
 src/app/views/templates/_managed_content.haml      |    4 +-
 src/app/views/templates/_metagroup_packages.haml   |    8 +++
 src/app/views/templates/content_selection.haml     |   49 ++++++++++-------
 src/app/views/templates/new.haml                   |   24 +++++---
 .../image_descriptor_package_metagroups.conf       |   20 +++++++
 10 files changed, 246 insertions(+), 45 deletions(-)
 create mode 100644 src/app/views/templates/_collections.haml
 create mode 100644 src/app/views/templates/_metagroup_packages.haml
 create mode 100644 src/config/image_descriptor_package_metagroups.conf

diff --git a/src/app/controllers/templates_controller.rb 
b/src/app/controllers/templates_controller.rb
index e06bae5..d658aa7 100644
--- a/src/app/controllers/templates_controller.rb
+++ b/src/app/controllers/templates_controller.rb
@@ -62,6 +62,13 @@ class TemplatesController < ApplicationController
       params[:packages] = params[:selected_packages]
       new
 
+    elsif params.include?('show_metagroup')
+      @metagroup = params['show_metagroup']
+      content_selection
+
+    elsif not params[:package_search].blank?
+      content_selection
+
     end
   end
 
@@ -105,9 +112,32 @@ class TemplatesController < ApplicationController
     @tpl = @id.blank? ? Template.new : Template.find(@id)
     @tpl.attributes = params[:tpl] unless params[:tpl].nil?
     @packages = []
-    @packages = params[:packages].collect{ |p| { :name => p } } if 
params[:packages]
+    @packages += params[:packages].collect{ |p| { :name => p } } unless 
params[:packages].blank?
+    @packages += params[:selected_packages].collect{ |p| { :name => p } } 
unless params[:selected_packages].blank?
     @groups = 
@repository_manager.all_groups_with_tagged_selected_packages(@packages, 
@tpl.platform)
     @embed  = params[:embed]
+    @categories = @repository_manager.categories(params[:tpl] ? 
params[:tpl][:platform] : nil)
+    @metagroups = @repository_manager.metagroups
+
+    if not params[:package_search].blank?
+      @metagroup_packages = 
@repository_manager.search_package(params[:package_search], params[:repository])
+    elsif not @metagroup.blank?
+      if @metagroup == 'Collections'
+        # TODO: if we remember selected groups, this could be done much more 
simply
+        @collections = 
@repository_manager.all_groups_with_tagged_selected_packages(@packages, 
params[:repository])
+      else
+        @metagroup_packages = 
@repository_manager.metagroup_packages(@metagroup, params[:repository])
+      end
+    end
+    if request.xhr?
+      if @metagroup_packages
+        render :partial => 'metagroup_packages' and return
+      end
+      if @collections
+        render :partial => 'collections' and return
+      end
+    end
+
     if @embed
       render :layout => false
     else
@@ -210,10 +240,8 @@ add account on <a href=\"#{url_for :controller => 
'provider', \
     @repository_manager = RepositoryManager.new
     @groups = @repository_manager.all_groups(params[:repository])
 
-    if params[:packages]
-      @selected_packages = params[:packages]
-    elsif params[:selected_packages]
-      @selected_packages = params[:selected_packages]
+    if not params[:packages].blank? or not params[:selected_packages].blank?
+      @selected_packages = params[:packages].to_a + 
params[:selected_packages].to_a
     elsif !tpl.nil?
       @selected_packages = tpl.xml.packages.collect { |p| p[:name] }
     else
diff --git a/src/app/util/repository_manager.rb 
b/src/app/util/repository_manager.rb
index fbe7a8b..64de179 100644
--- a/src/app/util/repository_manager.rb
+++ b/src/app/util/repository_manager.rb
@@ -47,6 +47,61 @@ class RepositoryManager
     return @all_groups
   end
 
+  def packages(repository = nil)
+  end
+
+  def categories(repository = nil)
+    unless @all_categories
+      @all_categories = {}
+      repositories.each do |r|
+        next if repository and repository != 'all' and repository != r.id
+        r.categories.each do |id, data|
+          if @all_categories[id]
+            @all_categories[id][:groups] += data[:groups]
+          else
+            @all_categories[id] = data
+          end
+        end
+      end
+    end
+    return @all_categories
+  end
+
+  def packages(repository = nil)
+    unless @packages
+      @packages = []
+      repositories.each do |r|
+        next if repository and repository != 'all' and repository != r.id
+        @packages += r.packages
+      end
+    end
+    return @packages
+  end
+
+  # TODO: this is temporary solution for categorizing packages
+  def metagroups
+    unless @metagroups
+      @metagroups = {}
+      File.readlines('config/image_descriptor_package_metagroups.conf').each 
do |line|
+        group, entries_str = line.chomp.split('=')
+        next unless group and entries_str
+        @metagroups[group] = entries_str.split(',')
+      end
+    end
+    @metagroups
+  end
+
+  def metagroup_packages(category, repository = nil)
+    res = []
+    groups = all_groups(repository)
+    metagroups[category].to_a.each do |entry|
+      cat, group = entry.split(';')
+      next unless c = categories[cat] and c[:groups].include?(group) and 
groups[group]
+      res += groups[group][:packages].keys
+    end
+    res.sort.uniq
+  end
+
   def all_groups_with_tagged_selected_packages(pkgs, repository = nil)
     groups = all_groups(repository)
     groups.each_value do |group|
@@ -67,6 +122,10 @@ class RepositoryManager
     res
   end
 
+  def search_package(str, repository = nil)
+    packages.select {|p| p =~ /#{Regexp.escape(str)}/i}
+  end
+
   private
 
   # returns true if all non-optional packages are selected
diff --git a/src/app/util/repository_manager/comps_repository.rb 
b/src/app/util/repository_manager/comps_repository.rb
index 80391ef..86c4da7 100644
--- a/src/app/util/repository_manager/comps_repository.rb
+++ b/src/app/util/repository_manager/comps_repository.rb
@@ -11,11 +11,19 @@ class CompsRepository < AbstractRepository
   end
 
   def groups
-    begin
-      @groups ||= Marshal.load(File.open(@cache_file, 'r'))
-    rescue Errno::ENOENT
-      raise "failed to read cached packages info, run 'rake dc:prepare_repos'"
+    @groups ||= load_data[:groups]
+  end
+
+  def categories
+    @categories ||= load_data[:categories]
+  end
+
+  def packages
+    pkgs = []
+    groups.each_value do |g|
+      pkgs += g[:packages].keys
     end
+    pkgs.uniq
   end
 
   def prepare_repo
@@ -25,7 +33,8 @@ class CompsRepository < AbstractRepository
       pkgs = group_packages(g)
       next if pkgs.empty?
       name = g.at_xpath('name').text
-      grps[name] = {
+      id = g.at_xpath('id').text
+      grps[id] = {
         :name => name,
         :repository_id => @id,
         :packages => pkgs
@@ -45,18 +54,51 @@ class CompsRepository < AbstractRepository
       }
     end
 
+    categories = {}
+    category_nodes.each do |cat|
+      id = cat.at_xpath('id').text
+      categories[id] = {
+        :name => cat.at_xpath('name').text,
+        :groups => cat.xpath('./grouplist/groupid').map {|g| g.text}
+      }
+    end
+
     Dir.mkdir(@cache_dir) unless File.directory?(@cache_dir)
-    Marshal.dump(grps, File.open(@cache_file, 'w'))
+    Marshal.dump({:groups => grps,
+                  :categories => categories}, File.open(@cache_file, 'w'))
   end
 
   private
 
+  def load_data
+    unless @load_data
+      begin
+        @load_data = Marshal.load(File.open(@cache_file, 'r'))
+      rescue Errno::ENOENT
+        raise "failed to read cached packages info, run 'rake 
dc:prepare_repos'"
+      end
+    end
+    @load_data
+  end
+
+  def parsed_group_xml
+    unless @xml
+      return nil unless data = group_xml
+      @xml = Nokogiri::XML(data)
+    end
+    @xml
+  end
+
   def group_nodes
-    return [] unless data = group_xml
-    xml = Nokogiri::XML(data)
+    return [] unless xml = parsed_group_xml
     xml.xpath('/comps/group')
   end
 
+  def category_nodes
+    return [] unless xml = parsed_group_xml
+    xml.xpath('/comps/category')
+  end
+
   def pkg_names
     pkgs = {}
     xml = Nokogiri::XML(primary_xml)
diff --git a/src/app/util/repository_manager/pulp_repository.rb 
b/src/app/util/repository_manager/pulp_repository.rb
index a0ea005..051bc47 100644
--- a/src/app/util/repository_manager/pulp_repository.rb
+++ b/src/app/util/repository_manager/pulp_repository.rb
@@ -30,6 +30,8 @@ class PulpRepository < AbstractRepository
   def initialize(conf)
     super
     @groups_url = File.join(strip_path(@baseurl), conf['packagegroups'])
+    @categories_url = File.join(strip_path(@baseurl), 
conf['packagegroupcategories'])
+    @packages_url = File.join(strip_path(@baseurl), conf['packages'])
   end
 
   def groups
@@ -41,8 +43,8 @@ class PulpRepository < AbstractRepository
       info['mandatory_package_names'].each {|p| pkgs[p] = {:type => 
'mandatory'}}
       next if pkgs.empty?
       name = info['name']
-      groups[name] = {
-        :name => name,
+      groups[info['id']] = {
+        :name => info['name'],
         :description => info['description'].to_s,
         :repository_id => @id,
         :packages => pkgs,
@@ -51,6 +53,25 @@ class PulpRepository < AbstractRepository
     return groups
   end
 
+  def categories
+    categories = {}
+    WrappedRestClient.get(@categories_url, HTTP_OPTS).each do |id, info|
+      categories[info['id']] = {
+        :name => info['name'],
+        :groups => info['packagegroupids'],
+      }
+    end
+    return categories
+  end
+
+  def packages
+    packages = []
+    WrappedRestClient.get(@packages_url, HTTP_OPTS).each do |info|
+      packages << info['name']
+    end
+    return packages
+  end
+
   private
 
   def strip_path(url)
diff --git a/src/app/views/templates/_collections.haml 
b/src/app/views/templates/_collections.haml
new file mode 100644
index 0000000..1b4604b
--- /dev/null
+++ b/src/app/views/templates/_collections.haml
@@ -0,0 +1,6 @@
+%ul.metagrouppackages
+  - @collections.keys.sort.each do |id|
+    %li
+      - selected = @collections[id][:selected] ? true : false
+      = check_box_tag 'groups[]', id, selected, {:disabled => selected, :id => 
"group_#{id}"}
+      = label_tag "group_#{id}", @collections[id][:name]
diff --git a/src/app/views/templates/_managed_content.haml 
b/src/app/views/templates/_managed_content.haml
index 97c4046..0657ff1 100644
--- a/src/app/views/templates/_managed_content.haml
+++ b/src/app/views/templates/_managed_content.haml
@@ -9,10 +9,10 @@
       No selected packages
     - else
       - repos = @repository_manager.repositories_hash
-      - @selected_packages.each do |pkg|
+      - @selected_packages.sort.each do |pkg|
         - pkg_group = @groups.keys.find {|g| @groups[g][:packages][pkg]}
         %fieldset.clearfix
-          = text_field_tag 'packages[]', pkg, :id => 
"selected_package_#{pkg}", :class => "alpha grid_7 packagename"
+          = text_field_tag 'packages[]', pkg, :id => "package_#{pkg}", :class 
=> "alpha grid_7 packagename"
           .grid_2= (pkg_group and repo = 
rep...@groups[pkg_group][:repository_id]]) ? repo.name.to_s : '&nbsp;'
           .grid_5.omega
             %button{:type => 'button', :disabled => 'disabled'} Config
diff --git a/src/app/views/templates/_metagroup_packages.haml 
b/src/app/views/templates/_metagroup_packages.haml
new file mode 100644
index 0000000..f1a42ec
--- /dev/null
+++ b/src/app/views/templates/_metagroup_packages.haml
@@ -0,0 +1,8 @@
+%h4
+  = params[:package_search].blank? ? "#...@metagroup} Packages" : "Search 
Results for '#{params[:package_search]}'"
+%ul.metagrouppackages
+  - @metagroup_packages.sort.each do |pkg|
+    %li
+      - selected = @packages.to_a.find {|p| p[:name] == pkg}
+      = check_box_tag 'packages[]', pkg, selected, {:disabled => selected, :id 
=> "package_#{pkg}"}
+      = label_tag "package_#{pkg}", pkg
diff --git a/src/app/views/templates/content_selection.haml 
b/src/app/views/templates/content_selection.haml
index c9c3568..ce2231c 100644
--- a/src/app/views/templates/content_selection.haml
+++ b/src/app/views/templates/content_selection.haml
@@ -1,3 +1,17 @@
+:javascript
+  $(document).ready(function() {
+    var $metagrouppackages = $('#metagrouppackages');
+    $('input[name="show_metagroup"]').click(function(e) {
+      e.preventDefault();
+      var data = {
+        'show_metagroup': e.currentTarget.value,
+        selected_packages:  
$("input:hidden[name='selected_packages[]']").map(function() {return 
$(this).val()}).get()
+      };
+      var url = '#{url_for :action => 'dispatch', :id => @id}';
+      $metagrouppackages.load(url, data);
+    });
+  });
+
 .grid_16
   %h3 Managed Content Selection
 
@@ -8,7 +22,7 @@
       <input type="hidden" id="selected_package_#{p[:name]}" 
name="selected_packages[]" value="#{p[:name]}" />
     %fieldset.clearfix
       .search.grid_4.alpha
-        %input{:type => "search", :placeholder => "Search for package", 
:disabled => "disabled"}
+        %input{:type => "search", :placeholder => "Search for package", :name 
=> 'package_search'}
         %button.action
       .grid_8
         %p
@@ -20,26 +34,21 @@
             = label_tag 'repositories[]', repo.name
       %a.grid_4.omega Advanced Search
 
-    %ul.softwaregroups
-      - groups = @groups.keys.sort
-      - unsorted = groups.delete('unsorted')
-      - groups.push('unsorted') if params[:show_unsorted] and unsorted
-      - groups.each do |group|
-        - group_sel = @groups[group][:selected]
-        - group_id = group.gsub(/\s/, '_')
+    %ul.metagroups{:class => 'actionsidebar', :style => 'float:left'}
+      %li
+        = submit_tag 'Collections', :name => 'show_metagroup', :class => 'icon'
+      %hr
+      - @metagroups.keys.sort.each do |cat|
         %li
-          = check_box_tag 'groups[]', group, group_sel, :id => 
"group_#{group_id}"
-          %label.disclosure
-            =group
-          %ul{:class => "packages group_#{group_id}"}
-            - pkgs = @groups[group][:packages]
-            - pkgs.keys.sort.each do |pkg|
-              - pkg_sel = pkgs[pkg][:selected] ? true : false
-              - pkg_id = pkg.gsub(/\s/, '_')
-              %li
-                = check_box_tag 'packages[]', pkg, pkg_sel, :id => 
"package_#{pkg_id}"
-                = label_tag "package_#{pkg_id}", pkg
-      = link_to "Show unsorted packages", {:action => 'dispatch', 
:add_software_form => true, :show_unsorted => true, 'tpl[id]' => @tpl.id}, {:id 
=> 'switch_all_link'} unless params[:show_unsorted]
+          = submit_tag cat, :name => "show_metagroup", :class => 'icon'
+    #metagrouppackages
+      - if @collections
+        = render :partial => 'collections'
+      - else
+        - if @metagroup_packages.blank?
+          No group selected
+        - else
+          = render :partial => 'metagroup_packages'
 
     %fieldset.clearfix
       = submit_tag "Add Selected", :name => "add_selected", :class => "grid_2 
alpha", :id => "do_add_software"
diff --git a/src/app/views/templates/new.haml b/src/app/views/templates/new.haml
index 2de44cb..152d7cc 100644
--- a/src/app/views/templates/new.haml
+++ b/src/app/views/templates/new.haml
@@ -11,7 +11,7 @@
         'tpl[platform]': $("select[name='tpl[platform]']").val() || ''
       }
       var url = '#{url_for :action => 'content_selection', :embed => true, :id 
=> @id}';
-      $content_container.empty().show();
+      //$content_container.empty().show();
       $sel_pkg_container.empty().show().addClass('loading');
       $sel_pkg_container.load(url, data, function(){
         $sel_pkg_container.removeClass('loading');
@@ -19,8 +19,11 @@
         $('#do_add_software').click(function(e) {
           e.preventDefault();
           var url = '#{url_for :action => 'managed_content'}';
+          var new_pkgs = $("input:checked[name='packages[]']").map(function() 
{return $(this).val()}).get();
+          var old_pkgs = 
$("input:hidden[name='selected_packages[]']").map(function() {return 
$(this).val()}).get();
           var data = {
-            'selected_packages[]': 
$("input:checked[name='packages[]']").map(function() {return 
$(this).val()}).get(),
+            'selected_packages[]': old_pkgs.concat(new_pkgs),
+            'selected_groups[]': 
$("input:checked[name='groups[]']").map(function() {return 
$(this).val()}).get(),
             'template_id'  : '#[email protected]? ? nil : @id}'
           };
           $content_container.load(url, data, function(){
@@ -45,14 +48,19 @@
             $submit.show();
           });
         });
-        //select all packages in group
-        $(".softwaregroups input[type='checkbox']").click(function() {
-          if ($(this).attr("checked") === true) {
-            
$(this).siblings("ul").find("input[type='checkbox']").attr("checked","checked");
-          } else {
-            
$(this).siblings("ul").find("input[type='checkbox']").removeAttr("checked");
+        //search
+        $("input[name='package_search']").keypress(function(event) {
+          if (event.keyCode == '13') {
+            event.preventDefault();
+            $("input[name=package_search] ~ button").click();
           }
         });
+        $("input[name=package_search] ~ button").click(function(event) {
+          event.preventDefault();
+          var data = { 'package_search': 
$("input[name='package_search']").val() };
+          var url = '#{url_for :action => 'dispatch', :id => @id}';
+          $('#metagrouppackages').load(url, data);
+        });
         //disclosure triangles
         $(".packages").hide();
         $groups.click(function() {
diff --git a/src/config/image_descriptor_package_metagroups.conf 
b/src/config/image_descriptor_package_metagroups.conf
new file mode 100644
index 0000000..7218e5c
--- /dev/null
+++ b/src/config/image_descriptor_package_metagroups.conf
@@ -0,0 +1,20 @@
+admin-tools=base-system;system-tools,base-system;admin-tools,rpmfusion_free;hardware-support
+desktop-gnome=rpmfusion_free;gnome-desktop,desktops;gnome-desktop
+desktop-kde=desktops;kde-desktop,rpmfusion_free;kde-desktop
+desktop-xfce=desktops;xfce-desktop
+desktop-other=desktops;window-managers,desktops;sugar-desktop,desktops;lxde-desktop,desktops;moblin-desktop
+education=apps;education,development;electronic-lab
+fonts=base-system;fonts,base-system;legacy-fonts
+games=apps;games,rpmfusion_free;games,rpmfusion_nonfree;games
+graphics=apps;graphics
+internet=apps;text-internet,apps;graphical-internet,rpmfusion_free;internet
+legacy=base-system;legacy-software-support
+localization=base-system;input-methods,language-support;khmer-support,language-support;persian-support,language-support;georgian-support,language-support;malay-support,language-support;tonga-support,language-support;portuguese-support,language-support;japanese-support,language-support;hungarian-support,language-support;somali-support,language-support;punjabi-support,language-support;bhutanese-support,language-support;british-support,language-support;korean-support,language-support;lao-support,language-support;inuktitut-support,language-support;german-support,language-support;hindi-support,language-support;faeroese-support,language-support;swedish-support,language-support;tsonga-support,language-support;russian-support,language-support;serbian-support,language-support;latvian-support,language-support;samoan-support,language-support;sinhala-support,language-support;catalan-support,language-support;lithuanian-support,language-support;turkish-support,language-support;arabic-support,language-support;vietnamese-support,language-support;mongolian-support,language-support;tswana-support,language-support;irish-support,language-support;italian-support,language-support;slovak-support,language-support;slovenian-support,language-support;belarusian-support,language-support;northern-sotho-support,language-support;kannada-support,language-support;malayalam-support,language-support;swati-support,language-support;breton-support,language-support;romanian-support,language-support;greek-support,language-support;tagalog-support,language-support;zulu-support,language-support;tibetan-support,language-support;danish-support,language-support;afrikaans-support,language-support;southern-sotho-support,language-support;bosnian-support,language-support;brazilian-support,language-support;basque-support,language-support;welsh-support,language-support;thai-support,language-support;telugu-support,language-support;low-saxon-support,language-support;urdu-support,language-support;tamil-support,language-support;indonesian-support,language-support;gujarati-support,language-support;xhosa-support,language-support;chinese-support,language-support;czech-support,language-support;venda-support,language-support;bulgarian-support,language-support;albanian-support,language-support;galician-support,language-support;armenian-support,language-support;dutch-support,language-support;oriya-support,language-support;maori-support,language-support;nepali-support,language-support;icelandic-support,language-support;ukrainian-support,language-support;assamese-support,language-support;bengali-support,language-support;spanish-support,language-support;hebrew-support,language-support;estonian-support,language-support;french-support,language-support;croatian-support,language-support;filipino-support,language-support;finnish-support,language-support;norwegian-support,language-support;southern-ndebele-support,language-support;polish-support,language-support;gaelic-support,language-support;marathi-support,language-support;ethiopic-support,language-support;esperanto-support,language-support;northern-sami-support,language-support;macedonian-support,language-support;walloon-support,language-support;kashubian-support,language-support;kashmiri-support,language-support;konkani-support,language-support;tajik-support,language-support;sindhi-support,language-support;uzbek-support,language-support;burmese-support,language-support;maithili-support,language-support;kazakh-support,language-support;azerbaijani-support,language-support;fijian-support,language-support;tetum-support,language-support;upper-sorbian-support,language-support;sanskrit-support,language-support;sardinian-support,language-support;swahili-support,language-support;maltese-support,language-support;friulian-support,language-support;latin-support,language-support;interlingua-support,language-support;occitan-support,language-support;hiligaynon-support,language-support;malagasy-support,language-support;amazigh-support,language-support;manx-support,language-support;turkmen-support,language-support;kinyarwanda-support,language-support;kurdish-support,language-support;coptic-support,language-support;luxembourgish-support,language-support;frisian-support,language-support;chichewa-support
+multimedia=apps;sound-and-video,rpmfusion_free;sound-and-video
+office=apps;office,apps;editors
+other=apps;engineering-and-scientific,rpmfusion_free;misc-libs,rpmfusion_nonfree;emulators
+programming=development;haskell,development;kde-software-development,development;gnome-software-development,development;development-tools,development;eclipse,development;development-libs,development;x-software-development,development;web-development,development;legacy-software-development,development;ruby,development;java-development,development;xfce-software-development,development;fedora-packager,development;mingw32,development;ocaml;development;perl;development;books
+publishing=apps;authoring-and-publishing,apps;font-design
+servers=servers;clustering,servers;dns-server,servers;server-cfg,servers;news-server,servers;web-server,servers;smb-server,servers;sql-server,servers;ftp-server,servers;printing,servers;mysql,servers;mail-server,servers;network-server,servers;legacy-network-server;servers;directory-server
+system=base-system;java,base-system;base-x,base-system;hardware-support,base-system;dial-up,base-system;base,rpmfusion_free;base,rpmfusion_free;system-tools,rpmfusion_nonfree;hardware-support,rpmfusion_nonfree;misc-tools,rpmfusion_nonfree;base
+virtualization=base-system;virtualization
-- 
1.7.2.3

_______________________________________________
deltacloud-devel mailing list
[email protected]
https://fedorahosted.org/mailman/listinfo/deltacloud-devel

Reply via email to