ntd registry importer

other registries updates
master
Константин 2023-08-23 23:51:38 +03:00
parent a61a3cf1e9
commit f870384b0c
26 changed files with 191 additions and 59 deletions

View File

@ -0,0 +1,65 @@
<?php
namespace App\Imports;
use App\Models\Asset;
use App\Models\Registries\Registry;
use App\Models\Registries\RegistryType;
use App\Services\Documents\DocumentDownloadService;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class NtdRegistryImport extends Import implements ToCollection, WithHeadingRow {
public Registry $registry;
public array $mapper = [
'name' => ['prop' => 'name', 'required' => true],
'link' => ['prop' => 'link'],
'active_since' => ['prop' => 'active_since', 'date' => true],
'active_till' => ['prop' => 'active_till', 'date' => true],
'category' => ['prop' => 'cat_name', 'required' => true],
'type' => ['prop' => 'type'],
'agency' => ['prop' => 'agency']
];
public function __construct() {
$this->registry = Registry::query()->where(['type' => RegistryType::NTD])->first();
}
public function collection(Collection $collection) {
$collection->each(function($row) use(&$data) {
if ($row = $this->mapData($row)) {
$this->importRow($row);
}
});
}
public function importRow($row) {
$category = $this->registry->categories()->firstOrCreate(['name' => $row['category']]);
$entry = $this->registry->entries()->firstOrCreate(['name' => $row['name'], 'category_id' => $category->id ?? 0]);
$active_since = (is_object($row['active_since'] ?? null)) ? $row['active_since'] : null;
$active_till = (is_object($row['active_till'] ?? null)) ? $row['active_till'] : null;
$asset = $this->download($row['link'] ?? null);
$link = $this->checkLink($row['link']);
$entry->update(['active_since' => $active_since, 'active_till' => $active_till, 'asset_id' => $asset->id ?? null, 'link' => $link]);
$entry->properties->setValues(['normative-document-type' => ['title' => $row['type'] ?? null], 'host-agency' => ['title' => $row['agency'] ?? null]]);
}
public function download($url): ?Asset {
return (new DocumentDownloadService())->download($url, 'registries/ntd');
}
public function checkLink($link) {
$ext = pathinfo($link, PATHINFO_EXTENSION);
$host = parse_url($link, PHP_URL_HOST);
$result = null;
if ($ext !== 'pdf') {
if ($host && !in_array($host, ['faufcc.ru', 'www.faufcc.ru'])) $result = $link;
}echo ("{$result}\n");
return $result;
}
}

View File

@ -31,6 +31,7 @@ class Entry extends Model {
'asset_id', 'asset_id',
'number', 'number',
'name', 'name',
'link',
'active_since', 'active_since',
'active_till', 'active_till',
'suspended_since', 'suspended_since',

View File

@ -15,6 +15,7 @@ class RegistryType {
public const DISCUSSIONS = 'discussions'; public const DISCUSSIONS = 'discussions';
public const RESEARCHES = 'researches'; public const RESEARCHES = 'researches';
public const TECHNICAL_CERTIFICATES = 'technical-certificates'; public const TECHNICAL_CERTIFICATES = 'technical-certificates';
public const NTD = 'ntd';
public const TITLES = [ public const TITLES = [
self::SIMPLE => 'Простой реестр', self::SIMPLE => 'Простой реестр',
@ -27,7 +28,8 @@ class RegistryType {
self::DEVELOPMENTS => 'Реестр планов разработки', self::DEVELOPMENTS => 'Реестр планов разработки',
self::DISCUSSIONS => 'Реестр публичных обсуждений', self::DISCUSSIONS => 'Реестр публичных обсуждений',
self::RESEARCHES => 'Реестр исследований', self::RESEARCHES => 'Реестр исследований',
self::TECHNICAL_CERTIFICATES => 'Реестр технических свидетельств' self::TECHNICAL_CERTIFICATES => 'Реестр технических свидетельств',
self::NTD => 'Реестр нормативно-технической документации'
]; ];
public const OPTIONS = [ public const OPTIONS = [
@ -42,6 +44,7 @@ class RegistryType {
self::DEVELOPMENTS => ['categorized' => true, 'properties' => 'entry-properties-development'], self::DEVELOPMENTS => ['categorized' => true, 'properties' => 'entry-properties-development'],
self::DISCUSSIONS => ['properties' => 'entry-properties-discussion'], self::DISCUSSIONS => ['properties' => 'entry-properties-discussion'],
self::RESEARCHES => ['categories' => true, 'properties' => 'entry-properties-research'], self::RESEARCHES => ['categories' => true, 'properties' => 'entry-properties-research'],
self::TECHNICAL_CERTIFICATES => ['properties' => 'entry-properties-technical-certificate', 'states' => true] self::TECHNICAL_CERTIFICATES => ['properties' => 'entry-properties-technical-certificate', 'states' => true],
self::NTD => ['categories' => true, 'properties' => 'entry-properties-ntd', 'states' => true]
]; ];
} }

View File

@ -0,0 +1,48 @@
<?php
namespace App\Services\Documents;
use App\Models\Asset;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
class DocumentDownloadService {
protected array $mimes = [
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'pdf' => 'application/pdf',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
];
public function __construct() {
}
public function download($url, $dir = null, $filename = null): ?Asset {
$info = pathinfo($url);
if (!empty($this->mimes[$info['extension'] ?? null])) {
$path = "public/documents";
$filename = $filename ? "{$filename}.{$info['extension']}" : $info['basename'];
$path = $dir ? "{$path}/{$dir}/{$filename}" : "{$path}/{$filename}";
$asset = Asset::query()->where(['path' => $path])->first();
if (!$asset && Storage::put($path, Http::get($url)->body())) $asset = $this->makeAsset($path);
elseif ($asset) var_dump($asset->path);
}
return $asset ?? null;
}
public function makeAsset($path, $name = null) {
$info = pathinfo($path);
return Asset::create([
'type' => 'document',
'path' => $path,
'mime' => $this->mimes[$info['extension']] ?? null,
'name' => $name ?? $info['basename'],
'filename' => $info['basename'],
'extension' => $info['extension'],
'user_id' => ($user = Auth::user()) ? $user->id : null
]);
}
}

View File

@ -2,8 +2,6 @@
namespace App\Services\Documents; namespace App\Services\Documents;
use App\Models\Asset;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -12,12 +10,6 @@ class DocumentGeneratorService {
public array $data; public array $data;
public array $options = []; public array $options = [];
public array $mimes = [
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'pdf' => 'application/pdf',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
];
public static array $classes = [ public static array $classes = [
'pdf' => GeneratePdfDocument::class, 'pdf' => GeneratePdfDocument::class,
'word' => GenerateWordDocument::class 'word' => GenerateWordDocument::class
@ -44,15 +36,6 @@ class DocumentGeneratorService {
} }
public function makeAsset($path, $name) { public function makeAsset($path, $name) {
$info = pathinfo($path); return (new DocumentDownloadService())->makeAsset($path, $name);
return Asset::create([
'type' => 'document',
'path' => $path,
'mime' => $this->mimes[$info['extension']] ?? null,
'name' => $name ?? $info['basename'],
'filename' => $info['basename'],
'extension' => $info['extension'],
'user_id' => ($user = Auth::user()) ? $user->id : null
]);
} }
} }

View File

@ -123,7 +123,7 @@ class EntryFilters extends FiltersService {
if ($exceptNative) $filters = $filters->filter(function($val, $prop) { if ($exceptNative) $filters = $filters->filter(function($val, $prop) {
return Field::byName($prop)->exists(); return Field::byName($prop)->exists();
}); });
return $filters->except('registry', 'category', 'types', 'state')->filter(function($val) { return $filters->except('registry', 'category', 'types')->filter(function($val) {
return collect($val)->filter(function($val) {return $val;})->isNotEmpty(); return collect($val)->filter(function($val) {return $val;})->isNotEmpty();
})->isEmpty(); })->isEmpty();
} }

View File

@ -14,6 +14,7 @@ use App\Transformers\Objects\FieldTransformer;
use App\Transformers\Objects\ObjectPropertyTransformer; use App\Transformers\Objects\ObjectPropertyTransformer;
use App\Transformers\Registries\EntryTransformer; use App\Transformers\Registries\EntryTransformer;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Support\Str;
class EntryForms extends FormsService { class EntryForms extends FormsService {
public array $formTitles = ['create' => 'Создание записи', 'update' => 'Редактирование записи']; public array $formTitles = ['create' => 'Создание записи', 'update' => 'Редактирование записи'];
@ -34,7 +35,8 @@ class EntryForms extends FormsService {
[ [
'name' => 'name', 'name' => 'name',
'title' => 'Наименование записи', 'title' => 'Наименование записи',
'type' => FieldType::STRING, 'type' => FieldType::TEXT,
'maxLength' => 750,
'required' => true, 'required' => true,
'value' => $model->name ?? null 'value' => $model->name ?? null
], ],
@ -49,6 +51,13 @@ class EntryForms extends FormsService {
'title' => 'Документ', 'title' => 'Документ',
'type' => FieldType::DOCUMENT, 'type' => FieldType::DOCUMENT,
'value' => ($asset = $model->asset ?? null) ? fractal($asset, new AssetTransformer()) : null 'value' => ($asset = $model->asset ?? null) ? fractal($asset, new AssetTransformer()) : null
],
[
'name' => 'link',
'title' => 'Ссылка',
'type' => FieldType::STRING,
'maxLength' => 750,
'value' => $model->link ?? null
] ]
]; ];
return ['data' => $fields]; return ['data' => $fields];
@ -106,6 +115,7 @@ class EntryForms extends FormsService {
$category = Category::byUuid($data['category'] ?? null)->first(); $category = Category::byUuid($data['category'] ?? null)->first();
$data['asset_id'] = ($asset = Asset::byUuid($data['asset'] ?? null)->first()) ? $asset->id : null; $data['asset_id'] = ($asset = Asset::byUuid($data['asset'] ?? null)->first()) ? $asset->id : null;
$data['category_id'] = $category->id ?? 0; $data['category_id'] = $category->id ?? 0;
$data['link'] = !empty($data['link']) ? Str::replace(env('APP_URL'), '', $data['link']) : null;
$model = $registry->entries()->create($data); $model = $registry->entries()->create($data);
if ($object = $model->properties ?? null) $object->setValues($data); if ($object = $model->properties ?? null) $object->setValues($data);
return fractal($model, new EntryTransformer())->respond(); return fractal($model, new EntryTransformer())->respond();
@ -114,6 +124,7 @@ class EntryForms extends FormsService {
public function update(string $id, array $data): ?JsonResponse { public function update(string $id, array $data): ?JsonResponse {
$model = Entry::byUuid($id)->firstOrFail(); $model = Entry::byUuid($id)->firstOrFail();
$data['asset_id'] = ($asset = Asset::byUuid($data['asset'] ?? null)->first()) ? $asset->id : null; $data['asset_id'] = ($asset = Asset::byUuid($data['asset'] ?? null)->first()) ? $asset->id : null;
$data['link'] = !empty($data['link']) ? Str::replace(env('APP_URL'), '', $data['link']) : null;
$model->update($data); $model->update($data);
if ($object = $model->properties ?? null) $object->setValues($data); if ($object = $model->properties ?? null) $object->setValues($data);
return fractal($model->fresh(), new EntryTransformer())->respond(); return fractal($model->fresh(), new EntryTransformer())->respond();

View File

@ -4,9 +4,7 @@ namespace App\Services\Registries;
use App\Models\Asset; use App\Models\Asset;
use App\Models\Registries\Registry; use App\Models\Registries\Registry;
use Illuminate\Support\Facades\Auth; use App\Services\Documents\DocumentDownloadService;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;
use PHPHtmlParser\Dom; use PHPHtmlParser\Dom;
class RegistryImportService { class RegistryImportService {
@ -30,34 +28,7 @@ class RegistryImportService {
public function download($url, $dir = null, $filename = null): ?Asset { public function download($url, $dir = null, $filename = null): ?Asset {
$urlInfo = parse_url($url); return (new DocumentDownloadService())->download($url, $dir, $filename);
if (empty($urlInfo['host'])) {
$url = str_replace('//', '/', "faufcc.ru/{$url}");
$url = "https://{$url}";
}
$info = pathinfo($url);
if ($info['extension'] ?? null) {
$path = "public/documents/registries";
$filename = $filename ? "{$filename}.{$info['extension']}" : $info['basename'];
$path = $dir ? "{$path}/{$dir}/{$filename}" : "{$path}/{$filename}";
$asset = Asset::query()->where(['path' => $path])->first();
if (!$asset && Storage::put($path, Http::get($url)->body())) $asset = $this->makeAsset($path);
elseif ($asset) var_dump($asset->path);
}
return $asset ?? null;
}
public function makeAsset($path, $name = null) {
$info = pathinfo($path);
return Asset::create([
'type' => 'document',
'path' => $path,
'mime' => $this->mimes[$info['extension']] ?? null,
'name' => $name ?? $info['basename'],
'filename' => $info['basename'],
'extension' => $info['extension'],
'user_id' => ($user = Auth::user()) ? $user->id : null
]);
} }
} }

View File

@ -100,7 +100,7 @@ class RulesetImportService extends RegistryImportService {
$data['order-name'] = $orderName; $data['order-name'] = $orderName;
$data['order-date'] = $orderDate ? Date::create($orderDate) : null; $data['order-date'] = $orderDate ? Date::create($orderDate) : null;
$filename = $orderName ? Str::slug("Приказ {$orderName} от {$orderDate}") : null; $filename = $orderName ? Str::slug("Приказ {$orderName} от {$orderDate}") : null;
$data['order-document'] = $this->download($link->href, 'ruleset', $filename); $data['order-document'] = $this->download($link->href, 'registries/ruleset', $filename);
} else $data['listings'][] = ['name' => Str::replace('Постановление правительства №', 'pp', $link->text)]; } else $data['listings'][] = ['name' => Str::replace('Постановление правительства №', 'pp', $link->text)];
} }
} else { } else {

View File

@ -25,6 +25,7 @@ class EntryTransformer extends TransformerAbstract {
'id' => $model->uuid, 'id' => $model->uuid,
'number' => $model->number, 'number' => $model->number,
'name' => $model->name, 'name' => $model->name,
'link' => $model->link,
'state' => $model->state, 'state' => $model->state,
'active_since' => $model->active_since ? $model->active_since->toIso8601String() : null, 'active_since' => $model->active_since ? $model->active_since->toIso8601String() : null,
'active_till' => $model->active_till ? $model->active_till->toIso8601String() : null, 'active_till' => $model->active_till ? $model->active_till->toIso8601String() : null,

View File

@ -21,6 +21,7 @@ class CreateRegistryEntriesTable extends Migration
$table->integer('asset_id')->index()->nullable(); $table->integer('asset_id')->index()->nullable();
$table->string('number')->index()->nullable(); $table->string('number')->index()->nullable();
$table->string('name', 750)->index()->nullable(); $table->string('name', 750)->index()->nullable();
$table->string('link', 750)->nullable();
$table->date('active_since')->index()->nullable(); $table->date('active_since')->index()->nullable();
$table->date('active_till')->index()->nullable(); $table->date('active_till')->index()->nullable();
$table->date('suspended_since')->index()->nullable(); $table->date('suspended_since')->index()->nullable();

View File

@ -45,6 +45,21 @@ class DictionariesTableSeeder extends Seeder {
'title' => 'Виды исследовательских работ', 'title' => 'Виды исследовательских работ',
'items' => ['nir' => 'НИР', 'niokr' => 'НИОКР'] 'items' => ['nir' => 'НИР', 'niokr' => 'НИОКР']
], ],
'normative-document-types' => [
'title' => 'Виды нормативно-техничесих документов',
'items' => ['sp' => 'СП', 'gost' => 'ГОСТ', 'sn' => 'СН', 'rk-eek' => 'Решение коллегии ЕЭК', 'gost-r' => 'ГОСТ Р',
'sanpin' => 'СанПиН', 'fnip' => 'ФНиП', 'foiv' => 'Приказ ФОИВа', 'pp-rf' => 'Постановление Правительства РФ',
'rp-rf' => 'Распоряжение Правительства РФ', 'tr-eaes' => 'Технический регламент ЕАЭС', 'fz' => 'Федеральный закон',
'sto' => 'СТО (ПБЯ)', 'rs-eek' => 'Решение Совета ЕЭК', 'st-sev' => 'СТ СЭВ']
],
'host-agencies' => [
'title' => 'Исполнительные органы',
'items' => ['minstroy' => 'Минстрой России', 'gov-rf' => 'Правительство РФ', 'kol-eek' => 'Коллегия ЕЭК', 'mintrans' => 'Минтранс России',
'mchs' => 'МЧС России', 'mincult' => 'Минкультуры России', 'minprirody' => 'Минприроды России', 'rospotreb' => 'Роспотребнадзор',
'rosstandart' => 'Росстандарт', 'rostechnadzor' => 'Ростехнадзор', 'rosatom' => 'ГК «Росатом»', 'president' => 'Президент Российской Федерации',
'minenergo' => 'Минэнерго России', 'sovet-eek' => 'Совет ЕЭК', 'minselhoz' => 'Минсельхоз России', 'mintrud' => 'Минтруд России',
'rosavia' => 'Росавиация']
],
'moderate-permissions' => [ 'moderate-permissions' => [
'title' => 'Права', 'title' => 'Права',
'items' => ['applications' => 'Рассмотрение предварительных заявок'] 'items' => ['applications' => 'Рассмотрение предварительных заявок']

View File

@ -282,6 +282,23 @@ class FieldsTableSeeder extends Seeder {
'type' => FieldType::STRING 'type' => FieldType::STRING
], ],
'normative-document-type' => [
'title' => 'Тип документа',
'type' => FieldType::RELATION,
'params' => [
'related' => DictionaryItem::class, 'transformer' => DictionaryItemTransformer::class,
'options' => ['show' => true, 'whereHas' => ['dictionary' => ['name' => 'normative-document-types']]]
]
],
'host-agency' => [
'title' => 'Принявший орган',
'type' => FieldType::RELATION,
'params' => [
'related' => DictionaryItem::class, 'transformer' => DictionaryItemTransformer::class,
'options' => ['show' => true, 'whereHas' => ['dictionary' => ['name' => 'host-agencies']]]
]
],
'operation-type' => [ 'operation-type' => [
'title' => 'Вид работы', 'title' => 'Вид работы',
'type' => FieldType::RELATION, 'type' => FieldType::RELATION,

View File

@ -99,6 +99,11 @@ class ObjectTypeFieldsTableSeeder extends Seeder {
'company-email', 'company-phone', 'producer-name', 'producer-address'] 'company-email', 'company-phone', 'producer-name', 'producer-address']
] ]
], ],
'entry-properties-ntd' => [
'common' => [
'fields' => ['normative-document-type', 'host-agency']
]
],
'entry-operation-ruleset' => [ 'entry-operation-ruleset' => [
'common' => [ 'common' => [

View File

@ -93,6 +93,9 @@ class ObjectTypesTableSeeder extends Seeder {
], ],
'entry-properties-technical-certificate' => [ 'entry-properties-technical-certificate' => [
'title' => 'Техническое свидетельство' 'title' => 'Техническое свидетельство'
],
'entry-properties-ntd' => [
'title' => 'Нормативно-технический документ'
] ]
] ]
], ],

View File

@ -20,11 +20,11 @@ class PagesTableSeeder extends Seeder
'children' => [ 'children' => [
'Документы об учреждении' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::SIMPLE], 'Документы об учреждении' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::SIMPLE],
'Нормативные правовые акты' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::SIMPLE], 'Нормативные правовые акты' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::SIMPLE],
'Наблюдательный совет' => [], 'Наблюдательный совет' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::CATEGORIZED],
'Государственное задание' => [], 'Государственное задание' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::CATEGORIZED],
'Закупки' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::CATEGORIZED], 'Закупки' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::CATEGORIZED],
'Бухгалтерская отчетность' => [], 'Бухгалтерская отчетность' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::CATEGORIZED],
'Антимонопольное законодательство' => [], 'Антимонопольное законодательство' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::CATEGORIZED]
] ]
], ],
'Противодействие коррупции' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::CATEGORIZED], 'Противодействие коррупции' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::CATEGORIZED],
@ -54,7 +54,7 @@ class PagesTableSeeder extends Seeder
'Нормирование и стандартизация' => [ 'Нормирование и стандартизация' => [
'children' => [ 'children' => [
'Реестр сводов правил' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::RULESET], 'Реестр сводов правил' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::RULESET],
'Реестр нормативно-технической документации' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::CATEGORIZED], 'Реестр нормативно-технической документации' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::NTD],
'Разработка сводов правил' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::DEVELOPMENTS], 'Разработка сводов правил' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::DEVELOPMENTS],
'Прикладные исследования' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::RESEARCHES], 'Прикладные исследования' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::RESEARCHES],
'Методические материалы' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::SIMPLE] 'Методические материалы' => ['type' => PageType::REGISTRY, 'registry_type' => RegistryType::SIMPLE]

View File

@ -1,10 +1,13 @@
<?php <?php
use App\Imports\CompaniesImport;
use App\Models\Registries\Registry; use App\Models\Registries\Registry;
use App\Models\Registries\RegistryType; use App\Models\Registries\RegistryType;
use App\Models\User; use App\Models\User;
use App\Services\Registries\RulesetImportService; use App\Services\Registries\RulesetImportService;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Storage;
use Maatwebsite\Excel\Facades\Excel;
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
@ -33,4 +36,8 @@ Artisan::command('htmlparser:import-rulesets', function() {
$service->import(); $service->import();
}); });
Artisan::command('dev:import-ntd', function() {
Excel::import(new \App\Imports\NtdRegistryImport(), Storage::path('import/registries/ntd.xlsx'));
});

Binary file not shown.

View File

@ -0,0 +1 @@
*