Artigo Henry Pereira · Ago. 3, 2025 5m read

artisan cover

Se você já viu um artesão em ação — seja um ceramista moldando o barro até virar arte ou um luthier transformando madeira bruta em um violão extraordinário — sabe que a mágica não está nos materiais, e sim no cuidado, na técnica e no processo. Tenho uma guitarra feita por um luthier que me inspira todos os dias. Mas, vou confessar, criar algo assim é um talento que não tenho.

Por outro lado, no digital, vejo muita gente esperando essa mesma “mágica” de uma IA generativa... prompts genéricos como “crie um app” e esperando resultados espetaculares. Espera-se que a IA faça milagres com zero contexto.

Foi dessa frustração que nasceu o dc-artisan — uma ferramenta para quem quer ser um verdadeiro artesão na criação de prompts. A proposta? Ajudar qualquer pessoa a transformar aquela ideia bruta e mal formata em um prompt funcional, eficiente e cheio de contexto.

Assim como o artesão não faz belas peças por sorte, gerar bons resultados com IA exige intenção, preparo e um bom processo. O problema quase nunca é a IA em si — é como nós a utilizamos. Igual ao luthier que escolhe a madeira certa e molda cada detalhe, engenhar prompts bons exige clareza, estrutura e propósito.

Acreditamos que o mundo merece muito mais do que “prompts mágicos” que só levem à decepção. A IA generativa mostra todo seu potencial guiada por humanos com clareza, objetivos reais e estrutura bem pensada. Nenhum artesão cria beleza por acidente — gerar boas respostas com IA requer preparo e cuidado.

Engenharia de Prompt é uma Arte — e o dc-artisan é Sua Oficina

O dc-artisan trata a criação de prompts como um ofício — algo sistematizado, que pode ser aprendido, testado e aprimorado. Ele oferece uma caixa de ferramentas completa, indo além da tentativa-e-erro.

De início, o dc-artisan busca entender seu prompt, ele interage diretamente com você:

  • Perguntas inteligentes: O dc-artisan analisa seu prompt e faz perguntas pontuais para entender o que você realmente quer, quem é o público, qual o formato esperado e que informações faltam. Por exemplo:
    • “Você espera qual tipo de saída? Um resumo textual, código ou dados estruturados?”
    • “Quem é o público-alvo?”
    • “Qual será o tipo de conteúdo usado com esse prompt?”

prompt enhance

Essas interações te ajudam a entender não apenas o “o que” você quer dizer, mas o “por quê”.

Com o objetivo claro, o dc-artisan analisa a estrutura do seu prompt e oferece sugestões personalizadas — para melhorar a clareza, ajustar o tom e preencher lacunas críticas de contexto.

E o melhor: tudo isso dentro do editor favorito — o VS Code! Você pode incluir variáveis diretamente no seu prompt (como {task} ou {audience}), o que oferece flexibilidade e reaproveitamento. É possível visualizar instantaneamente como os prompts ficam com diferentes substituições — e ver exatamente como vão funcionar.

Mas não para por aí.

Teste, Ajuste e Melhore Seus Prompts

O dc-artisan também ajuda você a fazer tuning de prompts para extrair o melhor desempenho. Basta fazer o upload de um CSV com casos de teste para que a ferramenta avalie de forma automatizada a consistência, a qualidade da saída e o impacto da estrutura do prompt em diferentes contextos.

O dc-artisan analisa cada resposta e gera um relatório completo com métricas de similaridade — assim você pode otimizar seus prompts com dados reais, não com achismo.

testing

Criar Prompts Sem Contexto Não É Técnica — É Caos

Criar prompts sem estrutura é como tentar esculpir madeira vendado. Algo pode sair... mas dificilmente vai soar como um bom instrumento.

Muitos caem em dois extremos: prompts vagos e curtos demais, ou blocos enormes de conteúdo jogados sem organização. Nenhum dos dois funciona. Ou a IA não entende o que você quer, ou se perde em um mar de informações inúteis.

E quando o contexto é longo ou confuso demais, até LLMs avançadas perdem o rumo. Em vez de pensar, elas repetem o conteúdo anterior ou se prendem a padrões do início da conversa. Ironicamente, modelos com contexto grandes (como 32k tokens) são ainda mais propensos a este erro.

A solução para esse problema? RAG (Retrieval-Augmented Generation): não dar qualquer informação à IA, mas sim as relevantes, no momento certo.

Como o dc-artisan e o Modo RAG Pipeline Entram em Cena

O dc-artisan une criação de prompts com gestão de contexto. Ele não só te ajuda a escrever prompts melhores — ele garante que a IA receba informação relevante, e não um tsunami aleatório.

Com o Modo RAG Pipeline, você pode:

  • 🗂️ Enviar e dividir documentos: PDF, DOCX, Markdown, TXT — tudo isso pode ser dividido em blocos e enviado para sua base vector.
  • 🧬 Inspecionar blocos de texto: Veja cada bloco de embedding vector com clareza.
  • 🧹 Gerenciar conteúdo: Remova diretamente blocos desatualizados ou irrelevantes, mantendo a “memória” da IA enxuta e refinada.

rag

Essa ideia foi inspirada pelo Portal de Ideias da InterSystems (DPI-I-557)

Arquitetura Robusta e Flexível

Um dos maiores diferenciais do dc-artisan está por trás dos panos: seu backend. A extensão roda sobre o InterSystems IRIS Interoperability, com um adapter para o liteLLM que desenvolvemos.

Essa estrutura garante flexibilidade e integração com múltiplas LLMs. Você não fica preso a uma única IA. Pode alternar e conectar, no mesmo ambiente, com plataformas como OpenAI, Gemini, Claude, Azure OpenAI entre outras.

Cada vez mais desenvolvedores estão percebendo que prompting não é sobre “mágica” — é sobre propósito, clareza e contexto. Não se trata de chutar palavras certas, mas projetar prompts como engenheiros e não como feiticeiros.

Assim como luthiers criam instrumentos com alma a partir de madeira, você também pode esculpir prompts com contexto, previsíveis e eficazes com uma ferramenta feita sob medida para isso.

dc-artisan não é só uma ferramenta — é uma mudança de mentalidade: do improviso para a precisão, da sorte para a técnica, da intuição para a arte.

🎸 Pronto pra criar prompts com as próprias mãos?
⚙️ Abra o VS Code, instale o dc-artisan e comece a esculpir seu prompt como um artesão — não como um mágico.

dc-artisan

2
0 26
Artigo Henry Pereira · Maio 30, 2025 6m read

imagem

Sabe aquela sensação de receber o resultado do seu exame de sangue e parecer que está em grego? É exatamente esse problema que o FHIRInsight veio resolver. Surgiu da ideia de que dados médicos não deveriam ser assustadores ou confusos – deveriam ser algo que todos podemos utilizar. Exames de sangue são extremamente comuns para verificar nossa saúde, mas, sejamos sinceros, interpretá-los é difícil para a maioria das pessoas e, às vezes, até para profissionais da área que não trabalham em um laboratório. O FHIRInsight quer tornar todo esse processo mais simples e acessível.

Logo FHIRInsight

🤖 Por que criamos o FHIRInsight

Tudo começou com uma simples pergunta:

“Por que interpretar um exame de sangue ainda é tão difícil — até para médicos, às vezes?”

Se você já olhou para um resultado de laboratório, provavelmente viu um monte de números, abreviações enigmáticas e uma “faixa de referência” que pode ou não se aplicar à sua idade, sexo ou condição. É uma ferramenta de diagnóstico, com certeza — mas sem contexto, vira um jogo de adivinhação. Até profissionais de saúde experientes às vezes precisam recorrer a diretrizes, artigos científicos ou opiniões de especialistas para entender tudo direito.

É aí que o FHIRInsight entra em cena.

Não foi feito apenas para pacientes — fizemos para quem está na linha de frente do atendimento. Para médicos em plantões intermináveis, para enfermeiros que captam sutis padrões nos sinais vitais, para qualquer profissional de saúde que precise tomar decisões certas com tempo limitado e muita responsabilidade. Nosso objetivo é facilitar um pouco o trabalho deles — transformando dados clínicos FHIR em algo claro, útil e fundamentado em ciência médica real. Algo que fale a língua humana.

O FHIRInsight faz mais do que só explicar valores de exames. Ele também:

  • Fornece aconselhamento contextual sobre se um resultado é leve, moderado ou grave
  • Sugere causas potenciais e diagnósticos diferenciais com base em sinais clínicos
  • Recomenda próximos passos — sejam exames adicionais, encaminhamentos ou atendimento de urgência
  • Utiliza RAG (Retrieval-Augmented Generation) para incorporar artigos científicos relevantes que fundamentam a análise

Imagine um jovem médico revisando um hemograma de um paciente com anemia. Em vez de buscar cada valor no Google ou vasculhar revistas médicas, ele recebe um relatório que não só resume o problema, mas cita estudos recentes ou diretrizes da OMS que embasam o raciocínio. Esse é o poder de combinar IA e busca vetorial sobre pesquisas selecionadas.

E o paciente?

Ele não fica mais diante de um monte de números, sem saber o que significa “bilirrubina 2,3 mg/dL” ou se deve se preocupar. Em vez disso, recebe uma explicação simples e cuidadosa. Algo que se assemelha mais a uma conversa do que a um laudo clínico. Algo que ele realmente entende — e que pode levar ao consultório do médico mais preparado e menos ansioso.

Porque é disso que o FHIRInsight trata de verdade: transformar complexidade médica em clareza e ajudar profissionais e pacientes a tomarem decisões melhores e mais confiantes — juntos.

🔍 Por dentro da tecnologia

Claro que toda essa simplicidade na superfície é viabilizada por uma tecnologia poderosa atuando discretamente nos bastidores.

Veja do que o FHIRInsight é feito:

  • FHIR (Fast Healthcare Interoperability Resources) — padrão global de dados de saúde. É assim que recebemos informações estruturadas como resultados de exames, histórico do paciente, dados demográficos e atendimentos. O FHIR é a linguagem que os sistemas médicos falam — e nós traduzimos essa linguagem em algo que as pessoas possam usar.
  • Busca Vetorial RAG (Retrieval-Augmented Generation): o FHIRInsight aprimora seu raciocínio diagnóstico indexando artigos científicos em PDF e URLs confiáveis em um banco de dados usando a busca vetorial nativa do InterSystems IRIS. Quando um resultado de exame fica ambíguo ou complexo, o sistema recupera conteúdo relevante para embasar suas recomendações — não da memória do modelo, mas de pesquisas reais e atualizadas.
  • Engenharia de Prompts para Raciocínio Médico: refinamos nossos prompts para guiar o LLM na identificação de um amplo espectro de condições relacionadas ao sangue. Desde anemia por deficiência de ferro até coagulopatias, desequilíbrios hormonais ou gatilhos autoimunes — o prompt conduz o LLM pelas variações de sintomas, padrões laboratoriais e causas possíveis.
  • Integração com LiteLLM: um adaptador customizado direciona requisições para múltiplos provedores de LLM (OpenAI, Anthropic, Ollama etc.) através de uma interface unificada, permitindo fallback, streaming e troca de modelo com facilidade.

Tudo isso acontece em segundos — transformando valores brutos de laboratório em insights médicos explicáveis e acionáveis, seja para um médico revisando 30 prontuários ou um paciente tentando entender seus números.

🧩 o Adapter LiteLLM: Uma Interface para Todos os Modelos

Nos bastidores, os relatórios do FHIRInsight movidos a IA são impulsionados pelo LiteLLM — uma library python que nos permite chamar mais de 100 LLMs (OpenAI, Claude, Gemini, Ollama etc.) por meio de uma única interface estilo OpenAI.

Porém, integrar o LiteLLM ao InterSystems IRIS exigiu algo mais permanente e reutilizável do que scripts Python escondidos numa Business Operation. Então, criamos nosso próprio Adapter LiteLLM.

Conheça o LiteLLMAdapter

Esse adaptador lida com tudo o que você esperaria de uma integração robusta de LLM:

  • Recebe parâmetros como prompt, model e temperature
  • Carrega dinamicamente variáveis de ambiente (por exemplo, chaves de API)

Para encaixar isso em nossa produção de interoperabilidade em um simples Business Operation:

  • Gerencia configuração em produção por meio da configuração padrão LLMModel
  • Integra-se ao componente FHIRAnalyzer para geração de relatórios em tempo real
  • Atua como uma “ponte de IA” central para quaisquer componentes futuros que precisem de acesso a LLM

Eis o fluxo de forma simplificada:

set response = ##class(dc.LLM.LiteLLMAdapter).CallLLM("Me fale sobre hemoglobina.", "openai/gpt-4o", 0.7)
write response

🧪 Conclusão

Quando começamos a desenvolver o FHIRInsight, nossa missão era simples: tornar exames de sangue mais fáceis de entender — para todo mundo. Não apenas pacientes, mas médicos, enfermeiros, cuidadores... qualquer pessoa que já tenha encarado um resultado de laboratório e pensado: “Ok, mas o que isso realmente significa?”

Todos nós já passamos por isso.

Ao combinar a estrutura do FHIR, a velocidade do InterSystems IRIS, a inteligência dos LLMs e a profundidade de pesquisas médicas reais via busca vetorial, criamos uma ferramenta que transforma números confusos em histórias significativas. Histórias que ajudam pessoas a tomarem decisões mais inteligentes sobre sua saúde — e, quem sabe, detectar algo cedo que passaria despercebido.

Mas o FHIRInsight não é só sobre dados. É sobre como nos sentimos ao olhar para esses dados. Queremos que seja claro, acolhedor e empoderador. Que a experiência seja... como um verdadeiro “vibecoding” na saúde — aquele ponto perfeito onde código inteligente, bom design e empatia humana se encontram.

Esperamos que você experimente, teste, questione — e nos ajude a melhorar.

Diga o que gostaria de ver no próximo release. Mais condições? Mais explicações? Mais personalização?

Isso é só o começo — e adoraríamos que você ajudasse a moldar o que vem a seguir.

1
0 33
Artigo Henry Pereira · Abr. 4, 2022 3m read

pandas

Não há dúvidas que as possibilidades são ilimitadas com o que se pode fazer com o IRIS 2021.2 com Embedded Python, desde visão computacional, automações, blockchain e principalmente a inteligência artificial.

Python é umas das linguagens que mais tem crescido nos últimos anos e existe uma infinidade de bibliotecas para facilitar a vida dos desenvolvedores.

Meu objetivo é escrever de forma simples, uma introdução ao Pandas para desenvolvedores de ObjectScript.

kungfupanda

Mas o que é Pandas?

Pandas é uma biblioteca criada para análise, manipulação e tratamento de dados.

A título de curiosidade, Pandas vem de "panel data" (dados em painel), um termo usado em estatística e econometria para conjunto de dados que incluem várias unidades amostrais (indivíduos, empresas, etc) acompanhadas ao longo do tempo. (fonte: Wikipedia)

No IRIS 2021.2 para utilizar o Pandas, primeiro é necessário importar.

import pandas as pd

import

Existem dois tipos principais de estruturas de dados no Pandas, que são Series e DataFrames.

Uma Serie nada mais é que um vetor, um array unidimensional.

selecaoPenta = pd.Series([“Lúcio”, “Edmílson”, “Roque Junior”, “Gilberto Silva”, “Marcos”, “Kaká”, “Vampeta”, “Anderson Polga”, “Dida”, “Rogério Ceni”, “Belletti”, “Ronaldinho”, “Ronaldo”, “Roberto Carlos”, “Kléberson”, “Rivaldo”, “Cafu”, “Júnior”, “Ricardinho”, “Luizão”, “Edílson”, “Denílson”, “Juninho Paulista”])

Já o DataFrame é uma estrutura de dados bidimensional. Um DataFrame é constituído de Series.

Vamos criar uma variável chamada artilheiros, com conjuntos de chaves e valores, com nome e quantidades de gols

artilheiros = {'Jogador':['Miroslav Klose', 'Ronaldo', 'Gerd Muller', 'Just Fontaine', 'Pele', 'Sandor Kocsis'],
'Gols': [16, 15, 14, 13, 12, 11]}
df = pd.DataFrame(artilheiros)

dataFrame

O método info() podemos obter um resumo do DataFrame

info

Com o método head() é possível visualizar as 5 primeiras linhas de um DataFrame e com tail() as 5 últimas. Podemos utilizar sort_values() para ordernar um DataFrame

sort

No Pandas podemos utilizar condicionais como índice para filtrar dados de maneira simples.

conditional

Com isnull() filtra apenas os que são nulos e notnull() é o oposto, apenas os que não são nulos.

O mais incrível é que é possível executar Queries SQL no IRIS e retornar diretamente em um DataFrame.

import iris
import pandas as pd
rs = iris.sql.exec("select Name, Super, TimeCreated from %Dictionary.ClassDefinition WHERE Name %STARTSWITH %Net.")
mydataframe = rs.dataframe()

É possível importar e exportar dados nos mais diversos formatos, podemos exportar um DataFrame para csv, xml, json ou html simplesmente executando to_csv(), to_xml(), to_json() ou to_html()

export

Para saber mais do Pandas aqui está a documentação oficial

Como sempre, se tiver dúvidas, comentários ou sugestões não hesite em me escrever! Obrigado por ler.

0
2 294
Artigo Henry Pereira · Dez. 1, 2021 5m read

freepik- freepik.com Em primeiro lugar, o que é anonimização de dados?

De acordo com a Wikipedia:

O anonimização de dados é um tipo de higienização de informações cujo objetivo é a proteção da privacidade. É o processo de remoção de informações de identificação pessoal dos conjuntos de dados, para que as pessoas que os dados descrevem permaneçam anônimas.

Em outras palavras, anonimização retém os dados, mas mantém a fonte anônima. Dependendo da técnica de anonimização adotada os dados são editados, mascarados ou substituídos.

E é este o propóstido do iris-Disguise, providenciar várias ferramentas de anonimização.

Você pode utilizar de duas maneira, executando um método ou especificando a estratégia de anonimização dentro da definição da classe persistente.

Na versão atual o iris-Disguise oferece 6 estratégias de anonimização:

  • Destruction
  • Scramble
  • Shuffling
  • Partial Masking
  • Randomization
  • Faking

Explicarei cada estratégia, mostrarei a execução por método e como mencionado, também mostrarei como usar pela definição da classe persistente. Para usar o iris-Disguise desta maneira você precisa do "óculos de disfarce". Na classe persistente, extenda da classe dc.Disguise.Glasses e mude o tipo das propriedades de acordo com a estratégia que escolher. Feito isto, a qualquer momento, é só chamar o método DisguiseProcess na própria classe. Todos os valores serão substituidos utilizando a estratégia definida na propriedade.

Vamos lá!

Destruction

Esta estratégia substitui a coluna inteira com uma palavra ('CONFIDENTIAL' é o padrão).

Do ##class(dc.Disguise.Strategy).Destruction("nomeDaClasse", "nomeDaPropriedade", "palavra")

O terceiro parâmetro é opcional. Caso não informado 'CONFIDENTIAL' será usado.

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As dc.Disguise.DataTypes.String(FieldStrategy = "DESTRUCTION");
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

1

Scramble

Esta estratégia embaralha todos os caracteres da propriedade.

Do ##class(dc.Disguise.Strategy).Scramble("nomeDaClasse", "nomeDaPropriedade")

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As dc.Disguise.DataTypes.String(FieldStrategy = "SCRAMBLE");
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

scramble

Shuffling

Shuffling vai rearranjar todos valores da propriedade. Não é considerado uma estratégia de máscara porque trabalha "verticalmente". É uma estratégia útil para relationship pois mantém a integridade. Até a versão atual, funciona apenas para relacionamentos one-to-many.

Do ##class(dc.Disguise.Strategy).Shuffling("nomeDaClasse", "nomeDaPropriedade")

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As %String;
Property Weapon As dc.Disguise.DataTypes.String(FieldStrategy = "SHUFFLING");
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

shuffling

Partial Masking

Esta estratégia ofusca parte dos dados, um número de cartão de crédito, por exemplo, será substituido por 456X XXXX XXXX X783

Do ##class(dc.Disguise.Strategy).PartialMasking("nomeDaClasse", "nomeDaPropriedade", prefix, suffix, "mascara")

Prefix, suffix e mascara são opcionais.

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As %String;
Property SSN As dc.Disguise.DataTypes.PartialMaskString(prefixLength = 2, suffixLength = 2);
Property Weapon As %String;
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

partialmsk

Randomization

Esta estratégia gera dados puramente aleatórios. São 3 tipos de randomização: integer, numeric and date.

Do ##class(dc.Disguise.Strategy).Randomization("nomeDaClasse", "nomeDaPropriedade", "type", from, to)

type: "integer", "numeric" ou "date". "integer" é o padrão.

from e to são opcionais. Servem para definir a faixa de randomização. Para o tipo integer o default é de 1 à 100. Para tipo numeric o default é de 1.00 à 100.00.

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As %String;
Property Age As dc.Disguise.DataTypes.RandomInteger(MINVAL = 10, MAXVAL = 25);
Property SSN As %String;
Property Weapon As %String;
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

rand

Fake Data

A ideia do Faking é substituir por valores aleatórios porém plausíveis. iris-Disguise possui uma pequena quantidade de métodos para gerar fake data.

Do ##class(dc.Disguise.Strategy).Fake("nomeDaClasse", "nomeDaPropriedade", "type")

type: "firstname", "lastname", "fullname", "company", "country", "city" and "email"

Class packageSample.FictionalCharacter Extends (%Persistent, dc.Disguise.Glasses)
{
Property Name As dc.Disguise.DataTypes.FakeString(FieldStrategy = "FIRSTNAME");
Property Age As %Integer;
Property SSN As %String;
Property Weapon As %String;
}
Do ##class(packageSample.FictionalCharacter).DisguiseProcess()

fake

### Quero ouvir você!

Feedbacks e ideias são muito bem vindas!

Me deixe saber o que pensa sobre esta ferramenta, como te atenderia e quais funcionalidades estão faltando.

Quero deixar um agradecimento especial para @Henrique Dias, @Oliver Wilms, @Robert Cemper, @Yuri Marx e @Evgeny Shvarov que comentaram, revisaram, sugeriram e fizeram ricas discussões que me inspiraram para criar e melhorar o iris-Disguise.

Se gostou da ideia poderia deixar o seu voto para o iris-disguise no concurso atual, obrigado.

3
0 131
Artigo Henry Pereira · Set. 12, 2021 3m read

https://media3.giphy.com/media/L0qTl8hl84EDly62J1/giphy.gif?cid=ecf05e47wl2uvkvz3dxsp1axa4gf5tsk7s7nqytg7vwadj38&rid=giphy.gif&ct=g

Sou apaixonado por documentários! No último final de semana estava assistindo um documentário da Netflix chamado This is Pop, como está na época do concurso InterSystems IRIS Analytics, pensei: Por que não criar um analítico da música Pop com InterSystems Iris?

O primeiro desafio era a base. Encontrei no Data World project um arquivo CSV com a lista dos top 100 da Billboard de 2000 à 2018, criado por "Michael Tauberg" @typhon, que encaixava perfeitamente.

Estava conversando com o @Henrique Dias e ele me deu a ideia de usar o Microsoft Power BI para criar um relatório com gráficos bonitos.

Quais foram os gêneros mais populares entre 2000 e 2018?

Quais artistas tiveram mais músicas na Billboard?

Que ano teve mais músicas dançantes?

Vamos analisar a base de dados, com ajuda do csvgen importamos o arquivo CSV.

A base contém:

Title — nome da música

Artist — nome do Artista

Energy — a energia da música — maior o valor, mais energia

Danceability — maior o valor, mais fácil de dançar a música

Loudness..dB.. — maior o valor, mais alta é a música

Liveness — maior o valor, mais provável que a música é uma gravação ao vivo.

Valence. — maior o valor, mais positiva é o estado de espírito da música.

Duration_ms. — a duração da música em milisegundos.

Acousticness.. maior o valor, mais acústica é a música

Speechiness. — maior o valor, mais palavras a música contém

Lyrics — Letra da música.

Genre — Gênero musical

No arquivo CSV o gênero é um array como este: [u'dance pop', u'hip pop', u'pop', u'pop rap', u'rap']

Minha ideia é criar uma tabela Genre (Gênero) e outra para resolver o relacionamento N:N. Um simples script popula estas tabelas.

Depois disto, é só conectar o Power BI no InterSystems Iris (aqui tem um passo-a-passo de como fazer isto).

Próximo passo: Infograficos legais.

https://github.com/henryhamon/pop-song-analytics/blob/master/assets/pop_songs_analytics_1.png?raw=true

Um gráfico de barras mostra a quantidade de artistas com mais músicas na Billboard e um gráfico de linha exibe a duração média das músicas por ano.

Um gráfico de pizza mostra os gêneros mais comuns, para minha surpresa, country contemporâneo é o gênero mais popular.

Música pop tem se tornado barulhenta com o passar dos anos? Para responder usei um diagrama de dispersão com a média loudness das músicas.

A música pop tem se tornado menos ou mais dançante?

Na segunda página um gráfico de barras mostra a como a danceability mudou pelos anos e a relação entre energy versus acousticness.

https://github.com/henryhamon/pop-song-analytics/blob/master/assets/pop_songs_analytics_2.png?raw=true

https://openexchange.intersystems.com/contest/current

Agradecimento especial ao @Henrique Dias pelas boas conversas e pelo apoio.

0
0 107
Artigo Henry Pereira · Ago. 10, 2021 8m read

https://media.giphy.com/media/Nxu57gIbNuYOQ/giphy.gif

Calma, calma, não estou incentivando uma guerra contra as máquinas no melhor estilo sci-fi para impedir ao dominação mundial do Ultron ou da Skynet.

Ainda não... ainda não 🤔

Convido você para desafiarmos as máquinas através da criação de um jogo bem simples utilizando ObjectScript com Python embarcado.

Tenho que dizer que fiquei super empolgado com a feature do Embedded Python no InterSystems IRIS, é incrível o leque de possibilidades que se abre para criar aplicações fantásticas.

Vamos construir um jogo da velha, as regras são bem simples e acredito que todos sabem como jogar.

Era o que me salvava do tédio na minha infância durante viagens longas de carro com a família, antes de crianças terem celulares ou tablets, nada como desafiar meus irmãos a jogar algumas partidas no vidro embaçado.

Então apertem o cinto e vamos lá!

Regras

Como comentado, as regras são muito simples:

  • apenas 2 jogadores por partida
  • é jogado em turnos em um grid de 3x3
  • o jogador humano sempre será a letra X e o computador a letra O
  • os jogadores só poderão colocar as letras nos espaços vazios
  • o primeiro que completar uma sequência de 3 letras iguais na horizontal, ou na vertical ou na diagonal, é o vencedor
  • quando os 9 espaços estiverem ocupados será empate e o fim da partida

https://media4.giphy.com/media/3oriNKQe0D6uQVjcIM/giphy.gif?cid=790b761123702fb0ddd8e14b01746685cc0059bac0bc66e9&rid=giphy.gif&ct=g

Todo o mecanismo e as regras escreveremos em ObjectScript, o mecanismo do jogador do computador será escrito em Python.

Vamos colocar as mãos na massa

Controlaremos o tabuleiro em uma global, sendo que cada linha estará em um nó e cada coluna em um piece.

Nosso primeiro método é para iniciar o tabuleiro, para facilitar irei iniciar a global já com os nós (linhas A, B e C) e com os 3 pieces:

/// Iniciate a New Game
ClassMethod NewGame() As %Status
{
  Set sc = $$$OK
  Kill ^TicTacToe
  Set ^TicTacToe("A") = "^^"
  Set ^TicTacToe("B") = "^^"
  Set ^TicTacToe("C") = "^^"
  Return sc
}

neste momento iremos criar o método para adicionar as letras nos espaços vazios, para isto cada jogador irá passar a localização do espaço do tabuleiro.

Cada linha uma letra e coluna um número, para colocar o X no meio, por exemplo, passamos B2 e a letra X para o método.

ClassMethod MakeMove(move As %String, player As %String) As %Boolean
{
  Set $Piece(^TicTacToe($Extract(move,1,1)),"^",$Extract(move,2,2)) = player
}

Vamos validar se a coordenada passada é válida, a maneira mais simples que vejo é utilizando uma expressão regular:

ClassMethod CheckMoveIsValid(move As %String) As %Boolean
{
  Set regex = ##class(%Regex.Matcher).%New("(A|B|C){1}[0-9]{1}")
  Set regex.Text = $ZCONVERT(move,"U")
  Return regex.Locate()
}

precisamos garantir que o espaço selecionado esteja vazio

ClassMethod IsSpaceFree(move As %String) As %Boolean
{
  Quit ($Piece(^TicTacToe($Extract(move,1,1)),"^",$Extract(move,2,2)) = "")
}

Nooice!

Agora vamos verificar se algum jogador venceu a partida ou se o jogo já terminou, para isto vamos criar o método CheckGameResult.

Primeiro verificamos se teve algum vencedor completando pela horizontal, usaremos uma list com as linhas e um simples $Find resolve

    Set lines = $ListBuild("A","B","C")
    // Check Horizontal
    For i = 1:1:3 {
      Set line = ^TicTacToe($List(lines, i))
      If (($Find(line,"X^X^X")>0)||($Find(line,"O^O^O")>0)) {
        Return $Piece(^TicTacToe($List(lines, i)),"^", 1)_" won"
      }
    }

Com outro for verificamos a vertical

For j = 1:1:3 {
      If (($Piece(^TicTacToe($List(lines, 1)),"^",j)'="") &&
        ($Piece(^TicTacToe($List(lines, 1)),"^",j)=$Piece(^TicTacToe($List(lines, 2)),"^",j)) &&
        ($Piece(^TicTacToe($List(lines, 2)),"^",j)=$Piece(^TicTacToe($List(lines, 3)),"^",j))) {
        Return $Piece(^TicTacToe($List(lines, 1)),"^",j)_" won"
      }
    }

para verificar a diagonal:

    If (($Piece(^TicTacToe($List(lines, 2)),"^",2)'="") &&
      (
        (($Piece(^TicTacToe($List(lines, 1)),"^",1)=$Piece(^TicTacToe($List(lines, 2)),"^",2)) &&
          ($Piece(^TicTacToe($List(lines, 2)),"^",2)=$Piece(^TicTacToe($List(lines, 3)),"^",3)))||
        (($Piece(^TicTacToe($List(lines, 1)),"^",3)=$Piece(^TicTacToe($List(lines, 2)),"^",2)) &&
        ($Piece(^TicTacToe($List(lines, 2)),"^",2)=$Piece(^TicTacToe($List(lines, 3)),"^",1)))
      )) {
      Return ..WhoWon($Piece(^TicTacToe($List(lines, 2)),"^",2))
    }

por último, verificamos se teve um empate

    Set gameStatus = ""
    For i = 1:1:3 {
      For j = 1:1:3 {
        Set:($Piece(^TicTacToe($List(lines, i)),"^",j)="") gameStatus = "Not Done"
      }
    }
    Set:(gameStatus = "") gameStatus = "Draw"

Great!

É hora de criarmos a máquina!

Vamos criar o nosso adversário, precisamos criar um algoritmo capaz de calcular todos os movimentos disponíveis e usar uma métrica para saber qual é o melhor movimento.

O ideal é utilizar um algoritmo de decisão chamado de MiniMax (Wikipedia: MiniMax)

https://media3.giphy.com/media/WhTC5v5qQP4yAUvGKz/giphy.gif?cid=ecf05e47cx92yiew8vsig62tjq738xf7hfde0a2ygyfdl0xt&rid=giphy.gif&ct=g

O algoritmo MiniMax é uma regra de decisão utilizado em teoria dos jogos, teoria de decisão e inteligência artificial.

Basicamente, precisamos saber como jogar assumindo quais serão os possíveis movimentos do oponente e pegar o melhor cenário possível.

Em detalhes, pegamos o cenário atual e recursivamente verificamos o resultado do movimento de cada jogador, caso o computador ganhar a partida pontuamos com +1, caso perder então pontuamos como -1 e 0 como um empate.

Caso não for o final do jogo, abrimos outra árvore a partir do estado atual. Feito isto encontramos a jogada com o valor máximo para o computador e a mínima para o adversário.

Veja o diagrama abaixo, existem 3 movimentos disponíveis: B2, C1 e C3.

Escolhendo C1 ou C3, o oponente tem uma chance de ganhar no próximo turno, já escolhendo B2 independente do movimento do adversário ganhamos a partida.

minimaxp

É como ter a joia do tempo em suas mãos para encontrar a melhor linha do tempo.

https://pa1.narvii.com/7398/463c11d54d8203aac94cda3c906c40efccf5fd77r1-460-184_hq.gif

Convertendo para python

ClassMethod ComputerMove() As %String [ Language = python ]
{
  import iris
  from math import inf as infinity
  computerLetter = "O"
  playerLetter = "X"

  def isBoardFull(board):
    for i in range(0, 8):
      if isSpaceFree(board, i):
        return False
    return True

  def makeMove(board, letter, move):
    board[move] = letter

  def isWinner(brd, let):
    # check horizontals
    if ((brd[0] == brd[1] == brd[2] == let) or \
      (brd[3] == brd[4] == brd[5] == let) or \
      (brd[6] == brd[7] == brd[8] == let)):
        return True
    # check verticals
    if ((brd[0] == brd[3] == brd[6] == let) or \
        (brd[1] == brd[4] == brd[7] == let) or \
        (brd[2] == brd[5] == brd[8] == let)):
        return True
    # check diagonals
    if ((brd[0] == brd[4] == brd[8] == let) or \
        (brd[2] == brd[4] == brd[6] == let)):
        return True
    return False

  def isSpaceFree(board, move):
    #Retorna true se o espaco solicitado esta livre no quadro
    if(board[move] == ''):
      return True
    else:
      return False

  def copyGameState(board):
    dupeBoard = []
    for i in board:
      dupeBoard.append(i)
    return dupeBoard

  def getBestMove(state, player):
    done = "Done" if isBoardFull(state) else ""
    if done == "Done" and isWinner(state, computerLetter): # If Computer won
      return 1
    elif done == "Done" and isWinner(state, playerLetter): # If Human won
      return -1
    elif done == "Done":    # Draw condition
      return 0

    # Minimax Algorithm
    moves = []
    empty_cells = []
    for i in range(0,9):
      if state[i] == '':
        empty_cells.append(i)

    for empty_cell in empty_cells:
      move = {}
      move['index'] = empty_cell
      new_state = copyGameState(state)
      makeMove(new_state, player, empty_cell)

      if player == computerLetter:
          result = getBestMove(new_state, playerLetter)
          move['score'] = result
      else:
          result = getBestMove(new_state, computerLetter)
          move['score'] = result

      moves.append(move)

    # Find best move
    best_move = None
    if player == computerLetter:
        best = -infinity
        for move in moves:
            if move['score'] > best:
                best = move['score']
                best_move = move['index']
    else:
        best = infinity
        for move in moves:
            if move['score'] < best:
                best = move['score']
                best_move = move['index']

    return best_move

  lines = ['A', 'B', 'C']
  game = []
  current_game_state = iris.gref("^TicTacToe")

  for line in lines:
    for cell in current_game_state[line].split("^"):
      game.append(cell)

  cellNumber = getBestMove(game, computerLetter)
  next_move = lines[int(cellNumber/3)]+ str(int(cellNumber%3)+1)
  return next_move
}

Primeiro converto a global em um array simples, ignorando colunas e linhas deixando flat para facilitar.

A cada movimento analisado chamamos o método copyGameState, que como o nome diz, copia o estado do jogo naquele momento, onde aplicamos o MinMax.

O método getBestMove que será chamado recursivamente até finalizar o jogo encontrando um vencedor ou o empate.

Primeiro os espaços vazios são mapeados e verificamos o resultado de cada movimento alternando entre os jogadores.

Os resultados são armazenados em move['score'] para depois de verificar todas as possibilidades encontrar o melhor movimento.

Espero que você tenha se divertido, é possível melhorar a inteligência utilizando algoritmos como Alpha-Beta Pruning (Wikipedia: AlphaBeta Pruning) ou redes neurais, só cuidado para não dar vida a Skynet.

https://media4.giphy.com/media/mBpthYTk5rfbZvdtIy/giphy.gif?cid=790b761181bf3c36d85a50b84ced8ac3c6c937987b7b0516&rid=giphy.gif&ct=g

Fique livre para deixar comentários ou perguntas

That's all folks

Código completo: InterSystems Iris versão 2021.1.0PYTHON

0
0 121
Artigo Henry Pereira · jan 7, 2021 13m read

Tempoestimado de leitura: 6 minutos
 

Olá a todos,

Fui apresentado ao TDD há quase 9 anos e imediatamente me apaixonei por ele. 
Hoje em dia se tornou muito popular, mas, infelizmente, vejo que muitas empresas não o utilizam. Além disso, muitos desenvolvedores nem sabem o que é exatamente ou como usá-lo, principalmente iniciantes.

Visão Geral

Meu objetivo com este artigo é mostrar como usar TDD com %UnitTest. Vou mostrar meu fluxo de trabalho e explicar como usar o cosFaker, um dos meus primeiros projetos, que criei usando o Caché e recentemente carreguei no OpenExchange.

Então, aperte o cinto e vamos lá.

O que é TDD?

O Desenvolvimento Guiado por Testes (TDD) pode ser definido como uma prática de programação que instrui os desenvolvedores a escrever um novo código apenas se um teste automatizado falhar.
Existem toneladas de artigos, palestras, apresentações, seja o que for, sobre suas vantagens e todas estão corretas.
Seu código já nasce testado, você garante que seu sistema realmente atenda aos requisitos definidos para ele, evitando o excesso de engenharia, e você tem um feedback constante.

Então, por que não usar o TDD? Qual é o problema com o TDD? A resposta é simples: o Custo! Isso custa muito!
Como você precisa escrever mais linhas de código com TDD, é um processo lento. Mas com o TDD você tem um custo final para criar um produto AGORA, sem ter que adicioná-lo no futuro.
Se você executar os testes o tempo todo, encontrará os erros antecipadamente, reduzindo assim o custo de sua correção.
Portanto, meu conselho: Simplesmente Faça!

Configuração

A InterSystems tem uma documentação e tutorial sobre como usar o  %UnitTest, que você pode ler aqui. 

Eu uso o vscode para desenvolver. Desta forma, crio uma pasta separada para testes. Eu adiciono o caminho para código do meu projeto ao UnitTestRoot e quando executo testes, passo o nome da subpasta de teste. E eu sempre passo no qualificador loadudl

Set ^UnitTestRoot = "~/code"

  Do ##class(%UnitTest.Manager).RunTest("myPack","/loadudl")

 

Passos

Provavelmente você já ouviu falar sobre o famoso ciclo TDD: vermelho ➡ verde ➡ refatorar. Você escreve um teste que falha, você escreve um código de produção simples para fazê-lo passar e refatora o código de produção.
Então, vamos sujar as mãos e criar uma classe para fazer cálculos matemáticos e outra para testá-la. A última classe deve estender de %UnitTest.TestCase.
Agora vamos criar um ClassMethod para retornar um quadrado de um número inteiro:



Class Production.Math

{


ClassMethod Square(pValue As %Integer) As %Integer

{

}


}

 

E teste o que acontecerá se passarmos 2. Deve retornar 4.

Class TDD.Math Extends %UnitTest.TestCase

{


Method TestSquare()

{

    Do $$$AssertEquals(##class(Production.Math).Square(2), 4)

}


}

 

Se você executar:

Do ##class(%UnitTest.Manager).RunTest("TDD","/loadudl")

o teste irá Falhar

Vermelho! O próximo passo é torná-lo Verde. 
Para fazer funcionar, vamos retornar 4 como resultado da execução do nosso método Square.

Class Production.Math

{


ClassMethod Square(pValue As %Integer) As %Integer

{

  Quit 4

}


}

e executar novamente nosso teste.

Provavelmente você não está muito satisfeito com esta solução, porque ela funciona para apenas um cenário. Ótimo! Vamos dar o próximo passo. Vamos criar outro cenário de teste, agora enviando um número negativo.

Class TDD.Math Extends %UnitTest.TestCase

{


Method TestSquare()

{

    Do $$$AssertEquals(##class(Production.Math).Square(2), 4)

}


Method TestSquareNegativeNumber()

{

    Do $$$AssertEquals(##class(Production.Math).Square(-3), 9)

}


}

Quando executamos o teste:

ele Falhará novamente, então vamos refatorar o código de produção:

Class Production.Math

{


ClassMethod Square(pValue As %Integer) As %Integer

{

  Quit pValue * pValue

}


}

e executar novamente nossos testes:

Agora tudo funciona bem... Esse é o ciclo do TDD, em pequenos passos.

Você deve estar se perguntando: por que devo seguir esses passos? Por que eu tenho que ver o teste falhar?
Trabalhei em equipes que escreveram o código de produção e só depois escrevi os testes. Mas eu prefiro seguir estes passos de bebê pelos seguintes motivos:
Tio Bob (Robert C. Martin) disse que escrever testes depois de escrever o código não é TDD e, em vez disso, é chamado de “perda de tempo”.
Outro detalhe, quando vejo o teste falhar, e depois vejo passar, estou testando o teste.
Seu teste também é um código; e pode conter erros também. E a maneira de testá-lo é garantir que ele falhe e seja aprovado quando for necessário. Desta forma, você "testou o teste". 

cosFaker

Para escrever bons testes, você pode precisar gerar dados de teste primeiro. Uma maneira de fazer isso é gerar um despejo (dump) de dados e usá-lo em seus testes.
Outra maneira é usar o cosFaker para gerar facilmente dados falsos quando você precisar deles. https://openexchange.intersystems.com/package/CosFaker

Basta fazer o download do arquivo xml, em seguida vá para o Portal de Gerenciamento -> System Explorer -> Classes -> Import. Selecione o arquivo xml a ser importado ou arraste o arquivo no Studio.
Você também pode importá-lo usando o Terminal

Do $system.OBJ.Load("yourpath/cosFaker.vX.X.X.xml","ck")

 

Localização

O cosFaker adicionará arquivos de localidades na pasta da aplicação CSP padrão. Por enquanto, existem apenas dois idiomas: Inglês e Português do Brasil (minha língua nativa). 
O idioma dos dados é escolhido de acordo com a configuração do seu Caché.
A localização do cosFaker é um processo contínuo, se você quiser ajudar, não hesite em criar um provedor localizado para sua própria localidade e enviar um Pull Request.
Com o cosFaker você pode gerar palavras aleatórias, parágrafos, números de telefone, nomes, endereços, e-mails, preços, nomes de produtos, datas, códigos de cores hexadecimais... etc.

Todos os métodos são agrupados por assunto nas classes, ou seja, para gerar uma Latitude você chama o método Latitude na classe Address 

 Write ##class(cosFaker.Address).Latitude()

-37.6806

Você também pode gerar Json para seus testes

Write ##class(cosFaker.JSON).GetDataJSONFromJSON("{ip:'ipv4',created_at:'date.backward 40',login:'username', text: 'words 3'}")

{
    "created_at":"2019-03-08",
    "ip":"95.226.124.187",
    "login":"john46",
    "text":"temporibus fugit deserunt"
}

 

Aqui está uma lista completa das classes e métodos do cosFaker:

  • cosFaker.Address
    • StreetSuffix
    • StreetPrefix
    • PostCode
    • StreetName
    • Latitude
      • Output: -54.7274
    • Longitude
      • Output: -43.9504
    • Capital( Location = “” )
    • State( FullName = 0 )
    • City( State = “” )
    • Country( Abrev = 0 )
    • SecondaryAddress
    • BuildingNumber
  • cosFaker.App
    • FunctionName( Group= “”, Separator = “” )
    • AppAction( Group= “” )
    • AppType
  • cosFaker.Coffee
    • BlendName
      • Output: Cascara Cake
    • Variety
      • Output: Mundo Novo
    • Notes
      • Output: crisp, slick, nutella, potato defect!, red apple
    • Origin
      • Output: Rulindo, Rwanda
  • cosFaker.Color
    • Hexadecimal
      • Output: #A50BD7
    • RGB
      • Output: 189,180,195
    • Name
  • cosFaker.Commerce
    • ProductName
    • Product
    • PromotionCode
    • Color
    • Department
    • Price( Min = 0, Max = 1000, Dec = 2, Symbol = “” )
      • Output: 556.88
    • CNPJ( Pretty = 1 )
      • CNPJ is the Brazilian National Registry of Legal Entities
      • Output: 44.383.315/0001-30
  • cosFaker.Company
    • Name
    • Profession
    • Industry
  • cosFaker.Dates
    • Forward( Days = 365, Format = 3 )
    • Backward( Days = 365, Format = 3 )
  • cosFaker.DragonBall
    • Character
      • Output: Gogeta
  • cosFaker.File
    • Extension
      • Output: txt
    • MimeType
      • Output: application/font-woff
    • Filename( Dir = “”, Name = “”, Ext = “”, DirectorySeparator = “/” )
      • Output: repellat.architecto.aut/aliquid.gif
  • cosFaker.Finance
    • Amount( Min = 0, Max = 10000, Dec = 2, Separator= “,”, Symbol = “” )
      • Output: 3949,18
    • CreditCard( Type = “” )
      • Output: 3476-581511-6349
    • BitcoinAddress( Min = 24, Max = 34 )
      • Output: 1WoR6fYvsE8gNXkBkeXvNqGECPUZ
  • cosFaker.Game
    • MortalKombat
      • Output: Raiden
    • StreetFighter
      • Output: Akuma
    • Card( Abrev = 0 )
      • Output: 5 of Diamonds
  • cosFaker.Internet
    • UserName( FirstName = “”, LastName = “” )
    • Email( FirstName = “”, LastName = “”, Provider = “” )
    • Protocol
      • Output: http
    • DomainWord
    • DomainName
    • Url
    • Avatar( Size = “” )
    • Slug( Words = “”, Glue = “” )
    • IPV4
      • Output: 226.7.213.228
    • IPV6
      • Output: 0532:0b70:35f6:00fd:041f:5655:74c8:83fe
    • MAC
      • Output: 73:B0:82:D0:BC:70
  • cosFaker.JSON
    • GetDataOBJFromJSON( Json = “” //  String de modelo JSON para criar dados )
      • Parameter Example: "{dates:'5 date'}"
      • Output: {"dates":["2019-02-19","2019-12-21","2018-07-02","2017-05-25","2016-08-14"]}
  • cosFaker.Job
    • Title
    • Field
    • Skills
  • cosFaker.Lorem
    • Word
    • Words( Num = “” )
    • Sentence( WordCount = “”, Min = 3, Max = 10 )
      • Output: Sapiente et accusamus reiciendis iure qui est.
    • Sentences( SentenceCount = “”, Separator = “” )
    • Paragraph( SentenceCount = “” )
    • Paragraphs( ParagraphCount = “”, Separator = “” )
    • Lines( LineCount = “” )
    • Text( Times = 1 )
    • Hipster( ParagraphCount = “”, Separator = “” )
  • cosFaker.Name
    • FirstName( Gender = “” )
    • LastName
    • FullName( Gender = “” )
    • Suffix
  • cosFaker.Person
    • cpf( Pretty = 1 )
      • CPF is the Brazilian Social Security Number
      • Output: 469.655.208-09
  • cosFaker.Phone
    • PhoneNumber( Area = 1 )
      • Output: (36) 9560-9757
    • CellPhone( Area = 1 )
      • Output: (77) 94497-9538
    • AreaCode
      • Output: 17
  • cosFaker.Pokemon
    • Pokemon( EvolvesFrom = “” )
      • Output: Kingdra
  • cosFaker.StarWars
    • Characters
      • Output: Darth Vader
    • Droids
      • Output: C-3PO
    • Planets
      • Output: Takodana
    • Quotes
      • Output: Only at the end do you realize the power of the Dark Side.
    • Species
      • Output: Hutt
    • Vehicles
      • Output: ATT Battle Tank
    • WookieWords
      • Output: nng
    • WookieSentence( SentenceCount = “” )
      • Output: ruh ga ru hnn-rowr mumwa ru ru mumwa.
  • cosFaker.UFC
    • Category
      • Output: Middleweight
    • Fighter( Category = “”, Country = “”, WithISOCountry = 0 )
      • Output: Dmitry Poberezhets
    • Featherweight( Country = “” )
      • Output: Yair Rodriguez
    • Middleweight( Country = “” )
      • Output: Elias Theodorou
    • Welterweight( Country = “” )
      • Output: Charlie Ward
    • Lightweight( Country = “” )
      • Output: Tae Hyun Bang
    • Bantamweight( Country = “” )
      • Output: Alejandro Pérez
    • Flyweight( Country = “” )
      • Output: Ben Nguyen
    • Heavyweight( Country = “” )
      • Output: Francis Ngannou
    • LightHeavyweight( Country = “” )
      • Output: Paul Craig
    • Nickname( Fighter = “” )
      • Output: Abacus

Vamos criar uma classe para o usuário com um método que retorna seu nome de usuário, que será FirstName concatenado com LastName.

Class Production.User Extends %RegisteredObject

{


Property FirstName As %String;


Property LastName As %String;


Method Username() As %String

{

}


}

 

Class TDD.User Extends %UnitTest.TestCase

{


Method TestUsername()

{

  Set firstName = ##class(cosFaker.Name).FirstName(),

    lastName = ##class(cosFaker.Name).LastName(),

    user = ##class(Production.User).%New(),

    user.FirstName = firstName,

    user.LastName = lastName


  Do $$$AssertEquals(user.Username(), firstName _ "." _ lastName)

}


}

Refatorando:

Class Production.User Extends %RegisteredObject

{


Property FirstName As %String;


Property LastName As %String;


Method Username() As %String

{

  Quit ..FirstName _ "." _ ..LastName

}


}

Agora vamos adicionar uma data de expiração da conta e validá-la.

Class Production.User Extends %RegisteredObject

{


Property FirstName As %String;


Property LastName As %String;


Property AccountExpires As %Date;


Method Username() As %String

{

  Quit ..FirstName _ "." _ ..LastName

}


Method Expired() As %Boolean

{

}


}




Class TDD.User Extends %UnitTest.TestCase

{


Method TestUsername()

{

  Set firstName = ##class(cosFaker.Name).FirstName(),

    lastName = ##class(cosFaker.Name).LastName(),

    user = ##class(Production.User).%New(),

    user.FirstName = firstName,

    user.LastName = lastName

    Do $$$AssertEquals(user.Username(), firstName _ "." _ lastName)

}


Method TestWhenIsNotExpired() As %Status

{

  Set user = ##class(Production.User).%New(),

    user.AccountExpires = ##class(cosFaker.Dates).Forward(40)

  Do $$$AssertNotTrue(user.Expired())

}


}

Refatorando:

Method Expired() As %Boolean

{

  Quit ($system.SQL.DATEDIFF("dd", ..AccountExpires, +$Horolog) > 0)

}

Agora vamos testar quando a conta expirou:

Method TestWhenIsExpired() As %Status

{

  Set user = ##class(Production.User).%New(),

    user.AccountExpires = ##class(cosFaker.Dates).Backward(40)

  Do $$$AssertTrue(user.Expired())

}

E tudo está verde...

Eu sei que esses são exemplos bobos, mas dessa forma você verá a simplicidade não apenas no código, mas também no design da classe.
 

Conclusão

Neste artigo, você aprendeu um pouco sobre Desenvolvimento Guiado por Testes e como usar a classe %UnitTest.
Também cobrimos o cosFaker e como gerar dados falsos para seus testes.

Há muito mais para aprender sobre testes e TDD, como usar essas práticas com código legado, testes de integração, testes de aceitação (ATDD), bdd, etc... 
Se você quiser saber mais sobre isso, recomendo fortemente 2 livros:
 

Test Driven Development Teste e design no mundo real com Ruby - Mauricio Aniche, realmente não sei se este livro tem versão em inglês. Existem edições para Java, C #, Ruby e PHP. Este livro me surpreendeu com sua grandiosidade.

E, claro, o livro de Kent Beck Test Driven Development by Example

Sinta-se à vontade para deixar comentários ou perguntas.
Isso é tudo, pessoal

1
0 517