vccs
Composable charting components for Vue 3, ported from Recharts
EcoGrad — Ecologia do Conhecimento UFSC: redes complexas, foresight e memética da produção acadêmica.
Translation pending
DataTypeScript · JavaScriptGitHub @GSimasOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Painel de inteligência da produção acadêmica da UFSC
Translation pending
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Plataforma de cientometria, análise de redes sociais (SNA) e mineração de textos que mapeia a produção acadêmica da UFSC — teses, dissertações e trabalhos de conclusão — para revelar a estrutura latente do conhecimento: redes de colaboração, genealogia acadêmica, temas emergentes e o papel topológico de pesquisadores e conceitos.Read the full README ↗
Plataforma de cientometria, análise de redes sociais (SNA) e mineração de textos que mapeia a produção acadêmica da UFSC — teses, dissertações e trabalhos de conclusão — para revelar a estrutura latente do conhecimento: redes de colaboração, genealogia acadêmica, temas emergentes e o papel topológico de pesquisadores e conceitos.
Os dados vêm do Repositório Institucional da UFSC, que roda DSpace. O EcoGrad não produz dado novo: coleta, normaliza, indexa e reorganiza o que já está depositado lá, e todo trabalho preserva o link para a página original. O acervo atual tem 92.331 registros, 85.567 trabalhos distintos (a mesma obra pode estar catalogada em mais de uma coleção) e 254 coleções; 80.415 registros têm resumo utilizável.
Em produção: https://ecograd.netlify.app
A aplicação é a versão web, em ecograd-web/ — React 18 + Vite + TypeScript +
Tailwind, com Netlify Functions. Rode com cd ecograd-web && npm install && npm run netlify:dev;
detalhes no README da aplicação.
A raiz guarda o que alimenta e valida essa aplicação:
| Arquivo | Papel |
|---|---|
backend.py (+ app_config.py, gemini_utils.py) | Referência numérica do Python original, importada por npm run verify:parity |
pipeline_ufsc.py | Enriquecimento da base: macrotemas (NMF + Gemini), normalização, consolidação |
coleta/ | Coleta semanal do Repositório Institucional, executada pelo GitHub Actions |
base_*.json.gz, programas_ufsc.json, mapa_colecoes_tcc.json | Bases copiadas para ecograd-web/public/data/ no sync:data |
base_ppgegc.json | Base de referência da comparação de paridade |
ecograd-mcp/ | Servidor MCP do acervo, publicado no npm — o índice como ferramenta de assistente |
A versão web é uma migração de um app Streamlit. Toda a matemática de redes complexas, foresight
e memética foi transcrita e verificada numericamente contra o backend.py original — veja
Paridade. A interface Streamlit em si (Principal.py
e pages/) foi removida do repositório em favor da versão web; ela continua acessível no
histórico do git, até o commit cad47e3.
O projeto vai além da contagem de publicações. Usando teoria de sistemas complexos e grafos, mapeia a "ecologia do conhecimento" de ecossistemas acadêmicos para:
O painel lateral recolhe para uma faixa de ícones, e o estado fica salvo na sessão. O botão Sobre, na tela inicial, explica a procedência dos dados e como a plataforma funciona por baixo.
De qualquer página, Exportar relatório monta um arquivo com as seções do Dashboard e os dossiês visitados que você marcar. Tudo acontece no navegador; nada é enviado para servidor. Dois formatos:
secoes, com
indicadores e tabelas) e, além dele, os registros completos da análise em documentos:
título, ano, nível, autoria, orientação, palavras-chave, macrotema, coleção, link, resumo,
artefatos da ontologia e PDFs, com os mesmos nomes de campo da base de origem. Campos vazios não
aparecem, como na base. Os resumos podem ser desmarcados quando o arquivo precisa ser menor — a
tela estima o tamanho antes. Gráficos não atravessam: viram o nome da figura omitida, já que os
dados que os sustentam estão nas tabelas.Os dois carregam coleções, período, versão da base, data de geração e as ressalvas sobre os limites do recorte. Elas não são desmarcáveis: um arquivo circula longe da tela que as explica.
KPIs da base, comparativo entre PPGs quando há mais de um, ficha oficial da CAPES (cruzada com a Plataforma Sucupira por fuzzy matching, que resolve programas renomeados) e uma síntese do perfil epistemológico escrita por IA.
Todo nome exibido é clicável — chips de destaque e as barras dos Top 10 — e leva direto ao dossiê da entidade no Motor de Busca.
Nas nuvens de palavras, em qualquer tela, clicar num termo abre um convite em vez de agir: o modal diz quantas coleções seriam baixadas e quanto pesam, oferece explorar o termo no recorte já carregado quando ele aparece nele, e avisa quando o termo só existe dentro de títulos e resumos — sem ser palavra-chave, macrotema, pessoa ou título — e portanto não tem o que abrir.
Busca unificada por Documento, Autor, Orientador, Co-orientador, Palavra-chave e Macrotema. Cada entidade abre um dossiê com:
Uma página com quatro abas, agrupadas por pergunta de pesquisa. Só a aba aberta é montada, e os cálculos mais pesados têm botão próprio — eles seguem em segundo plano se você trocar de aba.
Temas e conceitos — o que existe na seleção.
Tempo e tendências — como o vocabulário mudou.
Estrutura da rede — como tudo se conecta.
echarts-gl): arraste para
girar, roda para aproximar, botão direito para deslocar. Cor pela comunidade do Louvain e escala
dos eixos linear (a do modelo original) ou logarítmica.Especialização e dados — comparar níveis e levar embora.
O UFSCão fala em duas superfícies, com a mesma persona, a mesma configuração e os mesmos limites:
| Onde | Sobre o quê | Como recupera |
|---|---|---|
| Conversar, na tela inicial | o acervo inteiro | índice Postgres: SQL para quem/quanto/quando, busca textual e vetorial para tema |
| Botão flutuante, nas páginas | as coleções carregadas | dossiê montado por pergunta a partir do recorte em memória |
A conversa iniciada na tela inicial acompanha você para o botão flutuante, visível e citada, com o recorte de cada resposta declarado — as duas telas respondem sobre corpora diferentes, e o prompt diz isso ao modelo em vez de misturar os números em silêncio.
BYOK. A chave de API é sua, fica no seu navegador e vai direto ao provedor escolhido (OpenAI, Anthropic, Google, Groq, OpenRouter ou qualquer serviço compatível com a API de chat da OpenAI). O EcoGrad não recebe nem guarda a conversa. Configure em Configurações ou dentro do próprio chat. A única chamada a modelo que o servidor faz é o embedding da pergunta, para a busca por significado.
O modelo só escreve. Toda contagem sai de SQL; a busca por similaridade escolhe o que ler, e
nunca responde "quantos". Cada afirmação sobre um trabalho leva a citação [n] da obra que a
sustenta, e cada [n] é um botão que abre a obra no Motor de Busca. Citação a fonte que não foi
enviada é apontada na tela.
Duas profundidades de leitura. A resposta padrão lê uma amostra representativa de 15 a 20 obras, espalhada por coleção. Aprofundar lê todas as obras do tema com resumo utilizável, em lotes — mostrando chamadas, tokens e tempo estimados antes de gastar a sua chave.
Guardrails (src/lib/guardrails.ts) valem nas duas telas,
num arquivo só para não divergirem:
SELECT, com teto de tempo e de linhas; não existe tabela de escrita alcançável pela chave
publicada; e a chave do modelo é do próprio usuário.Responder "como a UFSC trata tal tema?" sem baixar nada exige um lugar onde o acervo inteiro já
esteja indexado. Esse lugar é um Postgres (Supabase) com o texto dos resumos em FTS e um vetor
por obra em pgvector. Ele é derivado e reconstruível: os .json.gz da raiz continuam sendo
a fonte, e indice_meta guarda o sha256 das bases que o geraram — a aplicação compara com o
manifesto publicado e se recusa a responder sobre uma base que não é mais a da tela.
| Camada | O que faz |
|---|---|
buscar_texto, tesauro, consulta_expandida | recuperação léxica, com expansão de vocabulário tirada do próprio acervo |
documento_embedding (pgvector, HNSW) | busca por significado, para o tema escrito com outro vocabulário |
panorama_tematico | panorama exato do tema em SQL mais uma amostra representativa, com cota por coleção |
obras_do_tema, resumos_das_obras | a lista inteira do tema e os resumos em páginas, para o "Aprofundar" |
consulta.* + consultar() | views comentadas e execução somente leitura, que é como o modelo escreve SQL (NL2SQL) |
pessoa, pessoa_fusao, perfis, rede_metrica | pessoas unificadas automaticamente, perfis e métricas de rede pré-calculadas |
Contagem nunca sai de similaridade. O vetor escolhe o que ler; quem conta é SQL. Obra achada só por significado vai ao modelo marcada como aproximação e fora de qualquer total.
cd ecograd-web
npm run indice:derivar # gera os arquivos do índice a partir das bases
npm run indice:carregar # carrega por COPY (porta 5432)
npm run indice:carregar:https # ou por HTTPS, onde a 5432 está bloqueada
npm run indice:embeddings # vetores das obras (custo único, ~US$ 8)
indice/schema.sql é a definição canônica: aplicá-lo num Postgres vazio e rodar os comandos acima
reconstrói o índice do zero. Não há migração incremental de propósito — divergência silenciosa
entre base e índice é o modo de falha que o projeto quer evitar, e reconstruir custa minutos.
O índice já é uma API pública: o PostgREST do Supabase expõe cada função com grant … to anon
em /rest/v1/rpc/<função>, e a chave publicável é pública por desenho — é a mesma que o navegador
recebe ao abrir o site. Não há serviço próprio no meio, nem custo além do banco que já existe.
docs/API.md — endereço, chave, cada endpoint com argumentos e retorno, as views
consultáveis por SQL e os limites de uso.ecograd-mcp/ — os mesmos endpoints como servidor MCP, para usar o acervo dentro
de um assistente: claude mcp add ecograd -- npx -y ecograd-mcp. Roda na máquina de quem usa, por
stdio, sem nada para hospedar.Duas rotinas no GitHub Actions mantêm tudo em dia, e a segunda depende da primeira:
| Rotina | Quando | O que faz |
|---|---|---|
coleta-ufsc.yml | segundas, 03:00 (Brasília) | coleta os depósitos novos do repositório e commita um lote em coletas/ |
indice-supabase.yml | logo após a coleta, mais rede de segurança nos dias 1 e 15 | deriva o índice das bases mais os lotes, carrega no Supabase, enriquece e calcula os vetores das obras novas |
A segunda roda por workflow_run, e não num horário próprio: o índice tem que ser derivado das
mesmas bases que o site publicou, e amarrar pelo relógio deixaria as duas metades fora de fase na
semana em que a coleta demorasse. Sem lote novo não há commit, o workflow_run não dispara, e o
índice fica como está — que é o correto, porque nada mudou.
O último passo é indice:conferir, que falha o job se o
carimbo não bater com as contagens, se o enriquecimento não tiver entrado, se a cobertura de vetor
cair abaixo de 90% ou se panorama_tematico e obras_do_tema discordarem. Numa rotina automática,
falhar alto é melhor do que servir meio acervo em silêncio.
Segredos necessários no repositório: SUPABASE_DB_URL (pooler em modo sessão, porta 5432) e
GEMINI_API_KEY.
Por que os embeddings não custam US$ 8 por semana. documento_embedding referencia documento
com on delete cascade, e a recarga do índice levaria os 85 mil vetores junto — obrigando a
recalcular tudo a cada execução. Duas coisas evitam isso: a carga preserva os vetores dentro da
própria transação, devolvendo os que ainda têm obra correspondente; e indice:embeddings --incremental lê do banco o que já foi pago, em vez do cache local, que num runner começa
sempre vazio. Só obra nova ou com resumo alterado vai ao modelo.
Runner e qualidade do resumo. Em runner hospedado do GitHub, a coleta fica fora da RedeUFSC: o DSpace pede verificação anti-robô e ela cai no Oasisbr/IBICT, sem resumo. Como o resumo é o que alimenta a busca textual e os vetores, colher pelo DSpace exige um runner self-hosted na rede da UFSC (ou com VPN), apontado pela variável de repositório
COLETA_RUNNER.
O índice e o UFSCão foram construídos por etapas com porta de medição, e o plano mudou duas vezes porque a medição desmentiu a aposta inicial. Os ADRs registram isso, e o mais recente vale sobre os anteriores:
| Documento | O que decide |
|---|---|
| ADR 001 | o plano original do chat semântico, em cinco etapas |
| ADR 002 | recorte por item, e não por coleção inteira |
| ADR 003 | a lacuna era de indexação, não de semântica: indexar o resumo levou a revocação de 46% para 79%, sem vetor nenhum |
| ADR 004 | o UFSCão sobre o acervo inteiro, em quatro fases (A0 a C) |
| Aferição | as 25 perguntas, os gabaritos e todo número medido |
Recuperação medida contra a triagem humana assinada, em k = 25 (revocação / precisão):
| Método | "empreendedorismo feminino" | "mulheres empreendedoras" | Sobreposição |
|---|---|---|---|
| Busca por rótulo (o app antes do índice) | 46% / 100% | — | — |
Texto (buscar_texto) | 79% / 76% | 63% / 83% | 64% |
| Vetor | 79% / 95% | 79% / 100% | 96% |
| Híbrido (RRF) — o que vai à tela | 83% / 87% | 75% / 82% | 80% |
Limite declarado: há um único tema com gabarito humano assinado. Uma amostra cega de 8 temas
sorteados está congelada em docs/evidencias/afericao/, aguardando triagem — é ela que dirá se os
números acima generalizam.
npm run afericao # roda o conjunto de aferição contra os gabaritos
npm run afericao:vetor # compara texto, vetor e híbrido
npm run afericao:amostra-cega # sorteia temas para medir generalização
netlify.toml # na raiz, com base = "ecograd-web"
docs/ # ADRs e aferição: o porquê de cada decisão, com os números
ecograd-web/
├── indice/schema.sql # definição canônica do índice Postgres
├── netlify/functions/ # DeepSeek (síntese, ontologia), Gemini (embedding), proxy CAPES, Neo4j
├── scripts/ # derivação e carga do índice, aferição, paridade
├── src/lib/ # motores analíticos, prompts e guardrails, em TypeScript puro
├── src/workers/ # ingestão e redes complexas fora da main thread
└── src/components/ # UI React
Front-end — React 18, Vite, Tailwind, Zustand (estado), TanStack Query (cache), Radix UI, ECharts (gráficos e nuvem de palavras) e react-force-graph (grafos).
Motores analíticos — kernels próprios sobre estrutura CSR: Brandes (BFS e Dijkstra, com amostragem de pivôs), closeness com correção Wasserman-Faust, clustering simples e ponderado, PageRank, autovetor, constraint de Burt, rich-club, assortatividade e Louvain (graphology).
Web Workers — a ingestão descomprime .json.gz de 62 MB e filtra a seleção; o worker de rede
roda SNA, bootstrap, grid search e a rede memética. Sem isso a página congelaria por minutos.
Backend serverless — Netlify Functions em TypeScript. As chaves secretas vivem só no runtime das
funções, nunca no bundle. O neo4j-query não aceita Cypher do cliente: só o nome de uma consulta
pré-registrada e parâmetros, que viajam como binds do driver. O embedding-consulta é a única
chamada a modelo que o servidor faz por conta própria — o vetor da pergunta, para a busca por
significado — e é cercada: só aceita a própria origem, texto curto e 30 chamadas por minuto por IP.
O que o EcoGrad não faz — não guarda conversa, não recebe a sua chave de API, não registra quem perguntou o quê. A conversa do UFSCão vai do seu navegador direto ao provedor que você escolheu; o que fica no EcoGrad é só o que você vê na sessão, e some ao fechar a aba.
O backend.py já aproximava o betweenness acima de 1500 nós; o mesmo princípio foi estendido às
demais métricas O(n·m), porque a rede completa da UFSC passa de 100 mil nós. Abaixo dos limiares o
resultado é idêntico ao NetworkX. A tabela completa está no
README da aplicação.
cd ecograd-web && npm run verify:parity
O harness roda os dois motores sobre a mesma base e compara os resultados. Cobertura:
| Bloco | O que é comparado |
|---|---|
| Topologia | degree, betweenness, closeness e clustering — diferença relativa máxima 2e-16 |
| Métricas complexas | densidade, eficiência global, entropia, PageRank, autovetor, Burt |
| Rede de coocorrência | rich-club por grau (153 valores), clustering ponderado, assortatividade, γ |
| Maturidade | assortatividade, rich-club, γ por regressão log-log e por estimador MLE |
| Radar de Foresight | os 43 termos, com Momentum e Novidade — diferença exatamente zero |
| Backtest | as 89 linhas, distribuição por quadrante e os oito vereditos |
| Memética | memes, mortalidade, sobreviventes, longevidade, meia-vida |
| QL e Jaccard | valores idênticos |
Duas diferenças conhecidas, ambas sem efeito sobre os números: Louvain é estocástico (a comparação é por modularidade) e empates de contagem na memética têm ordem arbitrária no pandas, que usa quicksort instável — o TypeScript desempata por nome, o que torna o Top-N reprodutível.
O harness já pagou por si: foi ele que expôs o autovetor iterando A·x em vez do x + A·x do
NetworkX, e a varredura do rich-club ordenando as arestas pelo maior grau em vez do menor — erro
que produzia coeficientes acima de 1.
cd ecograd-web && npm install
Crie ecograd-web/.env a partir de .env.example e rode tudo (front-end + funções) em
http://localhost:8888:
npm run netlify:dev
Para trabalhar só na interface, sem as funções, use npm run dev. As bases da raiz são copiadas
para public/data/ automaticamente. Detalhes, comandos e notas de deploy no
README da aplicação.
| Variável | Onde vale | Para quê |
|---|---|---|
VITE_INDICE_URL, VITE_INDICE_CHAVE | build | endereço e chave publicável do índice. O Vite as grava no JS que vai ao navegador |
DEEPSEEK_API_KEY | runtime das funções | síntese, ontologia e a cortesia do UFSCão |
GEMINI_API_KEY | runtime das funções | o embedding da pergunta (o índice foi vetorizado com o gemini-embedding-2) |
SUPABASE_DB_URL ou SUPABASE_SERVICE_ROLE_KEY | só na carga, na sua máquina | reconstruir o índice. Nunca precisam existir no ambiente de build |
As duas VITE_* são públicas por desenho — o prefixo significa gravar o valor no bundle, e a
chave é a publicável (anon), com RLS somente leitura e nenhuma tabela de escrita alcançável. Por
isso o netlify.toml traz SECRETS_SCAN_OMIT_KEYS para as duas: sem isso o scanner de segredos da
Netlify reprova o build ao encontrá-las no próprio bundle que acabou de gerar. O scanner segue
ligado para todo o resto.
Por serem de build, mudá-las exige recompilar — republicar o mesmo artefato não adianta.
npm run test:all # suíte completa
npm run typecheck # tsc --noEmit
npm run build # sync de dados + typecheck + build de produção
npm run verify:parity # paridade numérica com o backend Python
A interface Streamlit foi removida; o que ficou na raiz é a referência numérica usada pela comparação de paridade e o pipeline de enriquecimento da base.
pip install -r requirements.txt
As credenciais são lidas de variáveis de ambiente (ou de .streamlit/secrets.toml, que
app_config.py ainda aceita):
GEMINI_API_KEY=...
NEO4J_URI=...
NEO4J_USERNAME=...
NEO4J_PASSWORD=...
A migração do app Streamlit (cujo último estado vive no histórico do git, até o commit cad47e3)
está concluída, com uma exceção declarada.
| Módulo original | Onde estava | Onde está agora | Status |
|---|---|---|---|
| Espaço Topológico 3D (Grau × Betweenness × Closeness) | Exploração Global | Análise Avançada · Estrutura da rede | ✅ portado |
| Mapa Temático (quadrantes de Callon / Bibliometrix) | Principal | Análise Avançada · Temas e conceitos | ✅ portado |
| Sankey Temporal de palavras-chave | Fluxos | Análise Avançada · Tempo e tendências | ✅ portado |
| Boxplot de Especialização (QL por nível) | Exploração Global | Análise Avançada · Especialização e dados | ✅ portado |
| Furos Estruturais de Burt (seção dedicada) | Exploração Global | Análise Avançada · Estrutura da rede | ✅ portado |
| Exportação da rede (GEXF / GraphML / JSON) | Exploração Global | Análise Avançada · Especialização e dados | ✅ portado |
| Tabela geral da base com métricas SNA | Principal | Análise Avançada · Especialização e dados | ✅ portado |
| Métricas de redes complexas do grafo global | Exploração Global | Análise Avançada · Estrutura da rede | ✅ portado |
| Órbita via Neo4j (Cypher) | Motor de Busca | — | ⛔ fora de escopo, por decisão de projeto |
Três diferenças deliberadas em relação ao original, todas declaradas na própria interface:
echarts-gl, com a mesma órbita livre do scatter_3d original.
A tela do WebGL é uma superfície própria e o download de imagem não a alcança: ali a vista em
tabela é o caminho para levar os dados embora, e também o caminho pelo teclado. Ele oferece
escala logarítmica além da linear do original, porque as três métricas têm cauda longa e a linear
empilha a maioria dos nós num canto do cubo; o nome do eixo declara quando a escala está em uso.#/exploracao, #/foresight e #/memetica continuam abrindo, cada um na aba que guarda o
conteúdo que prometia.A órbita via Neo4j é a única funcionalidade que não será portada: a função existe no histórico, mas o EcoGrad usa o grafo em memória e o JSON como fonte, sem banco de grafos. Ver docs/ARMAZENAMENTO-E-VERSIONAMENTO.md.
Ver LICENSE.
Desenvolvido por Gustavo Simas — github.com/GSimas