tormenta20 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +27 -0
- data/README.md +203 -12
- data/Rakefile +9 -0
- data/db/schema.sql +382 -0
- data/db/seeds.rb +377 -0
- data/db/tormenta20.sqlite3 +0 -0
- data/docs/armaduras.md +72 -0
- data/docs/armas.md +95 -0
- data/docs/classes.md +105 -0
- data/docs/divindades.md +77 -0
- data/docs/escudos.md +49 -0
- data/docs/itens.md +59 -0
- data/docs/magias.md +157 -0
- data/docs/materiais_especiais.md +50 -0
- data/docs/melhorias.md +55 -0
- data/docs/origens.md +61 -0
- data/docs/poderes.md +98 -0
- data/docs/regras.md +51 -0
- data/examples/database_usage.rb +155 -0
- data/src/json/classes/arcanista.json +86 -0
- data/src/json/classes/barbaro.json +85 -0
- data/src/json/classes/bardo.json +91 -0
- data/src/json/classes/bucaneiro.json +96 -0
- data/src/json/classes/cacador.json +96 -0
- data/src/json/classes/cavaleiro.json +87 -0
- data/src/json/classes/clerigo.json +82 -0
- data/src/json/classes/druida.json +88 -0
- data/src/json/classes/guerreiro.json +90 -0
- data/src/json/classes/inventor.json +100 -0
- data/src/json/classes/ladino.json +96 -0
- data/src/json/classes/lutador.json +90 -0
- data/src/json/classes/nobre.json +100 -0
- data/src/json/classes/paladino.json +93 -0
- data/src/json/deuses/aharadak.json +26 -0
- data/src/json/deuses/allihanna.json +26 -0
- data/src/json/deuses/arsenal.json +28 -0
- data/src/json/deuses/azgher.json +28 -0
- data/src/json/deuses/hyninn.json +28 -0
- data/src/json/deuses/kallyadranoch.json +28 -0
- data/src/json/deuses/khalmyr.json +27 -0
- data/src/json/deuses/lena.json +28 -0
- data/src/json/deuses/lin_wu.json +28 -0
- data/src/json/deuses/marah.json +28 -0
- data/src/json/deuses/megalokk.json +28 -0
- data/src/json/deuses/nimb.json +27 -0
- data/src/json/deuses/oceano.json +26 -0
- data/src/json/deuses/sszzaas.json +28 -0
- data/src/json/deuses/tanna_toh.json +28 -0
- data/src/json/deuses/tenebra.json +26 -0
- data/src/json/deuses/thwor.json +27 -0
- data/src/json/deuses/thyatis.json +26 -0
- data/src/json/deuses/valkaria.json +30 -0
- data/src/json/deuses/wynna.json +25 -0
- data/src/json/equipamentos/armaduras/leves/armadura_acolchoada.json +15 -0
- data/src/json/equipamentos/armaduras/leves/armadura_de_couro.json +13 -0
- data/src/json/equipamentos/armaduras/leves/couraca.json +13 -0
- data/src/json/equipamentos/armaduras/leves/couro_batido.json +13 -0
- data/src/json/equipamentos/armaduras/leves/gibao_de_peles.json +13 -0
- data/src/json/equipamentos/armaduras/pesadas/armadura_completa.json +19 -0
- data/src/json/equipamentos/armaduras/pesadas/brunea.json +17 -0
- data/src/json/equipamentos/armaduras/pesadas/cota_de_malha.json +17 -0
- data/src/json/equipamentos/armaduras/pesadas/loriga_segmentada.json +17 -0
- data/src/json/equipamentos/armaduras/pesadas/meia_armadura.json +17 -0
- data/src/json/equipamentos/armas/exoticas/chicote.json +23 -0
- data/src/json/equipamentos/armas/exoticas/corrente_de_espinhos.json +23 -0
- data/src/json/equipamentos/armas/exoticas/espada_bastarda.json +19 -0
- data/src/json/equipamentos/armas/exoticas/katana.json +19 -0
- data/src/json/equipamentos/armas/exoticas/machado_anao.json +19 -0
- data/src/json/equipamentos/armas/exoticas/machado_taurico.json +17 -0
- data/src/json/equipamentos/armas/exoticas/rede.json +41 -0
- data/src/json/equipamentos/armas/fogo/mosquete.json +21 -0
- data/src/json/equipamentos/armas/fogo/pistola.json +21 -0
- data/src/json/equipamentos/armas/marciais/alabarda.json +17 -0
- data/src/json/equipamentos/armas/marciais/alfange.json +17 -0
- data/src/json/equipamentos/armas/marciais/arco_longo.json +22 -0
- data/src/json/equipamentos/armas/marciais/besta_pesada.json +21 -0
- data/src/json/equipamentos/armas/marciais/cimitarra.json +17 -0
- data/src/json/equipamentos/armas/marciais/espada_longa.json +17 -0
- data/src/json/equipamentos/armas/marciais/florete.json +17 -0
- data/src/json/equipamentos/armas/marciais/gadanho.json +17 -0
- data/src/json/equipamentos/armas/marciais/lanca_montada.json +20 -0
- data/src/json/equipamentos/armas/marciais/machadinha.json +19 -0
- data/src/json/equipamentos/armas/marciais/machado_de_batalha.json +17 -0
- data/src/json/equipamentos/armas/marciais/machado_de_guerra.json +17 -0
- data/src/json/equipamentos/armas/marciais/mangual.json +21 -0
- data/src/json/equipamentos/armas/marciais/marreta.json +17 -0
- data/src/json/equipamentos/armas/marciais/martelo_de_guerra.json +17 -0
- data/src/json/equipamentos/armas/marciais/montante.json +17 -0
- data/src/json/equipamentos/armas/marciais/picareta.json +17 -0
- data/src/json/equipamentos/armas/marciais/tridente.json +22 -0
- data/src/json/equipamentos/armas/simples/adaga.json +21 -0
- data/src/json/equipamentos/armas/simples/arco_curto.json +21 -0
- data/src/json/equipamentos/armas/simples/azagaia.json +20 -0
- data/src/json/equipamentos/armas/simples/besta_leve.json +21 -0
- data/src/json/equipamentos/armas/simples/bordao.json +17 -0
- data/src/json/equipamentos/armas/simples/clava.json +17 -0
- data/src/json/equipamentos/armas/simples/espada_curta.json +17 -0
- data/src/json/equipamentos/armas/simples/foice.json +17 -0
- data/src/json/equipamentos/armas/simples/funda.json +23 -0
- data/src/json/equipamentos/armas/simples/lanca.json +19 -0
- data/src/json/equipamentos/armas/simples/maca.json +17 -0
- data/src/json/equipamentos/armas/simples/pique.json +17 -0
- data/src/json/equipamentos/armas/simples/tacape.json +17 -0
- data/src/json/equipamentos/escudos/escudo_leve.json +22 -0
- data/src/json/equipamentos/escudos/escudo_pesado.json +22 -0
- data/src/json/equipamentos/itens/alimentacao/batata_valkariana.json +14 -0
- data/src/json/equipamentos/itens/alimentacao/gorad_quente.json +12 -0
- data/src/json/equipamentos/itens/alimentacao/macarrao_de_yuvalin.json +12 -0
- data/src/json/equipamentos/itens/alimentacao/prato_do_aventureiro.json +13 -0
- data/src/json/equipamentos/itens/alimentacao/racao_de_viagem.json +14 -0
- data/src/json/equipamentos/itens/alimentacao/refeicao_comum.json +9 -0
- data/src/json/equipamentos/itens/alimentacao/sopa_de_peixe.json +13 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/baga_de_fogo.json +17 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/dente_de_dragao.json +17 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/essencia_abissal.json +22 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/liquen_lilas.json +18 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/musgo_purpura.json +18 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/ossos_de_monstro.json +17 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/po_de_cristal.json +18 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/po_de_giz.json +18 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/ramo_verdejante.json +16 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/saco_de_sal.json +18 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/seixo_de_ambar.json +18 -0
- data/src/json/equipamentos/itens/alquimicos/catalisadores/terra_de_cemiterio.json +18 -0
- data/src/json/equipamentos/itens/alquimicos/preparados/acido.json +19 -0
- data/src/json/equipamentos/itens/alquimicos/preparados/balsamo_restaurador.json +16 -0
- data/src/json/equipamentos/itens/alquimicos/preparados/bomba.json +30 -0
- data/src/json/equipamentos/itens/alquimicos/preparados/cosmetico.json +18 -0
- data/src/json/equipamentos/itens/alquimicos/preparados/elixir_do_amor.json +23 -0
- data/src/json/equipamentos/itens/alquimicos/preparados/essencia_de_mana.json +16 -0
- data/src/json/equipamentos/itens/alquimicos/preparados/fogo_alquimico.json +27 -0
- data/src/json/equipamentos/itens/alquimicos/preparados/po_do_desaparecimento.json +18 -0
- data/src/json/equipamentos/itens/alquimicos/venenos/beladona.json +22 -0
- data/src/json/equipamentos/itens/alquimicos/venenos/bruma_sonolenta.json +21 -0
- data/src/json/equipamentos/itens/alquimicos/venenos/cicuta.json +22 -0
- data/src/json/equipamentos/itens/alquimicos/venenos/essencia_de_sombra.json +20 -0
- data/src/json/equipamentos/itens/alquimicos/venenos/nevoa_toxica.json +22 -0
- data/src/json/equipamentos/itens/alquimicos/venenos/peconha_comum.json +24 -0
- data/src/json/equipamentos/itens/alquimicos/venenos/peconha_concentrada.json +22 -0
- data/src/json/equipamentos/itens/alquimicos/venenos/peconha_potente.json +22 -0
- data/src/json/equipamentos/itens/alquimicos/venenos/po_de_lich.json +23 -0
- data/src/json/equipamentos/itens/alquimicos/venenos/riso_de_nimb.json +21 -0
- data/src/json/equipamentos/itens/animais/alforje.json +11 -0
- data/src/json/equipamentos/itens/animais/cao_de_caca.json +18 -0
- data/src/json/equipamentos/itens/animais/cavalo.json +20 -0
- data/src/json/equipamentos/itens/animais/cavalo_de_guerra.json +17 -0
- data/src/json/equipamentos/itens/animais/estabulo.json +10 -0
- data/src/json/equipamentos/itens/animais/ponei.json +21 -0
- data/src/json/equipamentos/itens/animais/ponei_de_guerra.json +18 -0
- data/src/json/equipamentos/itens/animais/trobo.json +14 -0
- data/src/json/equipamentos/itens/aventura/agua_benta.json +23 -0
- data/src/json/equipamentos/itens/aventura/algemas.json +35 -0
- data/src/json/equipamentos/itens/aventura/arpeu.json +18 -0
- data/src/json/equipamentos/itens/aventura/bandoleira_de_pocoes.json +15 -0
- data/src/json/equipamentos/itens/aventura/barraca.json +14 -0
- data/src/json/equipamentos/itens/aventura/corda.json +26 -0
- data/src/json/equipamentos/itens/aventura/espelho.json +10 -0
- data/src/json/equipamentos/itens/aventura/lampiao.json +19 -0
- data/src/json/equipamentos/itens/aventura/mochila.json +12 -0
- data/src/json/equipamentos/itens/aventura/mochila_de_aventureiro.json +14 -0
- data/src/json/equipamentos/itens/aventura/oleo.json +19 -0
- data/src/json/equipamentos/itens/aventura/organizador_de_pergaminhos.json +14 -0
- data/src/json/equipamentos/itens/aventura/pe_de_cabra.json +18 -0
- data/src/json/equipamentos/itens/aventura/saco_de_dormir.json +13 -0
- data/src/json/equipamentos/itens/aventura/simbolo_sagrado.json +15 -0
- data/src/json/equipamentos/itens/aventura/tocha.json +28 -0
- data/src/json/equipamentos/itens/aventura/vara_de_madeira.json +12 -0
- data/src/json/equipamentos/itens/esotericos/bolsa_de_po.json +17 -0
- data/src/json/equipamentos/itens/esotericos/cajado_arcano.json +20 -0
- data/src/json/equipamentos/itens/esotericos/cetro_elemental.json +22 -0
- data/src/json/equipamentos/itens/esotericos/costela_de_lich.json +18 -0
- data/src/json/equipamentos/itens/esotericos/dedo_de_ente.json +21 -0
- data/src/json/equipamentos/itens/esotericos/luva_de_ferro.json +17 -0
- data/src/json/equipamentos/itens/esotericos/medalhao_de_prata.json +16 -0
- data/src/json/equipamentos/itens/esotericos/orbe_cristalino.json +15 -0
- data/src/json/equipamentos/itens/esotericos/tomo_hermetico.json +17 -0
- data/src/json/equipamentos/itens/esotericos/varinha_arcana.json +15 -0
- data/src/json/equipamentos/itens/ferramentas/alaude_elfico.json +16 -0
- data/src/json/equipamentos/itens/ferramentas/colecao_de_livros.json +16 -0
- data/src/json/equipamentos/itens/ferramentas/equipamento_de_viagem.json +19 -0
- data/src/json/equipamentos/itens/ferramentas/estojo_de_disfarces.json +18 -0
- data/src/json/equipamentos/itens/ferramentas/flauta_mistica.json +17 -0
- data/src/json/equipamentos/itens/ferramentas/gazua.json +18 -0
- data/src/json/equipamentos/itens/ferramentas/instrumento_musical.json +20 -0
- data/src/json/equipamentos/itens/ferramentas/instrumentos_de_oficio.json +21 -0
- data/src/json/equipamentos/itens/ferramentas/luneta.json +15 -0
- data/src/json/equipamentos/itens/ferramentas/maleta_de_medicamentos.json +17 -0
- data/src/json/equipamentos/itens/ferramentas/sela.json +19 -0
- data/src/json/equipamentos/itens/ferramentas/tambor_das_profundezas.json +17 -0
- data/src/json/equipamentos/itens/servicos/conducao.json +19 -0
- data/src/json/equipamentos/itens/servicos/curandeiro.json +9 -0
- data/src/json/equipamentos/itens/servicos/estadia.json +42 -0
- data/src/json/equipamentos/itens/servicos/magia.json +23 -0
- data/src/json/equipamentos/itens/servicos/mensageiro.json +10 -0
- data/src/json/equipamentos/itens/veiculos/balao_goblin.json +34 -0
- data/src/json/equipamentos/itens/veiculos/canoa.json +18 -0
- data/src/json/equipamentos/itens/veiculos/carroca.json +20 -0
- data/src/json/equipamentos/itens/veiculos/carruagem.json +21 -0
- data/src/json/equipamentos/itens/veiculos/veleiro.json +11 -0
- data/src/json/equipamentos/itens/vestuario/andrajos_de_aldeao.json +16 -0
- data/src/json/equipamentos/itens/vestuario/bandana.json +14 -0
- data/src/json/equipamentos/itens/vestuario/botas_reforcadas.json +15 -0
- data/src/json/equipamentos/itens/vestuario/camisa_bufante.json +14 -0
- data/src/json/equipamentos/itens/vestuario/capa_esvoacante.json +14 -0
- data/src/json/equipamentos/itens/vestuario/capa_pesada.json +14 -0
- data/src/json/equipamentos/itens/vestuario/casaco_longo.json +15 -0
- data/src/json/equipamentos/itens/vestuario/chapeu_arcano.json +15 -0
- data/src/json/equipamentos/itens/vestuario/enfeite_de_elmo.json +15 -0
- data/src/json/equipamentos/itens/vestuario/farrapos_de_ermitao.json +16 -0
- data/src/json/equipamentos/itens/vestuario/gorro_de_ervas.json +14 -0
- data/src/json/equipamentos/itens/vestuario/luva_de_pelica.json +14 -0
- data/src/json/equipamentos/itens/vestuario/manopla.json +15 -0
- data/src/json/equipamentos/itens/vestuario/manto_camuflado.json +20 -0
- data/src/json/equipamentos/itens/vestuario/manto_eclesiastico.json +14 -0
- data/src/json/equipamentos/itens/vestuario/robe_mistico.json +14 -0
- data/src/json/equipamentos/itens/vestuario/sapatos_de_camurca.json +14 -0
- data/src/json/equipamentos/itens/vestuario/tabardo.json +14 -0
- data/src/json/equipamentos/itens/vestuario/traje_da_corte.json +19 -0
- data/src/json/equipamentos/itens/vestuario/traje_de_viajante.json +13 -0
- data/src/json/equipamentos/itens/vestuario/veste_de_seda.json +14 -0
- data/src/json/equipamentos/municoes/balas.json +13 -0
- data/src/json/equipamentos/municoes/flechas.json +13 -0
- data/src/json/equipamentos/municoes/pedras.json +14 -0
- data/src/json/equipamentos/municoes/virotes.json +14 -0
- data/src/json/itens_superiores/materiais_especiais/aco_rubi.json +44 -0
- data/src/json/itens_superiores/materiais_especiais/adamante.json +35 -0
- data/src/json/itens_superiores/materiais_especiais/gelo_eterno.json +33 -0
- data/src/json/itens_superiores/materiais_especiais/madeira_tollon.json +31 -0
- data/src/json/itens_superiores/materiais_especiais/materia_vermelha.json +51 -0
- data/src/json/itens_superiores/materiais_especiais/mitral.json +38 -0
- data/src/json/itens_superiores/melhorias/armaduras_escudos/ajustada.json +11 -0
- data/src/json/itens_superiores/melhorias/armaduras_escudos/delicada.json +15 -0
- data/src/json/itens_superiores/melhorias/armaduras_escudos/espinhosa.json +15 -0
- data/src/json/itens_superiores/melhorias/armaduras_escudos/espinhoso.json +11 -0
- data/src/json/itens_superiores/melhorias/armaduras_escudos/polida.json +13 -0
- data/src/json/itens_superiores/melhorias/armaduras_escudos/reforcada.json +13 -0
- data/src/json/itens_superiores/melhorias/armaduras_escudos/selada.json +14 -0
- data/src/json/itens_superiores/melhorias/armaduras_escudos/sob_medida.json +13 -0
- data/src/json/itens_superiores/melhorias/armas/atroz.json +11 -0
- data/src/json/itens_superiores/melhorias/armas/certeira.json +11 -0
- data/src/json/itens_superiores/melhorias/armas/cruel.json +11 -0
- data/src/json/itens_superiores/melhorias/armas/equilibrada.json +11 -0
- data/src/json/itens_superiores/melhorias/armas/harmonizada.json +13 -0
- data/src/json/itens_superiores/melhorias/armas/injecao_alquimica.json +14 -0
- data/src/json/itens_superiores/melhorias/armas/macica.json +12 -0
- data/src/json/itens_superiores/melhorias/armas/mira_telescopica.json +16 -0
- data/src/json/itens_superiores/melhorias/armas/precisa.json +12 -0
- data/src/json/itens_superiores/melhorias/armas/pungente.json +11 -0
- data/src/json/itens_superiores/melhorias/esotericos/canalizador.json +11 -0
- data/src/json/itens_superiores/melhorias/esotericos/energetico.json +12 -0
- data/src/json/itens_superiores/melhorias/esotericos/harmonizado.json +16 -0
- data/src/json/itens_superiores/melhorias/esotericos/poderoso.json +11 -0
- data/src/json/itens_superiores/melhorias/esotericos/vigilante.json +11 -0
- data/src/json/itens_superiores/melhorias/ferramentas_vestuario/aprimorado.json +15 -0
- data/src/json/itens_superiores/melhorias/geral/banhado_a_ouro.json +17 -0
- data/src/json/itens_superiores/melhorias/geral/cravejado_de_gemas.json +12 -0
- data/src/json/itens_superiores/melhorias/geral/discreto.json +13 -0
- data/src/json/itens_superiores/melhorias/geral/macabro.json +12 -0
- data/src/json/itens_superiores/melhorias/geral/material_especial.json +13 -0
- data/src/json/origens/acolito.json +11 -0
- data/src/json/origens/amigo_dos_animais.json +11 -0
- data/src/json/origens/amnesico.json +10 -0
- data/src/json/origens/aristocrata.json +11 -0
- data/src/json/origens/artesao.json +11 -0
- data/src/json/origens/artista.json +11 -0
- data/src/json/origens/assistente_de_laboratorio.json +11 -0
- data/src/json/origens/batedor.json +11 -0
- data/src/json/origens/capanga.json +11 -0
- data/src/json/origens/charlatao.json +11 -0
- data/src/json/origens/circense.json +11 -0
- data/src/json/origens/criminoso.json +11 -0
- data/src/json/origens/curandeiro.json +11 -0
- data/src/json/origens/eremita.json +11 -0
- data/src/json/origens/escravo.json +11 -0
- data/src/json/origens/estudioso.json +11 -0
- data/src/json/origens/fazendeiro.json +11 -0
- data/src/json/origens/forasteiro.json +11 -0
- data/src/json/origens/gladiador.json +11 -0
- data/src/json/origens/guarda.json +11 -0
- data/src/json/origens/herdeiro.json +11 -0
- data/src/json/origens/heroi_campones.json +11 -0
- data/src/json/origens/marujo.json +11 -0
- data/src/json/origens/mateiro.json +11 -0
- data/src/json/origens/membro_de_guilda.json +11 -0
- data/src/json/origens/mercador.json +11 -0
- data/src/json/origens/minerador.json +11 -0
- data/src/json/origens/nomade.json +11 -0
- data/src/json/origens/pivete.json +11 -0
- data/src/json/origens/refugiado.json +11 -0
- data/src/json/origens/seguidor.json +11 -0
- data/src/json/origens/selvagem.json +11 -0
- data/src/json/origens/soldado.json +11 -0
- data/src/json/origens/taverneiro.json +11 -0
- data/src/json/origens/trabalhador.json +11 -0
- data/src/json/poderes/habilidades_unicas_de_origem/a_prova_de_tudo.json +14 -0
- data/src/json/poderes/habilidades_unicas_de_origem/agua_no_feijao.json +14 -0
- data/src/json/poderes/habilidades_unicas_de_origem/alpinista_social.json +9 -0
- data/src/json/poderes/habilidades_unicas_de_origem/amigo_especial.json +16 -0
- data/src/json/poderes/habilidades_unicas_de_origem/antigo_mestre.json +15 -0
- data/src/json/poderes/habilidades_unicas_de_origem/busca_interior.json +13 -0
- data/src/json/poderes/habilidades_unicas_de_origem/confissao.json +14 -0
- data/src/json/poderes/habilidades_unicas_de_origem/coracao_heroico.json +13 -0
- data/src/json/poderes/habilidades_unicas_de_origem/cultura_exotica.json +13 -0
- data/src/json/poderes/habilidades_unicas_de_origem/desejo_de_liberdade.json +12 -0
- data/src/json/poderes/habilidades_unicas_de_origem/detetive.json +14 -0
- data/src/json/poderes/habilidades_unicas_de_origem/dom_artistico.json +10 -0
- data/src/json/poderes/habilidades_unicas_de_origem/escavador.json +13 -0
- data/src/json/poderes/habilidades_unicas_de_origem/esforcado.json +13 -0
- data/src/json/poderes/habilidades_unicas_de_origem/esse_cheiro.json +14 -0
- data/src/json/poderes/habilidades_unicas_de_origem/estoico.json +16 -0
- data/src/json/poderes/habilidades_unicas_de_origem/frutos_do_trabalho.json +19 -0
- data/src/json/poderes/habilidades_unicas_de_origem/gororoba.json +12 -0
- data/src/json/poderes/habilidades_unicas_de_origem/heranca.json +13 -0
- data/src/json/poderes/habilidades_unicas_de_origem/influencia_militar.json +14 -0
- data/src/json/poderes/habilidades_unicas_de_origem/lembrancas_graduais.json +14 -0
- data/src/json/poderes/habilidades_unicas_de_origem/medico_de_campo.json +12 -0
- data/src/json/poderes/habilidades_unicas_de_origem/membro_da_igreja.json +14 -0
- data/src/json/poderes/habilidades_unicas_de_origem/mochileiro.json +9 -0
- data/src/json/poderes/habilidades_unicas_de_origem/negociacao.json +12 -0
- data/src/json/poderes/habilidades_unicas_de_origem/palpite_fundamentado.json +13 -0
- data/src/json/poderes/habilidades_unicas_de_origem/pao_e_circo.json +11 -0
- data/src/json/poderes/habilidades_unicas_de_origem/passagem_de_navio.json +13 -0
- data/src/json/poderes/habilidades_unicas_de_origem/punguista.json +15 -0
- data/src/json/poderes/habilidades_unicas_de_origem/quebra_galho.json +14 -0
- data/src/json/poderes/habilidades_unicas_de_origem/rede_de_contatos.json +14 -0
- data/src/json/poderes/habilidades_unicas_de_origem/sangue_azul.json +12 -0
- data/src/json/poderes/habilidades_unicas_de_origem/truque_de_magica.json +14 -0
- data/src/json/poderes/habilidades_unicas_de_origem/vendedor_de_carcacas.json +13 -0
- data/src/json/poderes/habilidades_unicas_de_origem/vida_rustica.json +13 -0
- data/src/json/poderes/poderes_concedidos/afinidade_com_a_tormenta.json +14 -0
- data/src/json/poderes/poderes_concedidos/almejar_o_impossivel.json +13 -0
- data/src/json/poderes/poderes_concedidos/anfibio.json +13 -0
- data/src/json/poderes/poderes_concedidos/apostar_com_o_trapaceiro.json +12 -0
- data/src/json/poderes/poderes_concedidos/armas_da_ambicao.json +12 -0
- data/src/json/poderes/poderes_concedidos/arsenal_das_profundezas.json +14 -0
- data/src/json/poderes/poderes_concedidos/astucia_da_serpente.json +14 -0
- data/src/json/poderes/poderes_concedidos/ataque_piedoso.json +13 -0
- data/src/json/poderes/poderes_concedidos/aura_de_medo.json +17 -0
- data/src/json/poderes/poderes_concedidos/aura_de_paz.json +18 -0
- data/src/json/poderes/poderes_concedidos/aura_restauradora.json +13 -0
- data/src/json/poderes/poderes_concedidos/bencao_do_mana.json +10 -0
- data/src/json/poderes/poderes_concedidos/caricia_sombria.json +26 -0
- data/src/json/poderes/poderes_concedidos/centelha_magica.json +13 -0
- data/src/json/poderes/poderes_concedidos/compreender_os_ermos.json +17 -0
- data/src/json/poderes/poderes_concedidos/conhecimento_enciclopedico.json +13 -0
- data/src/json/poderes/poderes_concedidos/conjurar_arma.json +19 -0
- data/src/json/poderes/poderes_concedidos/coragem_total.json +11 -0
- data/src/json/poderes/poderes_concedidos/cura_gentil.json +10 -0
- data/src/json/poderes/poderes_concedidos/curandeira_perfeita.json +15 -0
- data/src/json/poderes/poderes_concedidos/dedo_verde.json +14 -0
- data/src/json/poderes/poderes_concedidos/descanso_natural.json +10 -0
- data/src/json/poderes/poderes_concedidos/dom_da_esperanca.json +15 -0
- data/src/json/poderes/poderes_concedidos/dom_da_imortalidade.json +8 -0
- data/src/json/poderes/poderes_concedidos/dom_da_profecia.json +6 -0
- data/src/json/poderes/poderes_concedidos/dom_da_ressurreicao.json +8 -0
- data/src/json/poderes/poderes_concedidos/dom_da_verdade.json +15 -0
- data/src/json/poderes/poderes_concedidos/escamas_draconicas.json +13 -0
- data/src/json/poderes/poderes_concedidos/escudo_magico.json +12 -0
- data/src/json/poderes/poderes_concedidos/espada_justiceira.json +13 -0
- data/src/json/poderes/poderes_concedidos/espada_solar.json +16 -0
- data/src/json/poderes/poderes_concedidos/extase_da_loucura.json +12 -0
- data/src/json/poderes/poderes_concedidos/familiar_ofidico.json +13 -0
- data/src/json/poderes/poderes_concedidos/farsa_do_fingidor.json +14 -0
- data/src/json/poderes/poderes_concedidos/fe_guerreira.json +20 -0
- data/src/json/poderes/poderes_concedidos/forma_de_macaco.json +20 -0
- data/src/json/poderes/poderes_concedidos/fulgor_solar.json +18 -0
- data/src/json/poderes/poderes_concedidos/furia_divina.json +20 -0
- data/src/json/poderes/poderes_concedidos/golpista_divino.json +14 -0
- data/src/json/poderes/poderes_concedidos/habitante_do_deserto.json +14 -0
- data/src/json/poderes/poderes_concedidos/inimigo_de_tenebra.json +14 -0
- data/src/json/poderes/poderes_concedidos/kiai_divino.json +13 -0
- data/src/json/poderes/poderes_concedidos/liberdade_divina.json +12 -0
- data/src/json/poderes/poderes_concedidos/manto_da_penumbra.json +14 -0
- data/src/json/poderes/poderes_concedidos/mente_analitica.json +16 -0
- data/src/json/poderes/poderes_concedidos/mente_vazia.json +16 -0
- data/src/json/poderes/poderes_concedidos/mestre_dos_mares.json +18 -0
- data/src/json/poderes/poderes_concedidos/olhar_amedrontador.json +14 -0
- data/src/json/poderes/poderes_concedidos/palavras_de_bondade.json +14 -0
- data/src/json/poderes/poderes_concedidos/percepcao_temporal.json +17 -0
- data/src/json/poderes/poderes_concedidos/pesquisa_abencoada.json +15 -0
- data/src/json/poderes/poderes_concedidos/poder_oculto.json +21 -0
- data/src/json/poderes/poderes_concedidos/presas_primordiais.json +23 -0
- data/src/json/poderes/poderes_concedidos/presas_venenosas.json +16 -0
- data/src/json/poderes/poderes_concedidos/rejeicao_divina.json +13 -0
- data/src/json/poderes/poderes_concedidos/reparar_injustica.json +13 -0
- data/src/json/poderes/poderes_concedidos/sangue_de_ferro.json +13 -0
- data/src/json/poderes/poderes_concedidos/sangue_ofidico.json +14 -0
- data/src/json/poderes/poderes_concedidos/servos_do_dragao.json +24 -0
- data/src/json/poderes/poderes_concedidos/sopro_do_mar.json +25 -0
- data/src/json/poderes/poderes_concedidos/sorte_dos_loucos.json +13 -0
- data/src/json/poderes/poderes_concedidos/talento_artistico.json +14 -0
- data/src/json/poderes/poderes_concedidos/teurgista_mistico.json +11 -0
- data/src/json/poderes/poderes_concedidos/tradicao_de_lin_wu.json +14 -0
- data/src/json/poderes/poderes_concedidos/transmissao_da_loucura.json +17 -0
- data/src/json/poderes/poderes_concedidos/tropas_duyshidakk.json +24 -0
- data/src/json/poderes/poderes_concedidos/urro_divino.json +13 -0
- data/src/json/poderes/poderes_concedidos/visao_nas_trevas.json +10 -0
- data/src/json/poderes/poderes_concedidos/voz_da_civilizacao.json +10 -0
- data/src/json/poderes/poderes_concedidos/voz_da_natureza.json +18 -0
- data/src/json/poderes/poderes_concedidos/voz_dos_monstros.json +11 -0
- data/src/json/poderes/poderes_concedidos/zumbificar.json +18 -0
- data/src/json/poderes/poderes_da_tormenta/anatomia_insana.json +12 -0
- data/src/json/poderes/poderes_da_tormenta/antenas.json +18 -0
- data/src/json/poderes/poderes_da_tormenta/armamento_aberrante.json +14 -0
- data/src/json/poderes/poderes_da_tormenta/articulacoes_flexiveis.json +15 -0
- data/src/json/poderes/poderes_da_tormenta/asas_insetoides.json +17 -0
- data/src/json/poderes/poderes_da_tormenta/carapaca.json +12 -0
- data/src/json/poderes/poderes_da_tormenta/corpo_aberrante.json +13 -0
- data/src/json/poderes/poderes_da_tormenta/cuspir_enxame.json +20 -0
- data/src/json/poderes/poderes_da_tormenta/dentes_afiados.json +19 -0
- data/src/json/poderes/poderes_da_tormenta/desprezar_a_realidade.json +21 -0
- data/src/json/poderes/poderes_da_tormenta/empunhadura_rubra.json +13 -0
- data/src/json/poderes/poderes_da_tormenta/fome_de_mana.json +11 -0
- data/src/json/poderes/poderes_da_tormenta/larva_explosiva.json +17 -0
- data/src/json/poderes/poderes_da_tormenta/legiao_aberrante.json +19 -0
- data/src/json/poderes/poderes_da_tormenta/maos_membranosas.json +18 -0
- data/src/json/poderes/poderes_da_tormenta/membros_estendidos.json +12 -0
- data/src/json/poderes/poderes_da_tormenta/membros_extras.json +27 -0
- data/src/json/poderes/poderes_da_tormenta/mente_aberrante.json +20 -0
- data/src/json/poderes/poderes_da_tormenta/olhos_vermelhos.json +12 -0
- data/src/json/poderes/poderes_da_tormenta/pele_corrompida.json +13 -0
- data/src/json/poderes/poderes_da_tormenta/sangue_acido.json +13 -0
- data/src/json/poderes/poderes_da_tormenta/visco_rubro.json +15 -0
- data/src/json/regras/alcances.json +33 -0
- data/src/json/regras/carga.json +47 -0
- data/src/json/regras/critico.json +32 -0
- data/src/json/regras/dinheiro_inicial.json +27 -0
- data/src/json/regras/empunhaduras.json +22 -0
- data/src/json/regras/habilidades_armas.json +37 -0
- data/src/json/regras/itens_superiores.json +19 -0
- data/src/json/regras/limites_uso.json +22 -0
- data/src/json/regras/moedas.json +32 -0
- data/src/json/regras/passos_dano.json +20 -0
- data/src/json/regras/proficiencias.json +32 -0
- data/src/json/regras/propositos_armas.json +37 -0
- data/src/json/regras/tipos_dano.json +25 -0
- data/src/json/regras/venenos.json +45 -0
- data/src/ruby/tormenta20/database.rb +224 -0
- data/src/ruby/tormenta20/models/arma.rb +123 -0
- data/src/ruby/tormenta20/models/armadura.rb +96 -0
- data/src/ruby/tormenta20/models/base.rb +19 -0
- data/src/ruby/tormenta20/models/classe.rb +134 -0
- data/src/ruby/tormenta20/models/divindade.rb +98 -0
- data/src/ruby/tormenta20/models/escudo.rb +59 -0
- data/src/ruby/tormenta20/models/item.rb +61 -0
- data/src/ruby/tormenta20/models/magia.rb +226 -0
- data/src/ruby/tormenta20/models/material_especial.rb +52 -0
- data/src/ruby/tormenta20/models/melhoria.rb +52 -0
- data/src/ruby/tormenta20/models/origem.rb +71 -0
- data/src/ruby/tormenta20/models/poder.rb +120 -0
- data/src/ruby/tormenta20/models/regra.rb +47 -0
- data/src/ruby/tormenta20/seeder.rb +267 -0
- data/src/ruby/tormenta20/version.rb +1 -1
- data/src/ruby/tormenta20.rb +169 -7
- data/src/schema/magia.schema.json +277 -0
- data/src/schema/tormenta20.sql +433 -0
- data/tasks/jsonlint.rb +6 -4
- data/tools/pdf_extractor/README.md +171 -0
- data/tools/pdf_extractor/__init__.py +17 -0
- data/tools/pdf_extractor/pdf_extractor.py +441 -0
- data/tools/pdf_extractor/pipeline.py +450 -0
- data/tools/pdf_extractor/prompts.py +789 -0
- data/tools/pdf_extractor/requirements.txt +4 -0
- metadata +488 -12
- data/src/ruby/open_struct_json.rb +0 -35
- data/src/ruby/tormenta20/classes.rb +0 -0
- data/src/ruby/tormenta20/equipment.rb +0 -0
- data/src/ruby/tormenta20/magias.rb +0 -35
- data/src/ruby/tormenta20/origins.rb +0 -0
- data/src/ruby/tormenta20/races.rb +0 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pipeline principal para extração de dados do PDF do Tormenta 20.
|
|
3
|
+
|
|
4
|
+
Uso:
|
|
5
|
+
python pipeline.py <pdf_path> <section> <entity_type> [--model MODEL] [--output-dir DIR]
|
|
6
|
+
|
|
7
|
+
Exemplo:
|
|
8
|
+
python pipeline.py tormenta20.pdf racas racas --model mistral --output-dir ../../src/json/racas
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import json
|
|
13
|
+
import re
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
import requests
|
|
20
|
+
from tqdm import tqdm
|
|
21
|
+
|
|
22
|
+
from pdf_extractor import PDFExtractor, extract_entities, list_available_sections, ENTITY_PATTERNS
|
|
23
|
+
from prompts import get_prompt, PROMPTS
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# Configuração do Ollama
|
|
27
|
+
OLLAMA_URL = "http://localhost:11434/api/generate"
|
|
28
|
+
DEFAULT_MODEL = "mistral"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class LLMClient:
|
|
32
|
+
"""Cliente para comunicação com o Ollama."""
|
|
33
|
+
|
|
34
|
+
def __init__(self, model: str = DEFAULT_MODEL, base_url: str = OLLAMA_URL):
|
|
35
|
+
self.model = model
|
|
36
|
+
self.base_url = base_url
|
|
37
|
+
|
|
38
|
+
def check_connection(self) -> bool:
|
|
39
|
+
"""Verifica se o Ollama está rodando."""
|
|
40
|
+
try:
|
|
41
|
+
response = requests.get("http://localhost:11434/api/tags", timeout=5)
|
|
42
|
+
return response.status_code == 200
|
|
43
|
+
except requests.exceptions.ConnectionError:
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
def list_models(self) -> list[str]:
|
|
47
|
+
"""Lista modelos disponíveis."""
|
|
48
|
+
try:
|
|
49
|
+
response = requests.get("http://localhost:11434/api/tags", timeout=5)
|
|
50
|
+
if response.status_code == 200:
|
|
51
|
+
data = response.json()
|
|
52
|
+
return [m["name"] for m in data.get("models", [])]
|
|
53
|
+
except:
|
|
54
|
+
pass
|
|
55
|
+
return []
|
|
56
|
+
|
|
57
|
+
def generate(self, system_prompt: str, user_prompt: str, temperature: float = 0.1) -> str:
|
|
58
|
+
"""
|
|
59
|
+
Gera resposta do modelo.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
system_prompt: Prompt de sistema
|
|
63
|
+
user_prompt: Prompt do usuário
|
|
64
|
+
temperature: Temperatura (menor = mais determinístico)
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Resposta do modelo
|
|
68
|
+
"""
|
|
69
|
+
payload = {
|
|
70
|
+
"model": self.model,
|
|
71
|
+
"prompt": user_prompt,
|
|
72
|
+
"system": system_prompt,
|
|
73
|
+
"stream": False,
|
|
74
|
+
"options": {
|
|
75
|
+
"temperature": temperature,
|
|
76
|
+
"num_predict": 4096, # Máximo de tokens na resposta
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
response = requests.post(self.base_url, json=payload, timeout=120)
|
|
82
|
+
response.raise_for_status()
|
|
83
|
+
data = response.json()
|
|
84
|
+
return data.get("response", "")
|
|
85
|
+
except requests.exceptions.Timeout:
|
|
86
|
+
raise TimeoutError("Timeout ao gerar resposta do modelo")
|
|
87
|
+
except requests.exceptions.RequestException as e:
|
|
88
|
+
raise ConnectionError(f"Erro de conexão com Ollama: {e}")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def extract_json_from_response(response: str) -> Optional[dict]:
|
|
92
|
+
"""
|
|
93
|
+
Extrai JSON de uma resposta do modelo.
|
|
94
|
+
|
|
95
|
+
Tenta encontrar JSON válido mesmo se houver texto extra.
|
|
96
|
+
"""
|
|
97
|
+
# Remove markdown code blocks se presentes
|
|
98
|
+
response = re.sub(r'^```json\s*', '', response, flags=re.MULTILINE)
|
|
99
|
+
response = re.sub(r'^```\s*$', '', response, flags=re.MULTILINE)
|
|
100
|
+
response = response.strip()
|
|
101
|
+
|
|
102
|
+
# Tenta parsear diretamente
|
|
103
|
+
try:
|
|
104
|
+
return json.loads(response)
|
|
105
|
+
except json.JSONDecodeError:
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
# Tenta encontrar JSON no texto
|
|
109
|
+
json_patterns = [
|
|
110
|
+
r'\{[\s\S]*\}', # Objeto JSON
|
|
111
|
+
r'\[[\s\S]*\]', # Array JSON
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
for pattern in json_patterns:
|
|
115
|
+
matches = re.findall(pattern, response)
|
|
116
|
+
for match in matches:
|
|
117
|
+
try:
|
|
118
|
+
return json.loads(match)
|
|
119
|
+
except json.JSONDecodeError:
|
|
120
|
+
continue
|
|
121
|
+
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def slugify(text: str) -> str:
|
|
126
|
+
"""Converte texto para slug."""
|
|
127
|
+
replacements = {
|
|
128
|
+
'á': 'a', 'à': 'a', 'ã': 'a', 'â': 'a', 'ä': 'a',
|
|
129
|
+
'é': 'e', 'è': 'e', 'ê': 'e', 'ë': 'e',
|
|
130
|
+
'í': 'i', 'ì': 'i', 'î': 'i', 'ï': 'i',
|
|
131
|
+
'ó': 'o', 'ò': 'o', 'õ': 'o', 'ô': 'o', 'ö': 'o',
|
|
132
|
+
'ú': 'u', 'ù': 'u', 'û': 'u', 'ü': 'u',
|
|
133
|
+
'ç': 'c', 'ñ': 'n'
|
|
134
|
+
}
|
|
135
|
+
text = text.lower()
|
|
136
|
+
for char, replacement in replacements.items():
|
|
137
|
+
text = text.replace(char, replacement)
|
|
138
|
+
|
|
139
|
+
text = re.sub(r'[^a-z0-9\s]', '', text)
|
|
140
|
+
text = re.sub(r'\s+', '_', text.strip())
|
|
141
|
+
return text
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def validate_json_structure(data: dict, entity_type: str) -> tuple[bool, list[str]]:
|
|
145
|
+
"""
|
|
146
|
+
Valida a estrutura básica do JSON.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
Tupla (é_válido, lista_de_erros)
|
|
150
|
+
"""
|
|
151
|
+
errors = []
|
|
152
|
+
|
|
153
|
+
# Campos obrigatórios para todos os tipos
|
|
154
|
+
required_fields = ["id", "name"]
|
|
155
|
+
for field in required_fields:
|
|
156
|
+
if field not in data:
|
|
157
|
+
errors.append(f"Campo obrigatório ausente: {field}")
|
|
158
|
+
|
|
159
|
+
# Validações específicas por tipo
|
|
160
|
+
if entity_type == "racas":
|
|
161
|
+
if "attributes" not in data:
|
|
162
|
+
errors.append("Campo 'attributes' ausente")
|
|
163
|
+
if "abilities" not in data:
|
|
164
|
+
errors.append("Campo 'abilities' ausente")
|
|
165
|
+
|
|
166
|
+
elif entity_type == "classes":
|
|
167
|
+
if "hit_points" not in data:
|
|
168
|
+
errors.append("Campo 'hit_points' ausente")
|
|
169
|
+
if "skills" not in data:
|
|
170
|
+
errors.append("Campo 'skills' ausente")
|
|
171
|
+
|
|
172
|
+
elif entity_type == "origens":
|
|
173
|
+
if "benefits" not in data:
|
|
174
|
+
errors.append("Campo 'benefits' ausente")
|
|
175
|
+
|
|
176
|
+
elif entity_type == "divindades":
|
|
177
|
+
if "channel_energy" not in data:
|
|
178
|
+
errors.append("Campo 'channel_energy' ausente")
|
|
179
|
+
if "granted_powers" not in data:
|
|
180
|
+
errors.append("Campo 'granted_powers' ausente")
|
|
181
|
+
|
|
182
|
+
elif entity_type == "pericias":
|
|
183
|
+
if "key_attribute" not in data:
|
|
184
|
+
errors.append("Campo 'key_attribute' ausente")
|
|
185
|
+
if "uses" not in data:
|
|
186
|
+
errors.append("Campo 'uses' ausente")
|
|
187
|
+
|
|
188
|
+
elif entity_type == "magias":
|
|
189
|
+
if "type" not in data:
|
|
190
|
+
errors.append("Campo 'type' ausente")
|
|
191
|
+
if "circle" not in data:
|
|
192
|
+
errors.append("Campo 'circle' ausente")
|
|
193
|
+
|
|
194
|
+
return len(errors) == 0, errors
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def process_entity(
|
|
198
|
+
client: LLMClient,
|
|
199
|
+
entity_content: str,
|
|
200
|
+
entity_type: str,
|
|
201
|
+
retries: int = 2
|
|
202
|
+
) -> tuple[Optional[dict], list[str]]:
|
|
203
|
+
"""
|
|
204
|
+
Processa uma entidade e retorna o JSON.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
client: Cliente LLM
|
|
208
|
+
entity_content: Conteúdo textual da entidade
|
|
209
|
+
entity_type: Tipo de entidade
|
|
210
|
+
retries: Número de tentativas
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
Tupla (json_data ou None, lista_de_erros)
|
|
214
|
+
"""
|
|
215
|
+
errors = []
|
|
216
|
+
|
|
217
|
+
for attempt in range(retries + 1):
|
|
218
|
+
try:
|
|
219
|
+
system_prompt, user_prompt = get_prompt(entity_type, entity_content)
|
|
220
|
+
response = client.generate(system_prompt, user_prompt)
|
|
221
|
+
|
|
222
|
+
json_data = extract_json_from_response(response)
|
|
223
|
+
if json_data is None:
|
|
224
|
+
errors.append(f"Tentativa {attempt + 1}: Não foi possível extrair JSON da resposta")
|
|
225
|
+
continue
|
|
226
|
+
|
|
227
|
+
is_valid, validation_errors = validate_json_structure(json_data, entity_type)
|
|
228
|
+
if not is_valid:
|
|
229
|
+
errors.extend([f"Tentativa {attempt + 1}: {e}" for e in validation_errors])
|
|
230
|
+
continue
|
|
231
|
+
|
|
232
|
+
return json_data, []
|
|
233
|
+
|
|
234
|
+
except Exception as e:
|
|
235
|
+
errors.append(f"Tentativa {attempt + 1}: {str(e)}")
|
|
236
|
+
|
|
237
|
+
if attempt < retries:
|
|
238
|
+
time.sleep(1) # Pequena pausa entre tentativas
|
|
239
|
+
|
|
240
|
+
return None, errors
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def run_pipeline(
|
|
244
|
+
pdf_path: str,
|
|
245
|
+
section: str,
|
|
246
|
+
entity_type: str,
|
|
247
|
+
model: str = DEFAULT_MODEL,
|
|
248
|
+
output_dir: str = None,
|
|
249
|
+
dry_run: bool = False,
|
|
250
|
+
toc_start: int = 3,
|
|
251
|
+
toc_end: int = 6
|
|
252
|
+
) -> dict:
|
|
253
|
+
"""
|
|
254
|
+
Executa o pipeline completo.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
pdf_path: Caminho para o PDF
|
|
258
|
+
section: Seção do índice ou slug
|
|
259
|
+
entity_type: Tipo de entidade
|
|
260
|
+
model: Modelo LLM a usar
|
|
261
|
+
output_dir: Diretório de saída
|
|
262
|
+
dry_run: Se True, não salva arquivos
|
|
263
|
+
toc_start: Página inicial do índice
|
|
264
|
+
toc_end: Página final do índice
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Estatísticas de execução
|
|
268
|
+
"""
|
|
269
|
+
stats = {
|
|
270
|
+
"total": 0,
|
|
271
|
+
"success": 0,
|
|
272
|
+
"failed": 0,
|
|
273
|
+
"errors": []
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
# Verificar tipo de entidade
|
|
277
|
+
if entity_type not in PROMPTS:
|
|
278
|
+
print(f"Erro: Tipo de entidade '{entity_type}' desconhecido.")
|
|
279
|
+
print(f"Tipos disponíveis: {list(PROMPTS.keys())}")
|
|
280
|
+
sys.exit(1)
|
|
281
|
+
|
|
282
|
+
# Inicializar cliente LLM
|
|
283
|
+
client = LLMClient(model=model)
|
|
284
|
+
|
|
285
|
+
if not client.check_connection():
|
|
286
|
+
print("Erro: Ollama não está rodando.")
|
|
287
|
+
print("Inicie com: ollama serve")
|
|
288
|
+
sys.exit(1)
|
|
289
|
+
|
|
290
|
+
available_models = client.list_models()
|
|
291
|
+
if model not in [m.split(":")[0] for m in available_models]:
|
|
292
|
+
print(f"Aviso: Modelo '{model}' pode não estar instalado.")
|
|
293
|
+
print(f"Modelos disponíveis: {available_models}")
|
|
294
|
+
print(f"Instale com: ollama pull {model}")
|
|
295
|
+
|
|
296
|
+
# Preparar diretório de saída
|
|
297
|
+
if output_dir:
|
|
298
|
+
output_path = Path(output_dir)
|
|
299
|
+
else:
|
|
300
|
+
output_path = Path(__file__).parent.parent.parent / "src" / "json" / entity_type
|
|
301
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
302
|
+
|
|
303
|
+
print(f"\n{'='*60}")
|
|
304
|
+
print(f"Pipeline de Extração - Tormenta 20")
|
|
305
|
+
print(f"{'='*60}")
|
|
306
|
+
print(f"PDF: {pdf_path}")
|
|
307
|
+
print(f"Seção: {section}")
|
|
308
|
+
print(f"Tipo: {entity_type}")
|
|
309
|
+
print(f"Modelo: {model}")
|
|
310
|
+
print(f"Saída: {output_path}")
|
|
311
|
+
print(f"{'='*60}\n")
|
|
312
|
+
|
|
313
|
+
# Extrair entidades
|
|
314
|
+
print("Extraindo entidades do PDF...")
|
|
315
|
+
try:
|
|
316
|
+
entities = extract_entities(pdf_path, section, entity_type)
|
|
317
|
+
except ValueError as e:
|
|
318
|
+
print(f"Erro: {e}")
|
|
319
|
+
print("\nListando seções disponíveis...")
|
|
320
|
+
sections = list_available_sections(pdf_path, toc_start, toc_end)
|
|
321
|
+
for slug, info in sections.items():
|
|
322
|
+
print(f" {slug}: {info['title']} (págs. {info['start_page']}-{info['end_page']})")
|
|
323
|
+
sys.exit(1)
|
|
324
|
+
|
|
325
|
+
stats["total"] = len(entities)
|
|
326
|
+
print(f"Encontradas {len(entities)} entidades.\n")
|
|
327
|
+
|
|
328
|
+
if dry_run:
|
|
329
|
+
print("Modo dry-run: mostrando entidades encontradas")
|
|
330
|
+
for entity in entities:
|
|
331
|
+
print(f" - {entity['header']}")
|
|
332
|
+
return stats
|
|
333
|
+
|
|
334
|
+
# Processar cada entidade
|
|
335
|
+
print("Processando entidades...\n")
|
|
336
|
+
for entity in tqdm(entities, desc="Processando"):
|
|
337
|
+
header = entity["header"]
|
|
338
|
+
content = entity["content"]
|
|
339
|
+
|
|
340
|
+
json_data, errors = process_entity(client, content, entity_type)
|
|
341
|
+
|
|
342
|
+
if json_data:
|
|
343
|
+
# Garantir que o ID está correto
|
|
344
|
+
if "id" not in json_data or not json_data["id"]:
|
|
345
|
+
json_data["id"] = slugify(header)
|
|
346
|
+
|
|
347
|
+
# Salvar arquivo
|
|
348
|
+
filename = f"{json_data['id']}.json"
|
|
349
|
+
filepath = output_path / filename
|
|
350
|
+
|
|
351
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
352
|
+
json.dump(json_data, f, ensure_ascii=False, indent=2)
|
|
353
|
+
|
|
354
|
+
stats["success"] += 1
|
|
355
|
+
tqdm.write(f"✓ {header} -> {filename}")
|
|
356
|
+
else:
|
|
357
|
+
stats["failed"] += 1
|
|
358
|
+
stats["errors"].append({
|
|
359
|
+
"entity": header,
|
|
360
|
+
"errors": errors
|
|
361
|
+
})
|
|
362
|
+
tqdm.write(f"✗ {header}: {errors[-1] if errors else 'Erro desconhecido'}")
|
|
363
|
+
|
|
364
|
+
# Relatório final
|
|
365
|
+
print(f"\n{'='*60}")
|
|
366
|
+
print("Relatório Final")
|
|
367
|
+
print(f"{'='*60}")
|
|
368
|
+
print(f"Total processado: {stats['total']}")
|
|
369
|
+
print(f"Sucesso: {stats['success']}")
|
|
370
|
+
print(f"Falhas: {stats['failed']}")
|
|
371
|
+
|
|
372
|
+
if stats["errors"]:
|
|
373
|
+
print(f"\nEntidades com erro:")
|
|
374
|
+
for error in stats["errors"]:
|
|
375
|
+
print(f" - {error['entity']}")
|
|
376
|
+
for e in error["errors"]:
|
|
377
|
+
print(f" {e}")
|
|
378
|
+
|
|
379
|
+
return stats
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def main():
|
|
383
|
+
parser = argparse.ArgumentParser(
|
|
384
|
+
description="Pipeline de extração de dados do Tormenta 20",
|
|
385
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
386
|
+
epilog="""
|
|
387
|
+
Exemplos:
|
|
388
|
+
# Listar seções disponíveis no PDF
|
|
389
|
+
python pipeline.py tormenta20.pdf --list-sections
|
|
390
|
+
|
|
391
|
+
# Extrair todas as raças
|
|
392
|
+
python pipeline.py tormenta20.pdf racas racas
|
|
393
|
+
|
|
394
|
+
# Extrair classes usando modelo específico
|
|
395
|
+
python pipeline.py tormenta20.pdf classes classes --model llama3
|
|
396
|
+
|
|
397
|
+
# Dry-run para ver entidades sem processar
|
|
398
|
+
python pipeline.py tormenta20.pdf magias magias --dry-run
|
|
399
|
+
|
|
400
|
+
Tipos de entidade disponíveis:
|
|
401
|
+
racas, classes, origens, divindades, pericias, magias, poderes
|
|
402
|
+
"""
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
parser.add_argument("pdf_path", help="Caminho para o PDF do Tormenta 20")
|
|
406
|
+
parser.add_argument("section", nargs="?", help="Seção do índice para extrair")
|
|
407
|
+
parser.add_argument("entity_type", nargs="?", help="Tipo de entidade")
|
|
408
|
+
|
|
409
|
+
parser.add_argument("--model", "-m", default=DEFAULT_MODEL,
|
|
410
|
+
help=f"Modelo LLM a usar (padrão: {DEFAULT_MODEL})")
|
|
411
|
+
parser.add_argument("--output-dir", "-o",
|
|
412
|
+
help="Diretório de saída para os JSONs")
|
|
413
|
+
parser.add_argument("--dry-run", "-n", action="store_true",
|
|
414
|
+
help="Não processa, apenas mostra entidades encontradas")
|
|
415
|
+
parser.add_argument("--list-sections", "-l", action="store_true",
|
|
416
|
+
help="Lista seções disponíveis no PDF")
|
|
417
|
+
parser.add_argument("--toc-start", type=int, default=3,
|
|
418
|
+
help="Página inicial do índice (padrão: 3)")
|
|
419
|
+
parser.add_argument("--toc-end", type=int, default=6,
|
|
420
|
+
help="Página final do índice (padrão: 6)")
|
|
421
|
+
|
|
422
|
+
args = parser.parse_args()
|
|
423
|
+
|
|
424
|
+
if args.list_sections:
|
|
425
|
+
sections = list_available_sections(args.pdf_path, args.toc_start, args.toc_end)
|
|
426
|
+
print("\nSeções disponíveis:")
|
|
427
|
+
print("=" * 60)
|
|
428
|
+
for slug, info in sections.items():
|
|
429
|
+
print(f"{slug}: {info['title']} (págs. {info['start_page']}-{info['end_page']})")
|
|
430
|
+
sys.exit(0)
|
|
431
|
+
|
|
432
|
+
if not args.section or not args.entity_type:
|
|
433
|
+
parser.print_help()
|
|
434
|
+
print("\nErro: section e entity_type são obrigatórios")
|
|
435
|
+
sys.exit(1)
|
|
436
|
+
|
|
437
|
+
run_pipeline(
|
|
438
|
+
pdf_path=args.pdf_path,
|
|
439
|
+
section=args.section,
|
|
440
|
+
entity_type=args.entity_type,
|
|
441
|
+
model=args.model,
|
|
442
|
+
output_dir=args.output_dir,
|
|
443
|
+
dry_run=args.dry_run,
|
|
444
|
+
toc_start=args.toc_start,
|
|
445
|
+
toc_end=args.toc_end
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
if __name__ == "__main__":
|
|
450
|
+
main()
|