Merge branch 'master' into settings-bundle

This commit is contained in:
Jan Böhmer 2025-01-17 22:06:18 +01:00
commit 8750573724
191 changed files with 27745 additions and 12133 deletions

View file

@ -27,6 +27,7 @@ use App\Entity\Attachments\AttachmentType;
use App\Entity\Attachments\PartAttachment;
use App\Entity\Base\AbstractStructuralDBElement;
use App\Entity\Parameters\PartParameter;
use App\Entity\Parts\Category;
use App\Entity\Parts\Footprint;
use App\Entity\Parts\InfoProviderReference;
use App\Entity\Parts\Manufacturer;
@ -36,6 +37,7 @@ use App\Entity\Parts\Supplier;
use App\Entity\PriceInformations\Currency;
use App\Entity\PriceInformations\Orderdetail;
use App\Entity\PriceInformations\Pricedetail;
use App\Repository\Parts\CategoryRepository;
use App\Services\InfoProviderSystem\DTOs\FileDTO;
use App\Services\InfoProviderSystem\DTOs\ParameterDTO;
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
@ -160,6 +162,12 @@ final class DTOtoEntityConverter
$entity->setMass($dto->mass);
//Try to map the category to an existing entity (but never create a new one)
if ($dto->category) {
//@phpstan-ignore-next-line For some reason php does not recognize the repo returns a category
$entity->setCategory($this->em->getRepository(Category::class)->findForInfoProvider($dto->category));
}
$entity->setManufacturer($this->getOrCreateEntity(Manufacturer::class, $dto->manufacturer));
$entity->setFootprint($this->getOrCreateEntity(Footprint::class, $dto->footprint));

View file

@ -0,0 +1,77 @@
<?php
namespace App\Services\InfoProviderSystem;
use App\Entity\Parts\Manufacturer;
use App\Entity\Parts\Part;
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
use Doctrine\ORM\EntityManagerInterface;
/**
* This service assists in finding existing local parts for a SearchResultDTO, so that the user
* does not accidentally add a duplicate.
*
* A part is considered to be a duplicate, if the provider reference matches, or if the manufacturer and the MPN of the
* DTO and the local part match. This checks also for alternative names of the manufacturer and the part name (as alternative
* for the MPN).
*/
final class ExistingPartFinder
{
public function __construct(private readonly EntityManagerInterface $em)
{
}
/**
* Return the first existing local part, that matches the search result.
* If no part is found, return null.
* @param SearchResultDTO $dto
* @return Part|null
*/
public function findFirstExisting(SearchResultDTO $dto): ?Part
{
$results = $this->findAllExisting($dto);
return count($results) > 0 ? $results[0] : null;
}
/**
* Returns all existing local parts that match the search result.
* If no part is found, return an empty array.
* @param SearchResultDTO $dto
* @return Part[]
*/
public function findAllExisting(SearchResultDTO $dto): array
{
$qb = $this->em->getRepository(Part::class)->createQueryBuilder('part');
$qb->select('part')
->leftJoin('part.manufacturer', 'manufacturer')
->Orwhere($qb->expr()->andX(
'part.providerReference.provider_key = :providerKey',
'part.providerReference.provider_id = :providerId',
))
//Or the manufacturer (allowing for alternative names) and the MPN (or part name) must match
->OrWhere(
$qb->expr()->andX(
$qb->expr()->orX(
"ILIKE(manufacturer.name, :manufacturerName) = TRUE",
"ILIKE(manufacturer.alternative_names, :manufacturerAltNames) = TRUE",
),
$qb->expr()->orX(
"ILIKE(part.manufacturer_product_number, :mpn) = TRUE",
"ILIKE(part.name, :mpn) = TRUE",
)
)
)
;
$qb->setParameter('providerKey', $dto->provider_key);
$qb->setParameter('providerId', $dto->provider_id);
$qb->setParameter('manufacturerName', $dto->manufacturer);
$qb->setParameter('manufacturerAltNames', '%'.$dto->manufacturer.'%');
$qb->setParameter('mpn', $dto->mpn);
return $qb->getQuery()->getResult();
}
}

View file

@ -45,6 +45,14 @@ class DigikeyProvider implements InfoProviderInterface
private readonly HttpClientInterface $digikeyClient;
/**
* A list of parameter IDs, that are always assumed as text only and will never be converted to a numerical value.
* This allows to fix issues like #682, where the "Supplier Device Package" was parsed as a numerical value.
*/
private const TEXT_ONLY_PARAMETERS = [
1291, //Supplier Device Package
39246, //Package / Case
];
public function __construct(HttpClientInterface $httpClient, private readonly OAuthTokenManager $authTokenManager,
private readonly string $currency, private readonly string $clientId,
@ -214,7 +222,12 @@ class DigikeyProvider implements InfoProviderInterface
continue;
}
$results[] = ParameterDTO::parseValueIncludingUnit($parameter['Parameter'], $parameter['Value']);
//If the parameter was marked as text only, then we do not try to parse it as a numerical value
if (in_array($parameter['ParameterId'], self::TEXT_ONLY_PARAMETERS, true)) {
$results[] = new ParameterDTO(name: $parameter['Parameter'], value_text: $parameter['Value']);
} else { //Otherwise try to parse it as a numerical value
$results[] = ParameterDTO::parseValueIncludingUnit($parameter['Parameter'], $parameter['Value']);
}
}
return $results;

View file

@ -98,16 +98,19 @@ class LCSCProvider implements InfoProviderInterface
private function getRealDatasheetUrl(?string $url): string
{
if ($url !== null && trim($url) !== '' && preg_match("/^https:\/\/(datasheet\.lcsc\.com|www\.lcsc\.com\/datasheet)\/.*(C\d+)\.pdf$/", $url, $matches) > 0) {
if (preg_match("/^https:\/\/datasheet\.lcsc\.com\/lcsc\/(.*\.pdf)$/", $url, $rewriteMatches) > 0) {
$url = 'https://www.lcsc.com/datasheet/lcsc_datasheet_' . $rewriteMatches[1];
}
$response = $this->lcscClient->request('GET', $url, [
'headers' => [
'Referer' => 'https://www.lcsc.com/product-detail/_' . $matches[2] . '.html'
],
]);
if (preg_match('/(pdfUrl): ?("[^"]+wmsc\.lcsc\.com[^"]+\.pdf")/', $response->getContent(), $matches) > 0) {
if (preg_match('/(previewPdfUrl): ?("[^"]+wmsc\.lcsc\.com[^"]+\.pdf")/', $response->getContent(), $matches) > 0) {
//HACKY: The URL string contains escaped characters like \u002F, etc. To decode it, the JSON decoding is reused
//See https://github.com/Part-DB/Part-DB-server/pull/582#issuecomment-2033125934
$jsonObj = json_decode('{"' . $matches[1] . '": ' . $matches[2] . '}');
$url = $jsonObj->pdfUrl;
$url = $jsonObj->previewPdfUrl;
}
}
return $url;

View file

@ -203,7 +203,7 @@ class MouserProvider implements InfoProviderInterface
if (isset($arr['SearchResults'])) {
$products = $arr['SearchResults']['Parts'] ?? [];
} else {
throw new \RuntimeException('Unknown response format');
throw new \RuntimeException('Unknown response format: ' .json_encode($arr, JSON_THROW_ON_ERROR));
}
$result = [];

File diff suppressed because it is too large Load diff