diff --git a/app/Classes/MetadataManager.php b/app/Classes/MetadataManager.php index fbed3d0..110476c 100644 --- a/app/Classes/MetadataManager.php +++ b/app/Classes/MetadataManager.php @@ -137,7 +137,7 @@ public function merge(array $array) */ public function get($key, $default = null) { - return $this->content->get($key, $default); + return collect($this->content)->dot()->get($key, $default); } /** diff --git a/app/Console/Commands/Wikidata/UpdateProperties.php b/app/Console/Commands/Wikidata/UpdateProperties.php new file mode 100644 index 0000000..70859ec --- /dev/null +++ b/app/Console/Commands/Wikidata/UpdateProperties.php @@ -0,0 +1,92 @@ +fetchData(); + $this->importData($data); + } + + /** + * Fetch data from Wikidata or cache. + */ + protected function fetchData(): array + { + $endpoint = 'https://query.wikidata.org/sparql'; + $query = " + SELECT ?property ?propertyType ?propertyLabel ?propertyDescription ?propertyAltLabel WHERE { + ?property wikibase:propertyType ?propertyType . + SERVICE wikibase:label { bd:serviceParam wikibase:language \"fr\". } + } + ORDER BY ASC(xsd:integer(STRAFTER(STR(?property), 'P')))"; + + $response = Http::throw()->timeout(60)->withHeaders(['Accept' => 'application/sparql-results+json']) + ->get($endpoint, ['query' => $query]); + + return $response->json(); + } + + /** + * Import data into the database. + */ + protected function importData(array $data): void + { + $total = count($data['results']['bindings']); + + $this->info("Starting import of $total Wikidata properties."); + + $bar = $this->output->createProgressBar($total); + $bar->start(); + + DB::transaction(function () use ($data, &$bar) { + $batchSize = 200; + + foreach (array_chunk($data['results']['bindings'], $batchSize) as $batch) { + $this->insertBatch($batch, $bar); + } + }); + + $bar->finish(); + $this->newLine(2); + $this->info('Properties imported successfully.'); + } + + /** + * Insert a batch of properties into the database. + * + * @param \Symfony\Component\Console\Helper\ProgressBar $bar + */ + protected function insertBatch(array $batch, $bar): void + { + $upsertData = []; + + foreach ($batch as $result) { + $propertyId = str_replace('http://www.wikidata.org/entity/', '', $result['property']['value']); + + $upsertData[] = [ + 'property_id' => $propertyId, + 'property_type' => $result['propertyType']['value'] ?? null, + 'label' => $result['propertyLabel']['value'] ?? null, + 'description' => $result['propertyDescription']['value'] ?? null, + 'alt_label' => $result['propertyAltLabel']['value'] ?? null, + ]; + + $bar->advance(); + } + + WikidataProperty::upsert($upsertData, 'property_id', ['property_type', 'label', 'description', 'alt_label']); + } +} diff --git a/app/Models/WikidataProperty.php b/app/Models/WikidataProperty.php new file mode 100644 index 0000000..c810701 --- /dev/null +++ b/app/Models/WikidataProperty.php @@ -0,0 +1,11 @@ +app->singleton(WikidataClient::class, function ($app) { + $config = $app['config']->get('services.wikidata'); + + return new WikidataClient($config); + }); + + $this->app->singleton(WikidataExtractor::class, function ($app) { + $inclusions = $app['config']->get('wikidata.inclusions', []); + $exclusions = $app['config']->get('wikidata.exclusions', []); + + return new WikidataExtractor($exclusions, $inclusions); + }); + } + + /** + * Bootstrap services. + */ + public function boot(): void + { + // + } +} diff --git a/app/Services/BundleCreator/Creators/CriticBundleCreator.php b/app/Services/BundleCreator/Creators/CriticBundleCreator.php index 151b2c6..70e0281 100644 --- a/app/Services/BundleCreator/Creators/CriticBundleCreator.php +++ b/app/Services/BundleCreator/Creators/CriticBundleCreator.php @@ -3,8 +3,14 @@ namespace App\Services\BundleCreator\Creators; use App\Classes\Bundle; +use App\Exceptions\BundleAlreadyExists; +use App\Services\Wikidata\WikidataClient; +use App\Services\Wikidata\WikidataExtractor; +use Exception; use Illuminate\Filesystem\FilesystemAdapter; +use Illuminate\Support\Str; +use function Laravel\Prompts\confirm; use function Laravel\Prompts\select; use function Laravel\Prompts\text; @@ -17,6 +23,63 @@ public function __construct(protected ?array $data, protected FilesystemAdapter // } + /** + * Create a bundle + */ + public function createBundle(): string + { + $entityId = $this->data['entity_id']; + + if ($entityId === false) { + throw new Exception('Bundle creation cancelled'); + } + + $kind = $this->data['kind']; + $title = $this->data['title']; + $slug = Str::slug($title); + $date = now(); + $path = sprintf('%s/%s/%s', static::$section, $kind, $slug); + $bundle = new Bundle($path, $this->disk); + + if ($bundle->exists()) { + throw new BundleAlreadyExists( + sprintf('A bundle already exists in %s', $path) + ); + } + + $wikidata = app()->make(WikidataClient::class); + $extractor = app()->make(WikidataExtractor::class); + $completeEntity = $wikidata->getEntityData($entityId, true)['entities'][$entityId]; + + $extractor->extract($completeEntity, $entityId); + + $bundle->metadata('wikidata/included')->setMany($extractor->included()); + $bundle->metadata('wikidata/excluded')->setMany($extractor->excluded()); + $bundle->metadata('wikidata/unused')->setMany($extractor->unused()); + $bundle->metadata('wikidata/entity')->setMany($extractor->everythingElse()); + + $frenchTitle = $bundle->metadata('wikidata/entity')->get('labels.fr.value', null); + $originalTitle = $bundle->metadata('wikidata/entity')->get('labels.en.value', null); + + if (!empty($originalTitle)) { + $bundle->metadata()->set('title', $originalTitle); + + if (!empty($frenchTitle) && $frenchTitle !== $originalTitle) { + $bundle->metadata()->set('subTitle', $frenchTitle); + } + } + + $bundle->metadata()->setMany([ + 'date' => $date->toIso8601String(), + ]); + + $bundle->markdown()->set(''); + + $bundle->save(); + + return $path; + } + /** * Return a boolean value indicating if the creator can actually make the * bundle using known data. @@ -26,7 +89,7 @@ public function canCreateBundle(): bool return !empty($this->data['kind']) && !empty($this->data['title']) - && !empty($this->data['entityId']); + && array_key_exists('entity_id', $this->data); } /** @@ -45,8 +108,16 @@ public function formSpecs(): ?array $specs['title'] = fn () => text('Work title', '', '', true); } - if (empty($this->data['entityId'])) { - $specs['entityId'] = fn () => text('Entity ID', '', '', true); + if (!empty($this->data['kind']) && !empty($this->data['title']) && empty($this->data['entity_id'])) { + $options = app()->make(WikidataClient::class)->searchEntityId($this->data['title']); + + if (!empty($options)) { + $specs['entity_id'] = fn () => select('Confirm searched work', $options); + } else { + $specs['entity_id'] = fn () => confirm( + 'No entityId was found in Wikidata for this work. Do you want to create the bundle anyway?' + ); + } } return $specs; diff --git a/app/Services/Wikidata/WikidataClient.php b/app/Services/Wikidata/WikidataClient.php new file mode 100644 index 0000000..7a965f6 --- /dev/null +++ b/app/Services/Wikidata/WikidataClient.php @@ -0,0 +1,132 @@ + 'wbsearchentities', + 'search' => $expression, + 'language' => 'en', + 'limit' => 10, + 'format' => 'json', + ]; + + $response = $this->getFromApi($data); + $items = []; + + if (!empty($response['search'])) { + foreach ($response['search'] as $item) { + $items[$item['id']] = sprintf('[%s] %s (%s)', $item['id'], $item['label'], $item['description'] ?? null); + } + } + + return $items; + } + + /** + * Return complete set of data related to specified entity + */ + public function getEntityData(string $entityId) + { + $data = [ + 'action' => 'wbgetentities', + 'ids' => $entityId, + 'languages' => 'fr|en', + 'format' => 'json', + ]; + + return $this->getFromApi($data); + + // if ($replaceIds) { + // $data = json_encode($result, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + // $properties = $this->getDeclaredPropertiesInEntity($data); + // $entities = $this->getDeclaredEntitiesInEntity($data, $entityId); + // $result = $this->replaceLabelsIn($result, $properties, $entities, $entityId); + // } + } + + /** + * Return an array containing Wikidata Entities Id as keys and corresponding + * labels as values + */ + public function getLabelsForEntities(array $entities) + { + $result = []; + $batchSize = 50; + $batches = array_chunk($entities, $batchSize); + + foreach ($batches as $batch) { + $ids = implode('|', $batch); + $data = [ + 'action' => 'wbgetentities', + 'ids' => $ids, + 'props' => 'labels|descriptions', + 'languages' => 'fr|en', + 'format' => 'json', + ]; + + $result += $this->getFromApi($data)['entities']; + } + + $labels = []; + + foreach ($result as $id => $entity) { + if ( + !isset($entity['labels']['fr']['value']) + && !isset($entity['labels']['en']['value']) + ) { + continue; + } + + $label = $entity['labels']['fr']['value'] + ?? $entity['labels']['en']['value']; + + $description = $entity['descriptions']['fr']['value'] + ?? $entity['descriptions']['en']['value'] + ?? null; + + if ($label) { + $labels[$id] = Str::ucfirst($label); + } + } + + return $labels; + } + + /** + * Perform a GET request against wikidata API + */ + private function getFromApi(array $params) + { + $baseUrl = $this->config['baseApiUrl']; + $slug = Str::slug(http_build_query($params)); + $cacheKey = sprintf('%s_%s', static::$cachePrefix, $slug); + + if (Cache::has($cacheKey)) { + return Cache::get($cacheKey); + } + + $response = Http::throw()->get($baseUrl, $params)->json(); + + Cache::put($cacheKey, $response, now()->addMonth()); + + return $response; + } +} diff --git a/app/Services/Wikidata/WikidataExtractor.php b/app/Services/Wikidata/WikidataExtractor.php new file mode 100644 index 0000000..e83c923 --- /dev/null +++ b/app/Services/Wikidata/WikidataExtractor.php @@ -0,0 +1,308 @@ +included; + } + + public function excluded() + { + return $this->excluded; + } + + public function unused() + { + return $this->unused; + } + + public function everythingElse() + { + return $this->everythingElse; + } + + public function __construct(protected array $exclusions, protected array $inclusions) + { + + } + + /** + * Split data from specified array in three arrays containing explicitely + * included properties, explicitely excluded properties and unused + * properties (neither included or excluded) + */ + public function extract(array $entityData, string $entityId) + { + $json = json_encode($entityData, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); + + $this->properties = $this->getDeclaredPropertiesInEntity($json); + $this->entities = $this->getDeclaredEntitiesInEntity($json, $entityId); + + $result = $this->browse($entityData['claims']); + + $this->included = $result['included']; + $this->excluded = $result['excluded']; + $this->unused = $result['unused']; + + unset($entityData['claims']); + + $this->everythingElse = $entityData; + } + + /** + * Return an array containing Wikidata Property ID as keys and corresponding + * label as values + */ + private function getDeclaredPropertiesInEntity(string $data) + { + preg_match_all('/P\d{1,}/', $data, $matches); + natsort($matches[0]); + + $ids = collect(array_values($matches[0]))->unique()->all(); + $properties = WikidataProperty::whereIn('property_id', $ids)->get(); + $result = collect($ids)->combine($properties->pluck('label')); + + return $result->toArray(); + } + + /** + * Return an array containing Wikidata Property ID as keys and corresponding + * label as values + */ + private function getDeclaredEntitiesInEntity(string $data, string $entityId) + { + preg_match_all('/Q\d{1,}/', $data, $matches); + natsort($matches[0]); + + $ids = collect(array_values($matches[0]))->except($entityId)->unique()->all(); + + return app()->make(WikidataClient::class)->getLabelsForEntities($ids); + } + + /** + * Recursively browse Wikidata array + */ + private function browse(array $claims) + { + $included = []; + $excluded = []; + $unused = []; + + foreach ($claims as $key => $data) { + $isExcluded = in_array($key, $this->exclusions); + $isIncluded = in_array($key, collect($this->inclusions)->flatten()->values()->toArray()); + $isUnused = !$isExcluded && !$isIncluded; + + $claim = $this->parseClaims($data, $isIncluded); + + if ($isExcluded) { + $newKey = $this->replaceValue($key, true, true); + $excluded[$newKey] = $claim; + } elseif ($isIncluded) { + $newKey = $this->replaceValue($key, true); + $included[$key] = $claim; + } elseif ($isUnused) { + $newKey = $this->replaceValue($key, true, true); + $unused[$newKey] = $claim; + } + } + + return [ + 'excluded' => $excluded, + 'included' => $this->reorganizeIncluded($included), + 'unused' => $unused, + ]; + } + + /** + * Parse claims of a specific property + */ + private function parseClaims(array $data, bool $parentIncluded) + { + $result = []; + + foreach ($data as $claim) { + $result[] = $this->parseClaim($claim, $parentIncluded); + } + + return $result; + } + + /** + * Parse a specific claim + */ + private function parseClaim(array $data, bool $parentIncluded) + { + $value = $this->parseSnak($data['mainsnak'], $parentIncluded); + + if (!empty($data['qualifiers'])) { + $itemQualifiers = []; + + foreach ($data['qualifiers'] as $qualifierProperty => $qualifiers) { + $qualifierKey = $this->replaceValue($qualifierProperty, true, !$parentIncluded); + + foreach ($qualifiers as $qualifierData) { + $qualifierValue = $this->parseSnak($qualifierData, $parentIncluded); + + $itemQualifiers[$qualifierKey][] = $qualifierValue; + } + } + + $result = [ + $value => $itemQualifiers, + ]; + } else { + $result = $value; + } + + return $result; + } + + /** + * Parse a specific snak + */ + private function parseSnak(array $data, bool $parentIncluded) + { + if (empty($data['datavalue']['value'])) { + dd($data); + } + + $value = $data['datavalue']['value']; + $valueType = $data['datavalue']['type']; + + switch ($valueType) { + case 'wikibase-entityid': + $value = $this->replaceValue($value['id'], true, !$parentIncluded); + break; + case 'string': + $value = $this->replaceValue($value, true, !$parentIncluded); + break; + case 'time': + $value = $value['time']; + break; + case 'quantity': + $value = $value['amount']; + break; + case 'monolingualtext': + $value = $value['text']; + break; + default: + dd($data['mainsnak']); + } + + return $value; + } + + /** + * Replace a value with a more human-friendly version. Basically replaces + * Wikidata entities and properties with labels stored in database, if it + * applies. + */ + private function replaceValue(string $value, bool $showCode = true, bool $showLabel = true) + { + $isExcluded = in_array($value, $this->exclusions); + $isIncluded = in_array($value, collect($this->inclusions)->flatten()->values()->toArray()); + $isUnused = !$isExcluded && !$isIncluded; + + $code = $value; + $label = $value; + + if (array_key_exists($value, $this->properties)) { + $label = $this->properties[$value]; + } elseif (array_key_exists($value, $this->entities)) { + $label = $this->entities[$value]; + } + + $both = $code !== $label ? sprintf('[%s] %s', $code, $label) : $value; + + if ($showCode && $showLabel) { + return $both; + } elseif ($showCode) { + return $code; + } else { + return $label; + } + } + + /** + * Take the "raw" included data and reorganize it according to the + * "inclusions" Wikidata configuration + */ + private function reorganizeIncluded(array $includedData) + { + $reorganized = []; + + foreach ($this->inclusions as $category => $properties) { + $result = $this->includeProperties($includedData, $properties); + + if (!empty($result)) { + $reorganized[$category] = $result; + } + } + + return $reorganized; + } + + /** + * Include specific properties + */ + private function includeProperties($includedData, $properties) + { + $result = []; + + foreach ($properties as $propertyId) { + if (!array_key_exists($propertyId, $includedData)) { + continue; + } + + $newKey = $this->replaceValue($propertyId, false, true); + + $values = $includedData[$propertyId]; + + $result[$newKey] = $this->includeValues($values); + } + + return $result; + } + + /** + * Include specific values + */ + private function includeValues(array $values) + { + $newValues = []; + + foreach ($values as $key => $value) { + $newKey = $this->replaceValue($key, false, true); + + if (is_array($value)) { + $value = $this->includeValues($value); + } else { + $value = $this->replaceValue($value, false, true); + } + + $newValues[$newKey] = $value; + } + + return $newValues; + } +} diff --git a/bootstrap/providers.php b/bootstrap/providers.php index fb8f1af..e95100a 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -4,4 +4,5 @@ App\Providers\AppServiceProvider::class, App\Providers\BundleCreatorServiceProvider::class, App\Providers\RebrickableServiceProvider::class, + App\Providers\WikidataServiceProvider::class, ]; diff --git a/config/services.php b/config/services.php index c0037e8..72a5e6e 100644 --- a/config/services.php +++ b/config/services.php @@ -19,6 +19,10 @@ 'key' => env('REBRICKABLE_API_KEY'), ], + 'wikidata' => [ + 'baseApiUrl' => 'https://www.wikidata.org/w/api.php', + ], + 'postmark' => [ 'token' => env('POSTMARK_TOKEN'), ], diff --git a/config/wikidata/exclusions.php b/config/wikidata/exclusions.php new file mode 100644 index 0000000..630775e --- /dev/null +++ b/config/wikidata/exclusions.php @@ -0,0 +1,234 @@ + [ + 'P1877', + ], + 'staff' => [ + 'P50', + 'P57', + 'P58', + 'P86', + 'P162', + 'P170', + 'P175', + 'P178', + 'P344', + 'P371', + 'P1040', + 'P1431', + 'P2515', + 'P2554', + 'P3092', + 'P5028', + ], + 'productionCompanies' => [ + 'P272', + ], + 'genres' => [ + 'P136', + ], + 'subjects' => [ + 'P921', + ], + 'publishers' => [ + 'P123', + ], + 'distribution' => [ + 'P161', + ], + 'sagas' => [ + 'P179', + ], + 'links' => [ + 'P856', + 'P345', + 'P1258', + 'P1265', + 'P1267', + 'P1651', + 'P1712', + 'P1733', + 'P1874', + 'P2002', + 'P2003', + 'P2013', + 'P2397', + 'P3984', + 'P4013', + 'P4073', + 'P4477', + 'P4947', + 'P4983', + 'P5749', + 'P6262', + 'P6398', + 'P7595', + 'P7596', + 'P8055', + 'P9586', + 'P9751', + 'P11460', + ], + 'locations' => [ + 'P840', + 'P915', + ], + 'miscKeywords' => [ + 'P180', + 'P8371', + 'P8411', + ], +]; diff --git a/config/wikidata/templates.php b/config/wikidata/templates.php new file mode 100644 index 0000000..515ac21 --- /dev/null +++ b/config/wikidata/templates.php @@ -0,0 +1,118 @@ + [ + 'P856' => [ + 'template' => null, + 'title' => 'Site officiel', + ], + 'P345' => [ + 'template' => 'https://www.imdb.com/title/%s/', + 'title' => 'IMDB', + ], + 'P1258' => [ + 'template' => 'https://www.rottentomatoes.com/%s', + 'title' => 'Rotten Tomatoes', + ], + 'P1265' => [ + 'template' => 'https://www.allocine.fr/film/fichefilm_gen_cfilm=%s.html', + 'title' => 'AlloCiné', + ], + 'P1267' => [ + 'template' => 'https://www.allocine.fr/series/ficheserie_gen_cserie=%s.html', + 'title' => 'AlloCiné', + ], + 'P1651' => [ + 'template' => 'https://www.youtube.com/watch?v=%s', + 'title' => 'YouTube', + ], + 'P1712' => [ + 'template' => 'https://www.metacritic.com/%s', + 'title' => 'Metacritic', + ], + 'P1733' => [ + 'template' => 'https://store.steampowered.com/app/%s/', + 'title' => 'Steam', + ], + 'P1874' => [ + 'template' => 'https://www.netflix.com/title/%s', + 'title' => 'Netflix', + ], + 'P2002' => [ + 'template' => 'https://twitter.com/%s', + 'title' => 'X', + ], + 'P2003' => [ + 'template' => 'https://www.instagram.com/%s', + 'title' => 'Instagram', + ], + 'P2013' => [ + 'template' => 'https://www.facebook.com/%s', + 'title' => 'facebook', + ], + 'P2397' => [ + 'template' => 'https://www.youtube.com/channel/%s', + 'title' => 'Chaîne YouTube', + ], + 'P3984' => [ + 'template' => 'https://www.reddit.com/r/%s/', + 'title' => 'Reddit', + ], + 'P4013' => [ + 'template' => 'https://giphy.com/%s', + 'title' => 'Giphy', + ], + 'P4073' => [ + 'template' => 'https://community.fandom.com/wiki/w:c:%s', + 'title' => 'Fandom', + ], + 'P4477' => [ + 'template' => 'https://www.humblebundle.com/store/%s', + 'title' => 'Humble Store', + ], + 'P4947' => [ + 'template' => 'https://www.themoviedb.org/movie/%s', + 'title' => 'TMDB', + ], + 'P4983' => [ + 'template' => 'https://www.themoviedb.org/tv/%s', + 'title' => 'TMBD', + ], + 'P5749' => [ + 'template' => 'https://www.amazon.com/dp/%s', + 'title' => 'Amazon', + ], + 'P6262' => [ + 'template' => 'https://community.fandom.com/wiki/w:c:%s', + 'title' => 'Fandom', + ], + 'P6398' => [ + 'template' => 'https://itunes.apple.com/us/movie/id%s', + 'title' => 'iTunes', + ], + 'P7595' => [ + 'template' => 'https://www.disneyplus.com/movies/wd/%s', + 'title' => 'Disney+', + ], + 'P7596' => [ + 'template' => 'https://www.disneyplus.com/series/wp/%s', + 'title' => 'Disney+', + ], + 'P8055' => [ + 'template' => 'https://www.amazon.com/gp/video/detail/%s', + 'title' => 'Amazon Prime', + ], + 'P9586' => [ + 'template' => 'https://tv.apple.com/movie/%s', + 'title' => 'Apple TV', + ], + 'P9751' => [ + 'template' => 'https://tv.apple.com/show/%s', + 'title' => 'Apple TV', + ], + 'P11460' => [ + 'template' => 'https://app.plex.tv/desktop/#!/provider/tv.plex.provider.metadata/details?key=/library/metadata/%s', + 'title' => 'Plex', + ], + ], +]; diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php deleted file mode 100644 index 05fb5d9..0000000 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ /dev/null @@ -1,49 +0,0 @@ -id(); - $table->string('name'); - $table->string('email')->unique(); - $table->timestamp('email_verified_at')->nullable(); - $table->string('password'); - $table->rememberToken(); - $table->timestamps(); - }); - - Schema::create('password_reset_tokens', function (Blueprint $table) { - $table->string('email')->primary(); - $table->string('token'); - $table->timestamp('created_at')->nullable(); - }); - - Schema::create('sessions', function (Blueprint $table) { - $table->string('id')->primary(); - $table->foreignId('user_id')->nullable()->index(); - $table->string('ip_address', 45)->nullable(); - $table->text('user_agent')->nullable(); - $table->longText('payload'); - $table->integer('last_activity')->index(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('users'); - Schema::dropIfExists('password_reset_tokens'); - Schema::dropIfExists('sessions'); - } -}; diff --git a/database/migrations/0001_01_01_000001_create_cache_table.php b/database/migrations/0001_01_01_000001_create_cache_table.php deleted file mode 100644 index b9c106b..0000000 --- a/database/migrations/0001_01_01_000001_create_cache_table.php +++ /dev/null @@ -1,35 +0,0 @@ -string('key')->primary(); - $table->mediumText('value'); - $table->integer('expiration'); - }); - - Schema::create('cache_locks', function (Blueprint $table) { - $table->string('key')->primary(); - $table->string('owner'); - $table->integer('expiration'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('cache'); - Schema::dropIfExists('cache_locks'); - } -}; diff --git a/database/migrations/0001_01_01_000002_create_jobs_table.php b/database/migrations/0001_01_01_000002_create_jobs_table.php deleted file mode 100644 index 425e705..0000000 --- a/database/migrations/0001_01_01_000002_create_jobs_table.php +++ /dev/null @@ -1,57 +0,0 @@ -id(); - $table->string('queue')->index(); - $table->longText('payload'); - $table->unsignedTinyInteger('attempts'); - $table->unsignedInteger('reserved_at')->nullable(); - $table->unsignedInteger('available_at'); - $table->unsignedInteger('created_at'); - }); - - Schema::create('job_batches', function (Blueprint $table) { - $table->string('id')->primary(); - $table->string('name'); - $table->integer('total_jobs'); - $table->integer('pending_jobs'); - $table->integer('failed_jobs'); - $table->longText('failed_job_ids'); - $table->mediumText('options')->nullable(); - $table->integer('cancelled_at')->nullable(); - $table->integer('created_at'); - $table->integer('finished_at')->nullable(); - }); - - Schema::create('failed_jobs', function (Blueprint $table) { - $table->id(); - $table->string('uuid')->unique(); - $table->text('connection'); - $table->text('queue'); - $table->longText('payload'); - $table->longText('exception'); - $table->timestamp('failed_at')->useCurrent(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - Schema::dropIfExists('jobs'); - Schema::dropIfExists('job_batches'); - Schema::dropIfExists('failed_jobs'); - } -}; diff --git a/database/migrations/2024_04_03_162705_create_wikidata_properties_table.php b/database/migrations/2024_04_03_162705_create_wikidata_properties_table.php new file mode 100644 index 0000000..3c06a20 --- /dev/null +++ b/database/migrations/2024_04_03_162705_create_wikidata_properties_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('property_id')->unique(); + $table->string('property_type')->nullable(); + $table->text('label')->nullable(); + $table->text('description')->nullable(); + $table->text('alt_label')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('wikidata_properties'); + } +};