Desenvolvimento de Relatórios Christopher Browne Desenvolvimento de Relatórios Os relatórios do GnuCash são gerados de uma forma que é bastante retrospectiva dos CGI da web; envolve: Escrever programas que gerem "janelas de opções". Ao contrário da aproximação CGI, as opções do GnuCash acabam por gerar directamente widgets GNOME, e não utilizam etiquetas de FORMs HTML. As semelhanças são, no entanto, bastante grandes já que ambos os sistemas de opções involvem: Construir uma lista de propriedades que descreve opções e valores de defeito; Submeter essa lista de propriedades para que o utilizador faça modificações; Retornar outra lista de propriedades contendo as modificações do utilzador. Escrever programas que obtenham dados da aplicação, baseados na lista de propriedades das opções, e depois gerem resultados em HTML. Muitas aplicações web produzem HTML de integridade questionável visto o programador gerar HTML na forma atabalhoada de apenas imprimir comandos que possam conter um tipo de dados com "pseudo-etiquetas". Por contraste, GnuCash utiliza uma estrutura de registos orientada para o HTML que torna mais difícil construir mau HTML do que o torna escrever HTML correcto. Esta apresentação irá assumir que o leitor já está de uma forma genérica familiarizado com o Scheme, particularmente com os seguintes conceitos: Ligar valores e funções utilizando define Como em: (define *value-x* 25) (define *value-y* 30) (define *string-a* "Aqui está uma string!") (define *string-b* "Uma string com uma aspa \" embebida") (define (add-1 x) (+ x 1)) Definir ligações locais utilizando let e let > (define let-to-hide-stuff (lambda (x y z) (let ((x 25);; Local binding of x inside the let (w 11)) (display "Dentro do let, x é:") (display x) (newline) (display "E w apenas existe dentro do let como ") (display w) (newline) (display "Adicionar x, y, z, w dentro do let:") (display (+ x y z w)) (newline)) (display "Agora, fora do let:") (newline) (display "x é:") (display x) (newline) (display "y é:") (display y) (newline) (display "z é:") (display z) (newline) (display "w é:") (display w) (newline))) > (let-to-hide-stuff 10 20 30) > (let-to-hide-stuff 10 20 30) Dentro do let, x é:25 E w apenas existe dentro do let como 11 Adicionar x, y, z, w dentro do let:86 Agora, fora do let: x é:10 y é:20 z é:30 reference to undefined identifier: w w é:> Definir listas utilizando list Como em: > (list 1 2 3 "four" 'five) (1 2 3 "four" five) > '(1 2 3 "four" five) (1 2 3 "four" five) Representar funções anónimas utilizando lambda A função add-1 mostrada anteriormente é na verdade definida como: (define add-1 (lambda (x) (+ x 1))) A estrutura de controlo if Utilizar map para aplicar uma função a todos os membros de uma lista, produzindo outra lista, como em: > (map (lambda (x) (+ x 1)) '(1 2 3 4)) (2 3 4 5) ou > (map (lambda (s n) (string-append s ":" (number->string n))) '("one" "two" "three" "four") '(1 2 3 4)) ("one:1" "two:2" "three:3" "four:4") Se os significados de alguns destes não são muito claros, veja as referências na secção sobre Documentação Scheme, e tente executar código como o acima utilizando Guile. Visão Geral de Relatórios Um relatório do GnuCash é adicionado ao sistena utilizando a função gnc:define-report, para a qual são passados os seguintes parâmetros: versão Este é um número de versão; actualmente não é utilizado para fazer nada particularmente inteligente. nome Este é um nome não-traduzido para o relatório que é notavelmente utilizado para disponibilizar o nome que será adicionado ao menu de relatórios do GnuCash . gerador de opções Este argumento é uma função que não leva parâmetros, e gera um conjunto de opções que serão utilizadas para apresentar uma janela que o utilizador pode utilizar para seleccionar valores para os parâmetros do relatório. Isto tipicamente irá incluir coisas como intrevalos de datas e listas de contas, para determinar que dados deverão ser seleccionados para o relatório, bem como outros controlos que determinam como os dados irão ser visualizados. visualizador O "visualizador" faz o trabalho de gerar o relatório. Aceita uma "base de dados" de opções, tal como a gerada pelo gerador de opções , obtem os dados correspondentes do motor do GnuCash, e gera, como resultado, um objecto do tipo documento html , que é o que o motor de relatórios do GnuCash sabe como apresentar. Um exemplo trivial poderá assemelhar-se ao seguinte: (let () (define (options-generator) (let ((options (gnc:new-options))) ;;; Criar nova lista de opções options)) ;;; Devolve-la (define (renderer options) (let ((document (gnc:make-html-document))) document)) ;;; Gera um documento vazio (gnc:define-report 'version 1 'name (N_ "Trivial Example") 'options-generator options-generator 'renderer renderer)) Note que os relatórios são tipicamente definidos dentro de um ambiente let ; as "funções de trabalho" serão assim todas invisíveis para o código fora do ambiente let , o que significa que não precisará de se preocupar em criar nomes de funções únicos. Apenas o nome do relatório tem forçosamente de ser único. Desde que cada relatório esteja do mesmo modo envolto num ambiente let , você poderia chamar a todas as funções de "renderização" rederizar sem causar quaisquer conflitos. A função de gerador de opções O gerador de opções introduz um número adicional de funções que são utilizadas para definir janelas de opções. (define (options-generator) (let ((option-set (gnc:new-options))) (gnc:options-add-report-date! option-set pagename-general "End Date" "a") (gnc:options-add-date-interval! option-set pagename-general "From" "To" "a") (gnc:options-add-interval-choice! option-set pagename-general "Step Size" "b" 'YearDelta) (gnc:options-add-account-levels! option-set pagename-general "Show Accounts down to Level" "c" 2) (gnc:options-add-account-selection! option-set pagename-general "Account Display Depth" "Always show subaccounts" "Accounts" "a" 3 *LIST-OF-INCOME-AND-EXPENSE-ACCOUNTS* options)) gnc:new-options cria um conjunto novo de opções, vazio. Isto tem de ser executado primeiro; as funções seguintes necessitam de ter um conjunto de opções a que se referir. gnc:options-add-report-date! adiciona uma opção de selecção que indica uma data única, geralmente utilizada como a data de final. gnc:options-add-date-interval! adiciona uma opção de selecção que permite especificar um intrevalo de datas. gnc:options-add-interval-choice! adiciona uma opção de selecção que permite escolher entre vários intrevalos de tempo, incluindo dias, semanas, quinzenas, meses, e anos. gnc:options-add-account-levels! adiciona uma opção que indica até que profundidade um conjunto de níveis de contas deverá ser mostrado. gnc:options-add-account-selection! permite seleccionar um conjunto de contas. Note que o último argumento é uma lista de contas de onde seleccionar, o que significa ser necessário, a um determinado ponto, filtrar uma lista de contas relevantes. Existem ainda funções de opções adicionais: gnc:options-add-currency! para seleccionar uma moeda; gnc:options-add-plot-size! para controlar como um gráfico deverá ser dimensionado. Subjacentes a estas existem as seguintes funções base do "gerador de opções" definidas em options-utilities.scm, que podem ser utilizadas para criar novos tipos de opções: gnc:register-option gnc:make-date-option gnc:make-multichoice-option gnc:make-simple-boolean-option gnc:make-account-list-option gnc:make-currency-option gnc:make-number-range-option Aceder aos Dados do GnuCash Existem vários formatos de dados que poderá querer aceder: Informação de Contas Preços de Acções Dados de Leitura de Opções Aceder aos Dados do Motor As funções utilizadas para aceder às várias formas de dados contabilísticos podem ser encontradas no ficheiro src/g-wrap/gnc.html . São necessários alguns exemplos aqui... Aceder a Dados de Opções Functions gnc:lookup-option, gnc:report-options, e gnc:option-value são as funções cruciais que mais provavelmente utilizará em src/scm/options.scm Um excerto do src/scm/report/hello-world.scm é o seguinte: ;; Primeiro, construir algumas funções de apoio para procurar valores de opções. (define (get-op section name) (gnc:lookup-option (gnc:report-options report-obj) section name)) (define (op-value section name) (gnc:option-value (get-op section name))) ;; O próximo passo é criar variáveis locais para todas as opções ;; específicas no conjunto de opções passado à função. (let ((bool-val (op-value "Hello, World!" "Boolean Option")) (mult-val (op-value "Hello, World!" "Multi Choice Option")) (string-val (op-value "Hello, World!" "String Option")) (date-val (gnc:date-option-absolute-time (op-value "Hello, World!" "Just a Date Option"))) (date2-val (gnc:date-option-absolute-time (op-value "Hello, World!" "Time and Date Option"))) (rel-date-val (gnc:date-option-absolute-time (op-value "Hello, World!" "Relative Date Option"))) (combo-date-val (gnc:date-option-absolute-time (op-value "Hello, World!" "Combo Date Option"))) (num-val (op-value "Hello, World!" "Number Option")) (bg-color-op (get-op "Hello, World!" "Background Color")) (txt-color-op (get-op "Hello, World!" "Text Color")) (accounts (op-value "Hello Again" "An account list option")) (list-val (op-value "Hello Again" "A list option")) (crash-val (op-value "Testing" "Crash the report"))) (now-do-stuff-with-options)) Preços de Acções A base de dados de preços de acções está em desenvolvimento, pelo que é um pouco sedo para ser específico sobre isto... Funções de Geração HTML Os relatórios são gerados como uma árvore de registos Guile, tendo como raíz um registo <documento html>, que consiste em informação de estilos, um título, e uma lista de registos <objecto html> que consistem numa função de renderização e noutra lista de objectos. Poderemos assim gerar um relatório simples: (define (build-simple-document) (let* ((document (gnc:make-html-document)) ;;; Aqui está um par de funções auxiliares (addfpara (lambda (obj) (gnc:html-document-add-object! document (gnc:make-html-text (gnc:html-markup-p obj))))) (addpara (lambda (text) (addfpara (gnc:html-markup/format text))))) ;;; Definir o título (gnc:html-document-set-title! document (_ "Simple List of Values")) ;;; Adicionar um parágrafo de texto (addpara (_ "This is a simple report, starting with a paragraph of text")) (addpara (_ "Next, we calculate random values, adding them to a balance.")) (let loop ((balance 0)) (if (< balance 500) (let ((newamt (- (random 500 200)))) ;;; Random number (addfpara (gnc:html-markup/format (_ "Another random adjustment of %s yields %s") (gnc:html-markup-tt (number->string newamt)) (gnc:html-markup-b (number->string balance)))) (loop (+ balance newamt))))) document)) ;;; Finalmente, devolver o documento A função de renderização A função de renderização é onde as funções das secções anteriores se juntam. Recebe, como entrada, uma "base de dados" de opções geradas assim que o utilizador adiciona os seus dados à janela produzida pelo generador de opções . Utiliza essas opções para controlar como aceder aos dados do GnuCash Finalmente, a partir desses dados, gera uma "base de dados" de texto HTML. A fnção de renderização disponibiliza, como valor de retorno, a "base de dados de HTML," que o GnuCash apresenta no visualizador de HTML. (define (renderer options) (let ((document (gnc:make-html-document))) document)) ;;; Gera um documento vazio Para Mais Informações Se necessitar de mais informações, ou tiver desenvolvido um relatório novo que possa ser útil para outros, por favor contacte a lista de desenvolvimento do GnuCash em gnucash-devel@gnucash.org .