Importer
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Command\Pf2eSchemaGenerator\Schema;
|
||||
use App\Command\Pf2eSchemaGenerator\SchemaItem;
|
||||
use App\Service\AonprdConnectionService;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'pf2e:generate',
|
||||
description: 'Add a short description for your command',
|
||||
)]
|
||||
class Pf2eGenerateCommand extends Command
|
||||
{
|
||||
public function __construct(private AonprdConnectionService $connectionService)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addOption("revision", null, InputOption::VALUE_REQUIRED, "Load index of specific revision. Default value - latest revision.")
|
||||
->addOption('type', null, InputOption::VALUE_REQUIRED, 'Specify types to import schema')
|
||||
->addOption('all', null, InputOption::VALUE_NONE, 'Generate schema for all types')
|
||||
;
|
||||
}
|
||||
|
||||
private int $revision;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private array $types;
|
||||
|
||||
/**
|
||||
* @var array<string, Schema>
|
||||
*/
|
||||
private array $schemas = [];
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$this->revision = $input->getOption('revision') ?? $this->connectionService->getRevision();
|
||||
|
||||
if ($input->getOption('all')) {
|
||||
$this->types = [];
|
||||
}
|
||||
else if (is_null($input->getOption('type'))) {
|
||||
// $io->error("You must specify --type=[TYPE] or --all");
|
||||
// return Command::INVALID;
|
||||
$this->types = ["class"];
|
||||
}
|
||||
else {
|
||||
$this->types = explode(",", $input->getOption('type'));
|
||||
}
|
||||
|
||||
$typePages = $this->loadTypePages();
|
||||
|
||||
if ($input->getOption('all')) {
|
||||
$this->types = array_keys($typePages);
|
||||
}
|
||||
|
||||
$pages = [];
|
||||
|
||||
foreach ($this->types as $type) {
|
||||
$pages = [...$pages, ...$typePages[$type]];
|
||||
$this->schemas[$type] = new Schema();
|
||||
}
|
||||
|
||||
$pages = array_unique($pages);
|
||||
|
||||
foreach ($pages as $page) {
|
||||
$this->loadPage($page);
|
||||
}
|
||||
|
||||
foreach ($this->schemas as $type => $schema) {
|
||||
$io->write("<options=bold,underscore>{$type}</>");
|
||||
$io->writeln(str_replace(PHP_EOL, PHP_EOL.str_repeat(" ", 4), PHP_EOL.$schema));
|
||||
$io->writeln("");
|
||||
}
|
||||
|
||||
$io->success('You have a new command! Now make it your own! Pass --help to see your options.');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
protected function loadTypePages(): array
|
||||
{
|
||||
$typePages = [];
|
||||
|
||||
$index = $this->connectionService->loadCurrentIndex($this->revision);
|
||||
|
||||
foreach ($index as $page => $elements) {
|
||||
foreach ($elements as $element) {
|
||||
preg_match("#^([a-z\\-]+?)-(\\d.*)$#", $element, $matches);
|
||||
$type = $matches[1];
|
||||
|
||||
if (!empty($targetType) && !in_array($type, $this->types)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($typePages[$type])) {
|
||||
$typePages[$type] = [];
|
||||
}
|
||||
|
||||
if (!in_array($page, $typePages[$type])) {
|
||||
$typePages[$type][] = $page;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $typePages;
|
||||
}
|
||||
|
||||
protected function loadPage(string $page): void {
|
||||
$data = $this->connectionService->load("/json-data/{$page}.json");
|
||||
|
||||
foreach ($data as $element) {
|
||||
$type = $element["category"];
|
||||
|
||||
if (!in_array($type, $this->types)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->schemas[$type]->process($element);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Service\AonprdConnectionService;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: "pf2e:revision",
|
||||
description: "Get current revision number of Archives of Nethys",
|
||||
)]
|
||||
class Pf2eIndexCommand extends Command
|
||||
{
|
||||
public function __construct(private AonprdConnectionService $connectionService)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$revision = $this->connectionService->getRevision();
|
||||
$io->success("Current Archives of Nethys' revision number is {$revision}");
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command\Pf2eSchemaGenerator;
|
||||
|
||||
class Schema
|
||||
{
|
||||
/**
|
||||
* @var SchemaItem[]
|
||||
*/
|
||||
private array $items = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function process(mixed $element): void
|
||||
{
|
||||
$keys = array_keys($element);
|
||||
|
||||
foreach ($this->items as $key => $item) {
|
||||
if (!in_array($key, $keys)) {
|
||||
$item->disableRequired();
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (!array_key_exists($key, $this->items)) {
|
||||
$this->items[$key] = new SchemaItem($key);
|
||||
}
|
||||
|
||||
$this->items[$key]->adapt($element[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static function resolveType(mixed $value): string
|
||||
{
|
||||
$type = gettype($value);
|
||||
|
||||
if ($type === "array") {
|
||||
if (empty($value)) {
|
||||
return "empty[]";
|
||||
}
|
||||
|
||||
if (array_is_list($value)) {
|
||||
$elementType = "";
|
||||
foreach ($value as $element) {
|
||||
$elementType = self::mergeTypes($elementType, self::resolveType($element));
|
||||
}
|
||||
|
||||
if (str_contains("|", $elementType)) {
|
||||
return "mixed[]";
|
||||
}
|
||||
|
||||
return $elementType . "[]";
|
||||
}
|
||||
|
||||
return "object";
|
||||
}
|
||||
|
||||
return $type;
|
||||
}
|
||||
|
||||
public static function mergeTypes(string $typeList, string $newType): string {
|
||||
if (empty($typeList)) {
|
||||
return $newType;
|
||||
}
|
||||
|
||||
else if (str_contains("|", $typeList)) {
|
||||
$types = explode("|", $typeList);
|
||||
|
||||
if (!in_array($newType, $types, true)) {
|
||||
$types[] = $newType;
|
||||
}
|
||||
|
||||
return implode("|", $types);
|
||||
}
|
||||
else if ($newType !== $typeList) {
|
||||
return $typeList . "|" . $newType;
|
||||
}
|
||||
|
||||
return $typeList;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
$rows = [];
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
$rows = [...$rows, ...$item->strings()];
|
||||
}
|
||||
|
||||
return implode(PHP_EOL, $rows);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command\Pf2eSchemaGenerator;
|
||||
|
||||
class SchemaItem
|
||||
{
|
||||
public string $name;
|
||||
|
||||
public string $type = "";
|
||||
|
||||
public bool $required = true;
|
||||
|
||||
public ?array $elements = [];
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function disableRequired(): void
|
||||
{
|
||||
$this->required = false;
|
||||
}
|
||||
|
||||
public function adapt(mixed $value): void
|
||||
{
|
||||
$type = Schema::resolveType($value);
|
||||
|
||||
$this->type = Schema::mergeTypes($this->type, $type);
|
||||
|
||||
if (str_contains($this->type, "[]") && !is_null($this->elements)) {
|
||||
foreach ($value as $element) {
|
||||
if (!in_array($element, $this->elements)) {
|
||||
$this->elements[] = $element;
|
||||
}
|
||||
}
|
||||
|
||||
if (count($this->elements) > 20) {
|
||||
$this->elements = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function strings(): array {
|
||||
$name = $this->name;
|
||||
|
||||
if (!$this->required) {
|
||||
$name = "[{$name}]";
|
||||
}
|
||||
|
||||
$rows = [
|
||||
sprintf("%s: %s", $name, $this->type),
|
||||
];
|
||||
|
||||
if (!empty($this->elements)) {
|
||||
sort($this->elements);
|
||||
$elements = [];
|
||||
foreach ($this->elements as $element) {
|
||||
$elements[] = is_string($element) ? "\"{$element}\"" : strval($element);
|
||||
}
|
||||
$rows[] = " Elements: [ " . implode(", ", $elements) . " ]";
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\Service\AonprdConnectionService;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'pf2e:types',
|
||||
description: 'Load types from Archives of Nethys',
|
||||
)]
|
||||
class Pf2eTypesCommand extends Command
|
||||
{
|
||||
public function __construct(private AonprdConnectionService $connectionService)
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this
|
||||
->addOption("revision", null, InputOption::VALUE_REQUIRED, "Load index of specific revision. Default value - latest revision.")
|
||||
->addOption("full", null, InputOption::VALUE_NONE, "Display all columns")
|
||||
;
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
$revision = $input->getOption("revision");
|
||||
$full = $input->getOption("full") ?? false;
|
||||
|
||||
$index = $this->connectionService->loadCurrentIndex($revision);
|
||||
|
||||
$types = [];
|
||||
|
||||
foreach ($index as $page) {
|
||||
foreach ($page as $element) {
|
||||
preg_match("#^([a-z\\-]+?)-(\\d.*)$#", $element, $matches);
|
||||
$type = $matches[1];
|
||||
$id = $matches[2];
|
||||
|
||||
if (!isset($types[$type])) {
|
||||
$types[$type] = [
|
||||
"name" => $type,
|
||||
"first_id" => $element,
|
||||
"count" => 0,
|
||||
"templates" => []
|
||||
];
|
||||
}
|
||||
|
||||
$template = preg_replace("#\\d+#", "%d", $id);
|
||||
|
||||
if (!in_array($template, $types[$type]['templates'])) {
|
||||
$types[$type]['templates'][] = $template;
|
||||
}
|
||||
|
||||
$types[$type]['count']++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($full) {
|
||||
$table = [];
|
||||
|
||||
foreach ($types as $key => $type) {
|
||||
$table[] = [
|
||||
$key,
|
||||
sprintf("<href=https://elasticsearch.aonprd.com/aon/_doc/%s>%s</>",
|
||||
$type["first_id"],
|
||||
$type["first_id"]
|
||||
),
|
||||
$type['count'],
|
||||
implode(", ", $type['templates']),
|
||||
];
|
||||
}
|
||||
|
||||
$io->table(["Type name", "First Id", "Amount", "ID templates"], $table);
|
||||
}
|
||||
else {
|
||||
foreach ($types as $type) {
|
||||
$io->writeln($type['name']);
|
||||
}
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity\Pf2e;
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\Pf2e\ItemRepository;
|
||||
use App\Repository\ItemRepository;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||
use Symfony\Component\Uid\Uuid;
|
||||
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
enum ItemTypeEnum: string {
|
||||
case Action = 'action';
|
||||
case Ancestry = 'ancestry';
|
||||
case AnimalCompanion = 'animal-companion';
|
||||
case AnimalCompanionAdvanced = 'animal-companion-advanced';
|
||||
case AnimalCompanionSpecialization = 'animal-companion-specialization';
|
||||
case AnimalCompanionUnique = 'animal-companion-unique';
|
||||
case ArcaneSchool = 'arcane-school';
|
||||
case ArcaneThesis = 'arcane-thesis';
|
||||
case Archetype = 'archetype';
|
||||
case Armor = 'armor';
|
||||
case ArmorGroup = 'armor-group';
|
||||
case Article = 'article';
|
||||
case Background = 'background';
|
||||
case Bloodline = 'bloodline';
|
||||
case CampsiteMeal = 'campsite-meal';
|
||||
case CategoryPage = 'category-page';
|
||||
case Cause = 'cause';
|
||||
case Klass = 'class';
|
||||
case KKlassFeature = 'class-feature';
|
||||
case KlassKit = 'class-kit';
|
||||
case KlassSample = 'class-sample';
|
||||
case Condition = 'condition';
|
||||
case ConsciousMind = 'conscious-mind';
|
||||
case Creature = 'creature';
|
||||
case CreatureAbility = 'creature-ability';
|
||||
case CreatureAdjustment = 'creature-adjustment';
|
||||
case CreatureFamily = 'creature-family';
|
||||
case CreatureThemeTemplate = 'creature-theme-template';
|
||||
case Curse = 'curse';
|
||||
case Deity = 'deity';
|
||||
case DeityCategory = 'deity-category';
|
||||
case DeviantAbilityClassification = 'deviant-ability-classification';
|
||||
case Disease = 'disease';
|
||||
case Doctrine = 'doctrine';
|
||||
case Domain = 'domain';
|
||||
case DruidicOrder = 'druidic-order';
|
||||
case Eidolon = 'eidolon';
|
||||
case Element = 'element';
|
||||
case Equipment = 'equipment';
|
||||
case EquipmentCategory = 'equipment-category';
|
||||
case FamiliarAbility = 'familiar-ability';
|
||||
case FamiliarSpecific = 'familiar-specific';
|
||||
case Feat = 'feat';
|
||||
case Hazard = 'hazard';
|
||||
case HellknightOrder = 'hellknight-order';
|
||||
case Heritage = 'heritage';
|
||||
case HuntersEdge = 'hunters-edge';
|
||||
case HybridStudy = 'hybrid-study';
|
||||
case Implement = 'implement';
|
||||
case Innovation = 'innovation';
|
||||
case Instinct = 'instinct';
|
||||
case KingdomEvent = 'kingdom-event';
|
||||
case KingdomStructure = 'kingdom-structure';
|
||||
case Language = 'language';
|
||||
case Lesson = 'lesson';
|
||||
case Methodology = 'methodology';
|
||||
case Muse = 'muse';
|
||||
case Mystery = 'mystery';
|
||||
case Patron = 'patron';
|
||||
case Plane = 'plane';
|
||||
case Racket = 'racket';
|
||||
case Relic = 'relic';
|
||||
case ResearchField = 'research-field';
|
||||
case Ritual = 'ritual';
|
||||
case Rules = 'rules';
|
||||
case SetRelic = 'set-relic';
|
||||
case Shield = 'shield';
|
||||
case Sidebar = 'sidebar';
|
||||
case SiegeWeapon = 'siege-weapon';
|
||||
case Skill = 'skill';
|
||||
case SkillGeneralAction = 'skill-general-action';
|
||||
case Source = 'source';
|
||||
case Spell = 'spell';
|
||||
case Style = 'style';
|
||||
case SubconsciousMind = 'subconscious-mind';
|
||||
case Tenet = 'tenet';
|
||||
case Tradition = 'tradition';
|
||||
case Trait = 'trait';
|
||||
case Vehicle = 'vehicle';
|
||||
case WarfareArmy = 'warfare-army';
|
||||
case WarfareTactic = 'warfare-tactic';
|
||||
case Way = 'way';
|
||||
case Weapon = 'weapon';
|
||||
case WeaponGroup = 'weapon-group';
|
||||
case WeatherHazard = 'weather-hazard';
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity\Pf2e;
|
||||
|
||||
enum ItemTypeEnum: string {
|
||||
case Feat = 'feat';
|
||||
case Ancestry = 'ancestry';
|
||||
case Heritage = 'heritage';
|
||||
case Archetype = 'archetype';
|
||||
case Background = 'background';
|
||||
case Skill = 'skill';
|
||||
case Klass = 'klass';
|
||||
case SubKlass = 'sub-klass';
|
||||
case Equipment = 'equipment';
|
||||
case Trait = 'trait';
|
||||
case Rule = 'rule';
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository\Pf2e;
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Pf2e\Item;
|
||||
use App\Entity\Item;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||
use Symfony\Contracts\HttpClient\ResponseInterface;
|
||||
|
||||
class AonprdConnectionService
|
||||
{
|
||||
public const DATA_LINK = "https://elasticsearch.aonprd.com";
|
||||
|
||||
private ?int $revision = null;
|
||||
|
||||
public function __construct(private HttpClientInterface $client)
|
||||
{
|
||||
}
|
||||
|
||||
public function load($url): array
|
||||
{
|
||||
if (!str_starts_with($url, 'https://') && !str_starts_with($url, 'http://')) {
|
||||
$url = self::DATA_LINK . (str_starts_with($url, "/") ? "" : "/") . $url;
|
||||
}
|
||||
|
||||
$response = $this->client->request('GET', $url);
|
||||
|
||||
return json_decode($response->getContent(false), true);
|
||||
}
|
||||
|
||||
public function getRevision(): int
|
||||
{
|
||||
if (null === $this->revision) {
|
||||
$data = $this->load("/aon/_doc/feat-1");
|
||||
$this->revision = intval(str_replace("aon", "", $data["_index"]));
|
||||
}
|
||||
|
||||
return $this->revision;
|
||||
}
|
||||
|
||||
public function loadCurrentIndex(int $revision = null): array
|
||||
{
|
||||
if ($revision === null) {
|
||||
$revision = $this->getRevision();
|
||||
}
|
||||
|
||||
$data = $this->load("/json-data/aon{$revision}-index.json");
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user