Tecnologias Utilizadas

Principais tecnologias do projeto apresentadas com trechos de código para ilustrar as implementações realizadas.

Python

A codificação deste projeto utiliza essa linguagem pois é a linguagem mais legal que os desenvolvedores conhecem, por ser a linguagem mais fácil de fazer um Hello Word (print "Hello Word") e também por ser a linguagem do framework Django, escolhido por facilitar a gestão das fontes e prover uma excelente arquitetura para sistemas web.

Além disso, esta adoção facilitará a integração do sistema desenvolvido com os em desenvolvimento no Interlegis (principal colaborador deste projeto).

Todos os logs do sistema são feito através do módulo nativo Python chamado logging, módulo configurável por meio do arquivo logger.conf. Os níveis de criticidade dos logs são determinados no momento de armazenar um registro de log. Por exemplo:

import logging
log = logging.getLogger('metabus')

log.error('Message here!')

2008-10-21 11:11:30 [5854] ERROR Message here!

Django

Django é o framework Python adotado como base para o desenvolvimento do Metabus. Este software além proporcionar recursos para um desenvolvimento ágil de aplicações Web, ainda sugere uma organização elegante do código fonte.

A separação de interesses sugerida nesse framework possui uma nomenclatura sutilmente diferente da nomenclatura comumente adotada por vários frameworks de aplicações Web. Ao invés do conhecido MVC (Model View and Controller) utiliza-se MTV (Model Template and View) assim o elemento 'View' do MVC chama-se 'Template' no MTV e o 'Controller' do MVC chama-se 'View' no MTV.

Processamento de requisições Web

As requisições Web são mapeadas para uma 'View' através do arquivo urls.py. Nesse arquivo podemos configurar expressões regulares que identificam cada uma das urls e associam a uma determinada função de callback (View). O código abaixo é um trecho do arquivo urls.py do módulo Interface Gráfica do Metabus e ilustra como é feita a associação de uma URL de requisição Web a uma View:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    url (r'^$', 'metabus.ui.views.home', name='home'),
    url (r'^search/$', 'metabus.ui.views.search', name='search'),
) 

Com esse código é feita a associação da url http://metabus.aquarela3.com.br/search/ para a View metabus.ui.views.search e da http://metabus.aquarela3.com.br (raiz do site) para metabus.ui.views.home. Esta função será a responsável por processar o dados necessários para montar a resposta á requisição.

Camada para acesso aos dados

O Django oferece um conjunto de classes para acesso e manipulação dos dados contidos na base de dados. Esta camada é responsável também por tornar o sistema independente do Sistema de Gerenciamento de Banco de Bados - SGBD, desta forma, pode ser utilizado qualquer banco do dados que seja suportado pelo Django (Postgres, MySQL, SQLite3 e Oracle) e as tarefas de conexão, criação das tabelas, obtenção de dados, inserções e alterações são todas efetuadas através dessa camada de um maneira única independente do SGBD utilizado.

Abaixo segue um trecho do arquivo models.py onde são definidas as entidade relacionadas com as fontes de busca:

class Parser(models.Model):

    LANGUAGES = (
        ('xslt', 'XSLT'),
    )o

    name = models.CharField(_('Name'), help_text=_('Name of parser'), max_length=250)
    description = models.TextField(_('Description'), help_text=_('Parser description and documentation'), max_length=1024, blank=True)
    language = models.CharField(_('Language'), help_text=_('Parser file language'), max_length=250, choices=LANGUAGES, default='xslt')
    file = models.FileField(_('File'), help_text=_('Parser file'), upload_to = 'parser/%Y/%M')

class Group(models.Model):
    name = models.CharField(_('Name'), help_text=_('Group name'), max_length=250)
    parent = models.ForeignKey('self', help_text=_('Parent group'), null=True, blank=True)


class Source(models.Model):

    name = models.CharField(_('Name'), help_text=_('Name of search engine'), max_length=250)
    url = models.CharField(_('Search URL'), help_text=_('URL of search engine'), unique=True, max_length=250)
    is_rss = models.BooleanField(_('Is RSS'), help_text=_('Mark it if the search engine return RSS data'), default=True)
    op_and = models.CharField(_('AND'), help_text=_('AND operator string to this search'), max_length=250, default='AND')
    op_or = models.CharField(_('OR'), help_text=_('OR operator string to this search'), max_length=250, default='OR')
    cache_expires = models.IntegerField(_('Cache expires'), help_text=_('Seconds time to exipires cache results'), default=60*60*24) # Default one day
    parser = models.ForeignKey(Parser, help_text=_('Parser file to transform the response in RSS'), null=True, blank=True)
    group = models.ManyToManyField(Group, help_text=_('Group of this source'), null=True, blank=True)

Com código acima o Django provê a criação de tabelas, e o total controle sobre os dados.

Estrutura de templates

Os templates do Django tem como principal objetivo gerar um arquivo baseado em texto formatado, por exemplo HTML.

Os arquivos de template contêm variáveis, quando o template é processado essas variáveis são substituídas por valores. Além de variáveis os arquivos contêm tags que controlam a lógica do template. A estrutura de templates pode ser organizada com herança de arquivos, que possibilita criar templates base com as definições de elementos comuns a todos os documentos do site, assim como blocos que permitem que outros arquivos sobrescrevam o seu conteúdo como pode ser observado no template base do metabus, apresentado no código abaixo:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-us" xml:lang="en-us">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   
    {# Block title should be overwrite #} 
    <title>{% block title %}Metabus - Interlegis{% endblock %}</title>

    {# Base stylesheet could be overwrite, however the new css have to import the base stylesheet #} 
    <link type="text/css" rel="stylesheet" href="{{MEDIA_URL}}css/{% block stylesheet %}base.css{% endblock %}" />

    {# Jquery Lib #}
    <script type="text/javascript" src="{{MEDIA_URL}}js/jquery-1.2.6.js"></script>

    {# Base js definition#}
    <script type="text/javascript" src="{{MEDIA_URL}}js/base.js"></script>

    {# Template js definition. Should be overwrite to import a new js file#}
    <script type="text/javascript" src="{{MEDIA_URL}}js/{% block javascript %}blank.js{% endblock %}"></script>
</head>

<body>
    <div id="container_dialog">
        <p></p>
    </div>

    <div id="container_top">
        <div id="container_bottom">
            <div id="container_body">
    
                <div id="top">
                    ...
                </div>

                <hr class="clear"/>
    
                <div id="header">
                    <img width="276" height="110" src="{{MEDIA_URL}}img/logo.gif" alt="Metabus"/>
                    <form id="search-form" method="GET" action="{% url search %}">
                        <fieldset>
                            <input type="text" id="keywords" name="keywords"/>
                            <input type="submit" value="search"/>
                        </fieldset>
                        <fieldset id="sources">
                            <ul>
                                {% for group in groups %}
                                <li>
                                    <input class="groups" id="{{ group.name }}" type="checkbox" value="{{ group.id }}" name="groups"/>
                                    <label class="groups" for="{{ group.name }}">{{ group.name }}</label>
                                    <fieldset class="groups">
                                        <ul>
                                            {% for source in group.sources %}
                                            <li>
                                                <input id="{{ source.name }}" type="checkbox" value="{{ source.id }}" name="sources"/>
                                                <label for="{{ source.name }}">{{ source.name }}</label>
                                            </li>
                                            {% endfor %}
                                        </ul>
                                    </fieldset>
                                </li>
                                {% endfor %}
                                <li>
                                    ...
                                </li>
                            </ul>
                        </fieldset>
                    </form>
                </div>

                <div id="content">
                    {% block content %}
                    {% endblock %}
                </div>
                
                <hr class="clear"/>
    
                <div id="footer">
                    ...
                </div>
            </div>
        </div>
    </div>
</body>
</html>

Interface administrativa automática

A interface de administração do metabus é provida pelo Django que cria um site de administração a partir do modelo da base de dados, disponibilizando uma interface unificada para modificar o conteúdo da base de dados do sistema.

O arquivo admin.py contém todas as configurações que controlam como essas tabelas aparecem no painel administrativo. A interface de administração não tem a intenção de ser usada pelos visitantes do site, mas sim pelos administradores que irão adicionar e manter as fontes de busca do sistema.

O arquivo que define as configurações da interface administrativa automática é apresentado abaixo:

from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
from metabus.source.models import Source, Parser, Group

class ParserAdmin(admin.ModelAdmin):
    fieldsets = (
        (None, {'fields': ('name', 'description',)}),
        (_('File'), {'fields': ('language', 'file')}),
    )    
    verbose_name = _('Parser')

class SourceAdmin(admin.ModelAdmin):
    fieldsets = (
        (None, {'fields': ('name', 'url','cache_expires')}),
        (_('Operators'), {'classes': ['collapse'], 'fields': ('op_and', 'op_or',)}),
        (_('Parser'), {'fields': ('is_rss', 'parser')}),
        (_('Groups'), {'fields': ('group',)})
    )
    verbose_name = _('Source')

class GroupAdmin(admin.ModelAdmin):
    verbose_name = _('Group')

admin.site.register(Parser, ParserAdmin)
admin.site.register(Source, SourceAdmin)
admin.site.register(Group, GroupAdmin)

Fabric

Fabric é uma ferramenta Python que auxilia a instalação e configuração de sistemas em servidores. Essa ferramenta é capaz de fazer upload de arquivos e executar comandos em servidores a fim de instalar um sistema remotamente em um ou diversos servidores.

Os comandos de instalação são organizados em funções python dentro do arquivo 'fabfile.py' Este arquivo possui a seguinte estrutura:

set(
    project = 'mbclient',
    project_log_path = '/home/metabus/var/log/$(project)',
    (...)

    fab_user    = 'metabus',
    fab_hosts   = ['127.0.0.1'],
 
    (...)
)
 
def deploy():
    "Deploy the project to the production server."
    (...)
 
def build():
    "Build the project. Prepare the packages to deploy."
    (...)
 
def clean():
    "Remove the build directory."
    (...)

Cada método definido neste arquivo é uma etapa da instalação. No início uma tupla 'set' permite configurar as variáveis necessárias para o script. Veja que, nesta tupla, indicamos na variável fab_hosts os servidores nos quais os comandos remotos devem ser executados. E na variável fab_user, o usuário que executará esses comandos nos servidores. Pode-se também criar variavéis próprias e inseri-las na tupla 'set'(como a variável project_log_path).

Além de poder utilizar todos os comandos python esta ferramenta fornece quatro principais métodos, são eles:

local: Executa comandos na máquina local com o usuário que roda o script. Ex:

local('mkdir -p $(build_path)/$(project)/')

run: Mesma funcionalidade do local, entretanto executa os comandos nas máquinas remotas (indicadas na variável fab_hosts) com o usuário especificado na variável fab_user. Ex:

run('cd $(destination)/; tar xf $(project_build); rm $(project_build)')

sudo: Igual ao run, porém executa o comando remotamente como super-usuário Ex:

sudo('/etc/init.d/apache2 restart')

put: Envia um arquivo para as máquinas remotas. Ex:

put('$(build_path)/$(project_build)', '$(destination)/$(project_build)')

Quando uma variável é utilizada dentro de um comando, isso significa que o Fabric irá parsear a string, para só então executar tal comando.

O principal motivo pelo qual decidiu-se pelo uso dessa ferramenta é organizar os arquivos dos projetos para que estes fiquem dispostos da mesma forma utilizada pelo servidor de produção, enviando os arquivos dentro de um pacote tar.gz para os servidores, descompactando e movendo para o local correto, além de reiniciar o servidor web para recarregar o projeto.

Última modificação 5 anos atrás Última modificação em 21/03/2013 16:27:48
 

The contents and data of this website are published under license:
Creative Commons 4.0 Brasil - Atribuir Fonte - Compartilhar Igual.