class Elexis::Wiki::Images

This helper class collect information about all images used in subdirectories doc ending with mediawiki and stores the results in a pictures.yml and a pictures.csv file

Several helper allow showing unused pictures, identifying files with same name, but different content and pictures use in several sub-directories

Attributes

actions[R]
csv[R]
docDir[R]
dup_non_identical[R]
duplicates[R]
extension[R]
multiple_names[R]
new_best_name[R]
nr_single_sha[R]
pictures[R]
rootDir[R]
sha_2_png[R]
short_and_sha_okay[R]
to_short_names[R]
wrong_best_name[R]
yml[R]

Public Class Methods

get_project_abbreviation(path) click to toggle source
# File lib/elexis/wiki/images.rb, line 183
def Images.get_project_abbreviation(path)
  dir = File.dirname(path)
  dir.sub!(/\/images$/, '')
  found = dir.sub(/(\.feature|[._]test.*|)\/doc/i, '').sub(/\.v\d$/, '')
  found.split('.')[-1]
end
get_small_name(path) click to toggle source
# File lib/elexis/wiki/images.rb, line 197
def Images.get_small_name(path)
  ext =  File.extname(path)
  to_consider = File.basename(path, ext).gsub(':','-').underscore
  if to_consider.index('.')
    part = to_consider.split('.')[-1].downcase
    if  part.split('_').size > 1
      result = part.split('_')[1..-1].join('_')
    else
      result = part
    end
  else
    result = to_consider
  end
  short = Images.get_project_abbreviation(path)
  regexp = /^(#{short})([^-])/i
  m = regexp.match(result)
  result = result.sub(regexp, '\1-\2') if m and short
  (result + ext).downcase
end
new(rootDir = Dir.pwd, docDir = 'doc', extension = '.mediawiki') click to toggle source
# File lib/elexis/wiki/images.rb, line 166
def initialize(rootDir = Dir.pwd, docDir = 'doc', extension = '.mediawiki')
  @rootDir            = rootDir
  @docDir             = docDir
  @extension          = extension
  @add_changes_to_git = false
  @pictures           = []
  @multiple_names     = {}
  @to_short_names     = {}
  @nr_single_sha      = []
  @short_and_sha_okay = []
  @wrong_best_name    = []
  @new_best_name      = {}
  @yml                = File.expand_path(File.join(@rootDir, 'pictures.yml'))
  @csv                = File.expand_path(File.join(@rootDir, 'pictures.csv'))
  initialize_database
end

Public Instance Methods

change_image_name_in_mediawiki(mediawiki_file, old_image_name, new_image_name) click to toggle source

Helper for scripts We assume that the referenced new_image_name is living in the same directory as the mediawiki_file

# File lib/elexis/wiki/images.rb, line 319
def change_image_name_in_mediawiki(mediawiki_file, old_image_name, new_image_name)
  savedDir = Dir.pwd
  Dir.chdir(File.dirname(mediawiki_file))
  if new_image_name.downcase != File.basename(new_image_name).downcase
    raise "new_image_name #{new_image_name} may not contain a directory path"
  end
  if @add_changes_to_git
    run_git_cmd("git add #{old_image_name} && git mv #{old_image_name} #{new_image_name}")
  elsif @actions
    @actions << "mv #{old_image_name} #{new_image_name}"
    FileUtils.mv(old_image_name, new_image_name, :verbose => true)
  end unless File.exists?(new_image_name)
  puts "change_image_name_in_mediawiki #{Dir.pwd}/#{mediawiki_file}: #{old_image_name.inspect} => #{new_image_name.inspect}" if $VERBOSE
  newLines = []
  lines = IO.readlines(mediawiki_file)
  lines.each{
    |line|
    image_name = nil
    if m = ImageWithSlashPattern.match(line)
      image_name = m[2] + m[3]
    elsif m = ImagePattern.match(line)
      image_name = m[2]
    end
    unless image_name
      newLines << fix_line(line)
      next;
    end
    if old_image_name.downcase.eql?(image_name.downcase) or/\/#{old_image_name}/i.match(image_name)
        dirName = File.dirname(mediawiki_file)
        simpleName = File.join(dirName, File.basename(new_image_name))
        raise "Could not find image" unless File.exists?(new_image_name)
        if files = Dir.glob(simpleName, File::FNM_CASEFOLD) and files.size >= 1
          newLines << fix_line(line, old_image_name, new_image_name)
        else
          newLines << fix_line(line)
        end
    else
      newLines << fix_line(line)
    end
  }
  File.open(mediawiki_file, "w") {|f| f.write newLines.join.gsub(/\[\[Datei:|\[\[Image:/i, '[[File:')}
  run_git_cmd("git add #{mediawiki_file}")
ensure Dir.chdir(savedDir)
end
cleanup_mediawikis() click to toggle source
# File lib/elexis/wiki/images.rb, line 402
def cleanup_mediawikis
  savedDir = Dir.pwd
  @docs.each do
    |docDir|
    Dir.chdir(File.join(@rootDir, docDir))
      wikis = Dir.glob('*.mediawiki')
      wikis.each{
        |file|
        output = ''
        lines = IO.readlines(file)
        lines.each{|line| output << fix_line(line) }
        File.open(file, 'w+') { |f| f.write(output) }
      }
  end
  ensure
    Dir.chdir(savedDir)
end
determine_cleanup() click to toggle source
# File lib/elexis/wiki/images.rb, line 217
def determine_cleanup
  savedDir = Dir.pwd
  Dir.chdir(@rootDir)
  @pictures.each{ |picture| set_best_name(picture) }
  @pictures.each{ |picture| verify_best_name(picture) }
  @wrong_best_name = []
  @pictures.each{ |picture| verify_best_name(picture) }
  @dup_non_identical = @pictures.collect{ |p| p[:path] if p[:rule] =~ /multiple/}.compact
  @pictures.find{ |outer| @pictures.find_all{ |inner| inner[:name].eql?(outer[:name]) and not inner[:sha256].eql?(outer[:sha256])}.size > 1 }
  @new_best_name     = @pictures.find_all{ |picture| picture[:best_name] and not picture[:best_name].downcase.eql?(picture[:name].downcase) }
  write_yml_and_csv
ensure
  Dir.chdir(savedDir)
end
execute_cleanup(add_changes_to_git=false) click to toggle source
# File lib/elexis/wiki/images.rb, line 261
def execute_cleanup(add_changes_to_git=false)
  @add_changes_to_git = add_changes_to_git
  @actions ||= []
  savedDir = Dir.pwd
  remove_obsolete_symlinks
  Dir.chdir(@rootDir)
  cmds = []
  @new_best_name.each{
    |picture|
    dir_name = File.dirname(picture[:path])
    Dir.chdir(File.join(@rootDir, dir_name))
    Dir.glob("*#{extension}").each  do
      |wiki_file|
    old_image_name = picture[:name]
    new_image_name = picture[:best_name].downcase
    if old_image_name.index('-')
      puts "Skipping mv #{old_image_name} #{new_image_name}"
      next
    end
    cmd = "change_image_name_in_mediawiki #{wiki_file} #{picture[:name]} #{picture[:best_name]}"
    # cmd = 'change_image_name_in_mediawiki test.mediawiki ch.elexis.icpc_icpc1.png icpc1.png'
    change_image_name_in_mediawiki wiki_file, old_image_name, new_image_name
    @actions  << cmd
    oldFiles = Dir.glob(old_image_name, File::FNM_CASEFOLD)
    oldFiles.each{ |old_file|
                  if @add_changes_to_git
                    run_git_cmd("git rm -f --ignore-unmatch #{old_file}") if File.exist?(old_file)
                  else
                    FileUtils.rm_f(old_file, :verbose => true)
                    @actions  << "rm -f #{old_file}"
                  end
                 }
  end
  }

  # change_image_name_in_mediawiki
  Dir.chdir(@rootDir)
  # Add all pictures which were downloaded and still have the same name
  @pictures.each { |picture|
                    next unless picture[:best_name]
                    dir = File.dirname(picture[:path])
                    dir ||= '.'
                    new_path = File.join(dir, picture[:best_name])
                    next unless File.exists?(picture[:path])
                    FileUtils.cp(picture[:path], new_path, :verbose => true)  unless File.exists?(new_path)
                    run_git_cmd("git add -f #{new_path}")
                    run_git_cmd("git rm -f --ignore-unmatch #{picture[:path]}", true) if picture[:path] != new_path
                   }
  @actions.uniq!
  cleanup_mediawikis
  # puts @actions.join("\n")
ensure
  Dir.chdir(savedDir)
end
fix_line(line, old_file=nil, new_file=nil) click to toggle source
# File lib/elexis/wiki/images.rb, line 363
def fix_line(line, old_file=nil, new_file=nil)
  line.gsub!(/(<U\+\h\h\h\h>)/,'') # remove pseudo UTF-8
  old_ref = 'unknown'
  unless old_file
    image_name = nil
    if m = ImageWithSlashPattern.match(line)
      old_file = m[2] + m[3]
      old_ref = m[3].downcase
    elsif m = ImagePattern.match(line)
      old_file = m[2]
      old_ref = m[2].downcase
    end
    return line unless m
  end
  new_file ||= Images.get_small_name(old_file)
  proj = Images.get_project_abbreviation(Dir.pwd)
  change_to = nil
  if File.exists?(new_file)
    change_to ||= new_file
  else
    [ old_ref,
      old_file.sub(/^#{proj}[_]/i, proj+'-'),
      new_file.sub(/^#{proj}/i, ''),
      proj + '-' + new_file.sub(/^#{proj}/i, ''),
      new_file.sub(/^#{proj}-#{proj}/i, proj + '-'),
      new_file.sub(/^#{proj}-(.*)/i, proj + '\1'),
      (proj + '-' + new_file).downcase,
      ].each{
        |try_this|
        if File.exists?(try_this.downcase)
          change_to ||= try_this.downcase
          break
        end
      }
  end
  return line unless change_to
  line.sub(old_file, change_to)
end
get_name_with_project(path) click to toggle source
# File lib/elexis/wiki/images.rb, line 190
def get_name_with_project(path)
  prefix =Images.get_project_abbreviation(path)
  basename = File.basename(path)
  result = /^#{prefix}/i.match(basename) ? basename : (prefix + '-' + basename).gsub(':', '-')
  result.downcase
end
run_git_cmd(git_cmd, may_fail=false) click to toggle source
# File lib/elexis/wiki/images.rb, line 252
def run_git_cmd(git_cmd, may_fail=false)
  return unless @add_changes_to_git
  res = system(git_cmd)
  unless res
    raise "Running #{git_cmd} failed"
  end unless may_fail
  @actions  << git_cmd
end

Private Instance Methods

initialize_database() click to toggle source
# File lib/elexis/wiki/images.rb, line 49
def initialize_database
  savedDir = Dir.pwd
  Dir.chdir(@rootDir)
  @docs = Dir.glob('**/doc').reject{|x| x.match(/(^vendor|\/vendor)\//) } # doc_fr is mostly a duplicate of doc_de
  @docs.each{
    |doc_dir|
    pngs = Dir.glob("#{doc_dir}/**/*.{png,jpg}", File::FNM_CASEFOLD)
    puts "#{doc_dir} has #{pngs.size} png" if $VERBOSE
    next if pngs.size == 0
    pngs.each {
              |png|
              if File.size(png) == 0
                FileUtils.rm_f(png, :verbose => true)
                next
              end
              sha256 = Digest::SHA256.hexdigest(IO.read(png))
              pict = { :sha256 => sha256,
                       :name => File.basename(png),
                       :project => File.basename(File.dirname(File.dirname(png))),
                       :path => png}
              @pictures << pict
              }
    # break if pngs.size > 1
  }
  @sha_2_png = {}
  @pictures.uniq! { |elem| elem[:path].downcase }
  @pictures.each {
    |picture|
    if @sha_2_png[picture[:sha256]]
      @sha_2_png[picture[:sha256]] << picture
    else
      @sha_2_png[picture[:sha256]] = [picture]
    end
  }
  @duplicates        = @sha_2_png.find_all{|sha, list| list.size > 1}
  write_yml_and_csv
ensure
  Dir.chdir(savedDir)
end
set_best_name(picture) click to toggle source
# File lib/elexis/wiki/images.rb, line 115
def set_best_name(picture)
  debug = $VERBOSE
  path = picture[:path]
  sha256 = Digest::SHA256.hexdigest(IO.read(path))
  same_sha256 = @pictures.find_all{|x| x[:sha256] == sha256}
  same_name   = @pictures.find_all{|x| x[:name] == picture[:name]}
  nrSha256 = same_name.collect{|c| Images.get_small_name(c[:sha256])}.uniq
  if same_sha256.size == 1 and nrSha256.size == 1
    puts "new_best_name 3 @short_and_sha_okay #{sha256} for #{path}" if debug
    picture[:rule] = 'short_and_sha_okay nr_single_sha'
    @short_and_sha_okay << path
    @nr_single_sha << same_sha256
    picture[:best_name] = Images.get_small_name(path)
    return
  else
    msg = " nrSha #{sha256.size} #{sha256}"
    if same_sha256.collect{|c| c[:name]}.uniq.size == 1 and nrSha256.size == 1
      puts "@short_and_sha_okay #{path} sha256 #{sha256} for #{same_sha256.size} files" if debug
      picture[:rule] = 'short_and_sha_okay'
      @short_and_sha_okay << path
      return
    end
    found = same_sha256.collect{|c| c[:name].downcase}.uniq
    if found.size == 1  and nrSha256.size == 1
      puts "new_best_name 4 case_sensitives #{found}" if debug
      picture[:rule] = 'case_sensitives'
      picture[:best_name] = Images.get_small_name(path)
      return
    end
    multiple = same_sha256.collect{|c| c[:name]}.uniq
    to_reduce = same_sha256.collect{|c| Images.get_small_name(c[:name])}.uniq
    if nrSha256.size == 1
      @to_short_names[path] = to_reduce
      puts "new_best_name 5 to_short_names #{multiple} =>  #{to_reduce} nrSha #{sha256.size} #{sha256}" if debug
      picture[:rule] = 'to_short_names'
      picture[:best_name] = Images.get_small_name(path)
      return
    end
    puts "new_best_name  6 multiple #{multiple} to_reduce #{to_reduce} #{get_name_with_project(picture[:path])}" if debug
    @multiple_names[path] = same_sha256
    picture[:best_name] =  get_name_with_project(picture[:path])
    picture[:rule] = 'multiple_names'
    return
  end
end
verify_best_name(picture) click to toggle source
# File lib/elexis/wiki/images.rb, line 89
def verify_best_name(picture)
  if picture[:best_name]
    wrong_best_name = @pictures.find_all{|x| x[:best_name] == picture[:best_name] and x[:sha256] != picture[:sha256]}
    if wrong_best_name.size > 0
      puts "   #{wrong_best_name.size} entries #{wrong_best_name}" #if $VERBOSE
      @wrong_best_name << [picture[:path], wrong_best_name.collect{|x| x[:path]} ]
      new_name = get_name_with_project(picture[:path])
      if picture[:best_name] != new_name
        puts "new_best_name 2 is #{new_name} for #{picture[:path]}" #if $VERBOSE
        picture[:rule] += " name get_name_with_project"
        picture[:best_name] = new_name
      else
        @@uniq_id ||= 0
        new_name = '27'
        begin
          @@uniq_id += 1
          new_name = File.dirname(picture[:path]) + "/#{@@uniq_id}_" + picture[:name]
        end until File.exist?(new_name).eql?(false)
        puts "new_best_name 3 is #{new_name} for #{picture[:path]}" # if $VERBOSE
        picture[:rule] += " third #{new_name}"
        picture[:best_name] = File.basename(new_name)
      end
    end
  end
end
write_yml_and_csv() click to toggle source
# File lib/elexis/wiki/images.rb, line 38
def write_yml_and_csv
  File.open(@yml, 'w+') {|f| f.puts @pictures.to_yaml}
  column_header = ["sha256","name","project","path"]
  CSV.open(csv, 'w+',
           :write_headers=> true,
           :headers => column_header
          ) do |csv|
        csv << ["# Generated by: #{File.basename(__FILE__)} at #{Time.now.utc}"]
        @pictures.each{ |pict| csv << pict.values.to_a  }
  end
end