Arch Linux Brasil Repo Web View
Comecei a trabalhar numa idéia de criar uma sequência de scripts, que gerariam uma espécie de relatório, em html, das atividades no repositório Arch Linux Brasil.
A idéia surgiu quando eu resolvi ver o que existia no Repo Brasil, e sinceramente me incomodou ficar vendo as pastas em ftp. Porque não estão criar uma página com esse conteúdo?
Sem a pretensão de criar algo grande, comecei a trabalhar. E o resultado, até agora, é esse: http://danielbalieiro.archlinux-br.org/
Não sei se vou terminar. Sinceramente já me dou por satisfeito
. MASSSS resolvi colar aqui o código, para caso alguem queira brincar com isso.
O código usa as gem´s:
libarchive_ruby
sqlite3-ruby
init.rb:
require "downloader.rb"
require "archiver.rb"
require "classes.rb"
require "database.rb"
require "htmlmaker.rb"
# Chama o Downloader.
# Retonar um Hash contendo:
# key: repositório
# value: caminho do arquivo no disco (relativo)
def download
downloader = Downloader.new("http://repo.archlinux-br.org/", ["i686", "x86_64"], "archlinuxbr.db.tar.gz")
downloader.start
end
#
# Extrai os dados dos pacotes do arquivo do repositório
# Recebe:
# key: repositorio
# value: caminho do arquivo no disco (relativo)
#
def extract(key, value)
puts("\nIniciando arquivador para o respositório: #{key}")
archiver = Archiver.new(key, value)
archiver.start
end
# Insere os repositórios e pacotes no banco de dados
def put_database(packages_by_repository)
puts("\nAlimentando banco de dados...")
db_name = "archlinux.db"
d = Database.new(db_name)
d.write(packages_by_repository)
d.close
puts("Banco de dados pronto!")
db_name
end
def generate_html(db_name)
puts("\nGerando relatório em HTML")
html = HtmlMaker.new(db_name)
html.start
puts("Relatório HTML gerado!");
end
# Método principal, efetua a chamada dos demais metódos
def execute
# hash com os pacotes por repositorio.
# Exemplo:
# key = repo.archlinux-br.org-i686
# value: array de Package.class
packages_by_repository = {}
begin
download.each do |key, value|
packages_by_repository[key] = extract(key, value)
end
db_name = put_database packages_by_repository
generate_html db_name
rescue Exception => e
puts("\n!! ERRO: #{e} (#{e.class})!")
puts("!! Detalhes: ")
puts("\t"+e.backtrace.join("\n\t"))
end
end
# Chamada inicial
execute
htmlmaker.rb:
require 'ftools'
require 'iconv'
class HtmlMaker
def initialize(db_name)
@dist_dir = 'C:\\java\\Apache2.2\\htdocs'
@db = Database.new(db_name)
@repositories = @db.find_repositories
end
def start
index
@db.close
end
def index
cd = Iconv.new('utf-8', 'iso-8859-1')
File.open(@dist_dir + File::SEPARATOR + 'index.html', 'w') do |file|
file.puts(cd.iconv('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1-strict.dtd ">'))
file.puts(cd.iconv('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'))
file.puts(cd.iconv(head))
file.puts(cd.iconv(body))
file.puts(cd.iconv('</html>'))
end
end
def body
body = '<body>'
body += head_container
body += content
body += foot
body += '</body>'
end
def foot
ft = '<div class="foot">'
ft += ' Copyright &copy; 2002-2008 <a href="mailto:jvinet@zeroflux.org" title="contact Judd Vinet">Judd Vinet</a> e <a href="mailto:aaron@archlinux.org" title="contact Aaron Griffin">Aaron Griffin</a>.<br />'
ft += ' O nome e o logo Arch Linux são marcas reconhecidas. Alguns direitos reservados.'
ft += '</div>'
ft
end
def content
content = ' <div id="content"> '
content += content_right
content += content_left
content += ' </div> '
content
end
def content_left
cl = ' <div class="left">'
cl += ' <div id="about" class="box">'
cl += ' <h2>Relatório de Atividades no Repositório!</h2>'
cl += ' <p>O objetivo desse projeto é prover ao usuário do repositório do Arch Linux do Brasil, uma forma simples, rápida e clara de visualizar as modificações no repositório nacional.</p>'
cl += ' </div>'
cl += ' <br /><br />'
cl += ' <div style="float:right;position:relative;bottom:-25px"></div>'
cl += ' '
@db.find_repositories.each do |repo|
cl += ' <h2 class="title">Últimas Atualizações no Repositório: '+repo.arch+'</h2>'
cl += ' <div>'
cl += ' <table width="100%">'
cl += ' <tr>'
cl += ' <th align="left">Nome</td>'
cl += ' <th align="left">Versão</td>'
cl += ' <th align="right">Data</td>'
cl += ' </tr>'
@db.last_packages_by_repository(repo.id).each do |package|
cl += ' <tr>'
cl += ' <td align="left">'+package.name+'</td>'
cl += ' <td align="left">'+package.version+'</td>'
cl += ' <td align="right">'+package.date.strftime("%d / %m / %Y")+'</td>'
cl += ' </tr>'
end
cl += ' </table> '
cl += ' <br /><br />'
cl += ' </div>'
end
cl += '</div> '
cl
end
def content_right
cr = ' <div class="right"> '
cr += ' <div id="updates">'
cr += ' <table width="100%">'
cr += ' <tr>'
cr += ' <td><h3>Atualizações Recentes</h3></td>'
cr += ' <td style="vertical-align:top;text-align:right">'
cr += ' <a href="/feeds/packages.xml">'
cr += ' <img src="/media/rss.png" alt="RSS Feed" />'
cr += ' </a>'
cr += ' </td>'
cr += ' </tr>'
@db.last_packages.each do |package|
cr += ' <tr>'
cr += ' <td>'
cr += ' <a href="'+package.url+'">'
cr += package.name
cr += ' </a>'
cr += ' </td>'
cr += ' <td>'+package.arch+'</td>'
cr += ' </tr>'
end
cr += ' </table>'
cr += ' </div>'
cr += ' </div> '
cr
end
def head_container
hc = '<div id="head_container"> '
hc += ' <div id="title">'
hc += ' <div id="logo">'
hc += ' <h1 id="archtitle">'
hc += ' <a href="/" title="Arch Linux (Home)">Arch Linux</a>'
hc += ' </h1>'
hc += ' </div>'
hc += ' </div>'
hc += ' <div id="main_nav">'
hc += ' <ul>'
hc += ' <li><a href="http://archlinux-br.org">Arch Linux Brasil</a></li>'
hc += ' <li><a href="http://github.com">Código Fonte</a></li>'
hc += ' <li class="selected"><a href="/">Site</a></li>'
hc += ' </ul>'
hc += ' </div>'
hc += ' <div id="brdmenu" class="inbox">'
hc += ' Página gerada em: '+Time.now.strftime("%d / %m / %Y")
hc += ' </div>'
hc += '</div>'
hc
end
def head
head = ' <head> '
head += ' <title>Arch Linux Brasil - Relatório de Atividades no Repositório</title>'
head += ' <meta http-equiv="content-type" content="text/html; charset=utf-8" />'
head += ' <link rel="stylesheet" href="/media/default.css" />'
head += ' <link rel="icon" href="/media/favicon.ico" type="image/x-icon" />'
head += ' <link rel="shortcut icon" href="/media/favicon.ico" type="image/x-icon" />'
head += ' <link rel="alternate" type="application/rss+xml" title="Arch Linux Brasil - Atualizações de Pacotes" href="/feeds/packages.xml" />'
head += ' </head> '
head
end
end
downloader.rb:
require 'net/http'
require 'ftools'
#
# Classe responsável por efetuar o download do arquivo "db" do repositório
#
class Downloader
# Construtor
# Parametros:
# url: caminho do repositório
# repo: tipo de repositório (exemplo: i686)
# file_name: nome do arquivo que vai ser feito o download
def initialize(url, repo, file_name)
@url = URI.parse(url)
@file_name = file_name
@repo = repo
end
# Método principal de negócio, chamado pelo init.rb
def start
fileahash = {}
puts("Iniciando download... ")
puts("\tHost: " + @url.host.to_s)
puts("\tNome do arquivo: " + @file_name)
@repo.each do |repo|
puts("\tRepositório: " + repo)
Net::HTTP.start(@url.host) do |http|
resp = http.get("/" + repo + "/" + @file_name)
file_path = real_path(@url.host.to_s, repo) + File::SEPARATOR + @file_name
fileahash[@url.host.to_s + '/' + repo] = file_path
open(file_path, "wb") do |file|
file.write(resp.body)
end
end
end
puts("Download concluído!")
fileahash
end
# Método para criar as pastas necessárias à organização do download
def real_path(host, repo)
d = 'download' + File::SEPARATOR + host + File::SEPARATOR + repo + File::SEPARATOR
if ! File.exist? d
File.makedirs d
end
d
end
end
database.rb:
require 'sqlite3'
require 'ftools'
class Database
def initialize(db_name)
@db = nil
if ! File.exist? db_name
@db = SQLite3::Database.new(db_name)
create_tables.each do |sql|
@db.execute(sql)
end
end
@db = SQLite3::Database.new(db_name) if @db == nil
end
def close
@db.close
end
def insert_repository(repo)
id = 0
host = repo.split('/')[0]
arch = repo.split('/')[1]
r = @db.execute("select * from repositories where host = '#{host}' and arch = '#{arch}';")
if r.empty?
id = generate_index('repositories')
sql = "insert into repositories(id, description, host, arch) "
sql += " values (#{id}, '#{repo}', '#{host}', '#{arch}' );"
@db.execute(sql)
elsif r != nil
r.each do |row|
id = row[0]
end
end
id.to_i
end
def insert_package(package, repository_id)
sql = " select * from packages where name = '#{package.name}' "
sql += " and version = '#{package.version}' "
sql += " and arch = '#{package.arch}' "
r = @db.execute(sql)
if r.empty?
filename = ''
filename = package.filename.tr("'", '') if ! package.filename.nil?
name = ''
name = package.name.tr("'", '') if ! package.name.nil?
desc = ''
desc = package.desc.tr("'", '') if ! package.desc.nil?
packager = ''
packager = package.packager.tr("'", '') if ! package.packager.nil?
url = ''
url = package.url.tr("'", '') if ! package.url.nil?
id = generate_index('packages')
sql = "insert into packages ( "
sql += " id, repository_id, filename, name, version, desc, md5sum, "
sql += " url, arch, builddate, packager, repository"
sql += " ) "
sql += " values ( "
sql += " #{id}, #{repository_id}, '#{filename}', '#{name}', "
sql += " '#{package.version}', '#{desc}', '#{package.md5sum}', "
sql += " '#{url}', '#{package.arch}', '#{package.date}', "
sql += " '#{packager}', '#{package.repository}' "
sql += " ) ;"
@db.execute(sql)
end
end
def write(packages_by_repository)
packages_by_repository.each do |key, values|
puts("\n\tAlimentando Repositório: #{key}")
repository_id = insert_repository(key)
values.each do |package|
puts("\t\tAlimentando pacote: #{package.name}")
insert_package(package, repository_id)
end
end
end
def generate_index(table)
index = 0
@db.execute("select max(id) from #{table};") do |row|
index = row[0]
end
return index.to_i + 1
end
def find_repositories
repos = []
sql = 'select id, description, host, arch from repositories order by host;'
@db.execute(sql) do |row|
repo = Repository.new
repo.id = row[0]
repo.description = row[1]
repo.host = row[2]
repo.arch = row[3]
repos << repo
end
repos
end
def row_to_package(row)
p = Package.new
p.id = row[0]
p.filename = row[1]
p.name = row[2]
p.version = row[3]
p.desc = row[4]
p.md5sum = row[5]
p.url = row[6]
p.arch = row[7]
p.date = Time.at(row[8].to_i)
# p.date = Time.parse(row[8])
p.packager = row[9]
p.repository = row[10]
p
end
def last_packages_by_repository(repository_id)
packages = []
sql = 'select id, '
sql += 'filename, '
sql += 'name, '
sql += 'version, '
sql += 'desc, '
sql += 'md5sum, '
sql += 'url, '
sql += 'arch, '
sql += 'builddate, '
sql += 'packager, '
sql += 'repository '
sql += ' from packages '
sql += ' where repository_id = '+repository_id
sql += ' order by builddate desc '
sql += ' limit 0, 5; '
@db.execute(sql) do |row|
packages << row_to_package(row)
end
packages
end
def last_packages
packages = []
sql = 'select id, '
sql += 'filename, '
sql += 'name, '
sql += 'version, '
sql += 'desc, '
sql += 'md5sum, '
sql += 'url, '
sql += 'arch, '
sql += 'builddate, '
sql += 'packager, '
sql += 'repository '
sql += ' from packages '
sql += ' order by builddate desc '
sql += ' limit 0, 28; '
@db.execute(sql) do |row|
packages << row_to_package(row)
end
packages
end
def create_tables
sqls = []
sql = ' create table repositories ( '
sql += ' id int(10) constraint pk_repositories primary key,'
sql += ' description varchar(100),'
sql += ' host varchar(100) not null, '
sql += ' arch varchar(10) not null'
sql += ' );'
sqls << sql
sql = ' create table packages ('
sql += ' id int(10) constraint pk_packages primary key,'
sql += ' repository_id int(10),'
sql += ' filename varchar(100),'
sql += ' name varchar(70),'
sql += ' version varchar(20),'
sql += ' desc varchar(1000),'
sql += ' md5sum varchar(100),'
sql += ' url varchar(100),'
sql += ' arch varchar(10),'
sql += ' builddate int(20),'
sql += ' packager varchar(100),'
sql += ' repository varchar(100) '
sql += ' );'
sqls << sql
sqls
end
end
classes.rb:
# Classe que representa uma Package
class Package
attr_accessor :filename, :name, :version, :desc, :md5sum, :url, :arch, :date, :packager, :repository, :id
def to_s
s = "Package: repository=(#{repository}) filename=(#{filename}) name=(#{name}) version =(#{version}) "
s += "desc=(#{desc}) md5sum=(#{md5sum}) url=(#{url}) arch=(#{arch}) "
s += "date=(#{date}) packager=(#{packager}) id=(#{id})"
end
end
# Classe que representa um Repositorio
class Repository
attr_accessor :id, :description, :host, :arch
def to_s
s = "Repository: id=(#{id}) description=(#{description}) host=(#{host}) arch=(#{arch})"
end
end
archiver.rb:
require 'libarchive_ruby'
require 'ftools'
require "classes.rb"
require 'time'
#
# Classe responsável por ler o arquivo "db" e gerar classes Package
#
class Archiver
# Construtor
# Parametros:
# key: repositorio
# value: arquivo a ser analisado
def initialize(key, value)
@file = value
@repo = key
end
# Converte as informações do arquivo 'desc', dentro do arquivo "db" e devolve array de string.
# Cada linha é uma entrada do array
def convert(data)
array = []
data.each_line do |line|
if ! line.lstrip.empty?
array << line[0...-1]
end
end
array << '%REPOSITORY%'
array << @repo
array
end
# Gera um "map" com as informações do arquivo a ser analisado.
# Retorno:
# key: nome do pacote
# value: dados em string do pacote (array)
def generate_map
map = {}
Archive.read_open_filename(@file) do |ar|
while entry = ar.next_header
name = entry.pathname
data = ar.read_data
if name[-5,5] == (File::SEPARATOR + 'desc')
puts("\tLendo pacote: #{name.split('/')[0]}")
map[name.split('/')[0]] = convert(data)
end
end
end
map
end
# Converte a string (array) de dados do pacote em um objeto Package
def converto_to_package(data_array)
p = Package.new
i = 0
data_array.each do |data|
i += 1
if data.include? '%FILENAME%'
p.filename = data_array[i]
elsif data.include? '%NAME%'
p.name = data_array[i]
elsif data.include? '%VERSION%'
p.version = data_array[i]
elsif data.include? '%DESC%'
p.desc = data_array[i]
elsif data.include? '%MD5SUM%'
p.md5sum = data_array[i]
elsif data.include? '%URL%'
p.url = data_array[i]
elsif data.include? '%ARCH%'
p.arch = data_array[i]
elsif data.include? '%BUILDDATE%'
p.date = data_array[i]
#Time.at(data_array[i].to_i)
elsif data.include? '%PACKAGER%'
p.packager = data_array[i]
elsif data.include? '%REPOSITORY%'
p.repository = data_array[i]
end
end
p
end
# Método principal da classe, efetua a chamada dos demais
def start
packages = []
generate_map.each do |key, value|
packages << converto_to_package(value)
end
puts("Arquivador concluído!")
packages
end
end
t+