Skip to content

Commit 0b824cb

Browse files
committed
Add makefile to help with development setup, change part_ids in bulk import jobs to junction table and implement filtering based on bulk import jobs status and its associated parts' statuses.
1 parent 0b982d3 commit 0b824cb

22 files changed

+1362
-122
lines changed

Makefile

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# PartDB Makefile for Test Environment Management
2+
3+
.PHONY: help test-setup test-clean test-db-create test-db-migrate test-cache-clear test-fixtures test-run dev-setup dev-clean dev-db-create dev-db-migrate dev-cache-clear dev-warmup dev-reset
4+
5+
# Default target
6+
help:
7+
@echo "PartDB Test Environment Management"
8+
@echo "=================================="
9+
@echo ""
10+
@echo "Available targets:"
11+
@echo " test-setup - Complete test environment setup (clean, create DB, migrate, load fixtures)"
12+
@echo " test-clean - Clean test cache and database files"
13+
@echo " test-db-create - Create test database (if not exists)"
14+
@echo " test-db-migrate - Run database migrations for test environment"
15+
@echo " test-cache-clear- Clear test cache"
16+
@echo " test-fixtures - Load test fixtures"
17+
@echo " test-run - Run PHPUnit tests"
18+
@echo ""
19+
@echo "Development Environment:"
20+
@echo " dev-setup - Complete development environment setup (clean, create DB, migrate, warmup)"
21+
@echo " dev-clean - Clean development cache and database files"
22+
@echo " dev-db-create - Create development database (if not exists)"
23+
@echo " dev-db-migrate - Run database migrations for development environment"
24+
@echo " dev-cache-clear - Clear development cache"
25+
@echo " dev-warmup - Warm up development cache"
26+
@echo " dev-reset - Quick development reset (clean + migrate)"
27+
@echo ""
28+
@echo " help - Show this help message"
29+
30+
# Complete test environment setup
31+
test-setup: test-clean test-db-create test-db-migrate test-fixtures
32+
@echo "✅ Test environment setup complete!"
33+
34+
# Clean test environment
35+
test-clean:
36+
@echo "🧹 Cleaning test environment..."
37+
rm -rf var/cache/test
38+
rm -f var/app_test.db
39+
@echo "✅ Test environment cleaned"
40+
41+
# Create test database
42+
test-db-create:
43+
@echo "🗄️ Creating test database..."
44+
-php bin/console doctrine:database:create --if-not-exists --env test || echo "⚠️ Database creation failed (expected for SQLite) - continuing..."
45+
46+
# Run database migrations for test environment
47+
test-db-migrate:
48+
@echo "🔄 Running database migrations..."
49+
COMPOSER_MEMORY_LIMIT=-1 php bin/console doctrine:migrations:migrate -n --env test
50+
51+
# Clear test cache
52+
test-cache-clear:
53+
@echo "🗑️ Clearing test cache..."
54+
rm -rf var/cache/test
55+
@echo "✅ Test cache cleared"
56+
57+
# Load test fixtures
58+
test-fixtures:
59+
@echo "📦 Loading test fixtures..."
60+
php bin/console partdb:fixtures:load -n --env test
61+
62+
# Run PHPUnit tests
63+
test-run:
64+
@echo "🧪 Running tests..."
65+
php bin/phpunit
66+
67+
# Quick test reset (clean + migrate + fixtures, skip DB creation)
68+
test-reset: test-cache-clear test-db-migrate test-fixtures
69+
@echo "✅ Test environment reset complete!"
70+
71+
# Development helpers
72+
dev-setup: dev-clean dev-db-create dev-db-migrate dev-warmup
73+
@echo "✅ Development environment setup complete!"
74+
75+
dev-clean:
76+
@echo "🧹 Cleaning development environment..."
77+
rm -rf var/cache/dev
78+
rm -f var/app_dev.db
79+
@echo "✅ Development environment cleaned"
80+
81+
dev-db-create:
82+
@echo "🗄️ Creating development database..."
83+
-php bin/console doctrine:database:create --if-not-exists --env dev || echo "⚠️ Database creation failed (expected for SQLite) - continuing..."
84+
85+
dev-db-migrate:
86+
@echo "🔄 Running database migrations..."
87+
COMPOSER_MEMORY_LIMIT=-1 php bin/console doctrine:migrations:migrate -n --env dev
88+
89+
dev-cache-clear:
90+
@echo "🗑️ Clearing development cache..."
91+
rm -rf var/cache/dev
92+
@echo "✅ Development cache cleared"
93+
94+
dev-warmup:
95+
@echo "🔥 Warming up development cache..."
96+
COMPOSER_MEMORY_LIMIT=-1 php bin/console cache:warmup --env dev -n --memory-limit=1G
97+
98+
dev-reset: dev-cache-clear dev-db-migrate
99+
@echo "✅ Development environment reset complete!"

migrations/Version20250802153643.php

Lines changed: 0 additions & 52 deletions
This file was deleted.

migrations/Version20250802205143.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DoctrineMigrations;
6+
7+
use App\Migration\AbstractMultiPlatformMigration;
8+
use Doctrine\DBAL\Schema\Schema;
9+
10+
/**
11+
* Auto-generated Migration: Please modify to your needs!
12+
*/
13+
final class Version20250802205143 extends AbstractMultiPlatformMigration
14+
{
15+
public function getDescription(): string
16+
{
17+
return 'Add bulk info provider import jobs and job parts tables';
18+
}
19+
20+
public function mySQLUp(Schema $schema): void
21+
{
22+
$this->addSql('CREATE TABLE bulk_info_provider_import_jobs (id INT AUTO_INCREMENT NOT NULL, name LONGTEXT NOT NULL, field_mappings LONGTEXT NOT NULL, search_results LONGTEXT NOT NULL, status VARCHAR(20) NOT NULL, created_at DATETIME NOT NULL, completed_at DATETIME DEFAULT NULL, prefetch_details TINYINT(1) NOT NULL, created_by_id INT NOT NULL, CONSTRAINT FK_7F58C1EDB03A8386 FOREIGN KEY (created_by_id) REFERENCES `users` (id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB');
23+
$this->addSql('CREATE INDEX IDX_7F58C1EDB03A8386 ON bulk_info_provider_import_jobs (created_by_id)');
24+
25+
$this->addSql('CREATE TABLE bulk_info_provider_import_job_parts (id INT AUTO_INCREMENT NOT NULL, status VARCHAR(20) NOT NULL, reason LONGTEXT DEFAULT NULL, completed_at DATETIME DEFAULT NULL, job_id INT NOT NULL, part_id INT NOT NULL, CONSTRAINT FK_CD93F28FBE04EA9 FOREIGN KEY (job_id) REFERENCES bulk_info_provider_import_jobs (id), CONSTRAINT FK_CD93F28F4CE34BEC FOREIGN KEY (part_id) REFERENCES `parts` (id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ENGINE = InnoDB');
26+
$this->addSql('CREATE INDEX IDX_CD93F28FBE04EA9 ON bulk_info_provider_import_job_parts (job_id)');
27+
$this->addSql('CREATE INDEX IDX_CD93F28F4CE34BEC ON bulk_info_provider_import_job_parts (part_id)');
28+
$this->addSql('CREATE UNIQUE INDEX unique_job_part ON bulk_info_provider_import_job_parts (job_id, part_id)');
29+
}
30+
31+
public function mySQLDown(Schema $schema): void
32+
{
33+
$this->addSql('DROP TABLE bulk_info_provider_import_job_parts');
34+
$this->addSql('DROP TABLE bulk_info_provider_import_jobs');
35+
}
36+
37+
public function sqLiteUp(Schema $schema): void
38+
{
39+
$this->addSql('CREATE TABLE bulk_info_provider_import_jobs (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name CLOB NOT NULL, field_mappings CLOB NOT NULL, search_results CLOB NOT NULL, status VARCHAR(20) NOT NULL, created_at DATETIME NOT NULL, completed_at DATETIME DEFAULT NULL, prefetch_details BOOLEAN NOT NULL, created_by_id INTEGER NOT NULL, CONSTRAINT FK_7F58C1EDB03A8386 FOREIGN KEY (created_by_id) REFERENCES "users" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
40+
$this->addSql('CREATE INDEX IDX_7F58C1EDB03A8386 ON bulk_info_provider_import_jobs (created_by_id)');
41+
42+
$this->addSql('CREATE TABLE bulk_info_provider_import_job_parts (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, status VARCHAR(20) NOT NULL, reason CLOB DEFAULT NULL, completed_at DATETIME DEFAULT NULL, job_id INTEGER NOT NULL, part_id INTEGER NOT NULL, CONSTRAINT FK_CD93F28FBE04EA9 FOREIGN KEY (job_id) REFERENCES bulk_info_provider_import_jobs (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_CD93F28F4CE34BEC FOREIGN KEY (part_id) REFERENCES "parts" (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
43+
$this->addSql('CREATE INDEX IDX_CD93F28FBE04EA9 ON bulk_info_provider_import_job_parts (job_id)');
44+
$this->addSql('CREATE INDEX IDX_CD93F28F4CE34BEC ON bulk_info_provider_import_job_parts (part_id)');
45+
$this->addSql('CREATE UNIQUE INDEX unique_job_part ON bulk_info_provider_import_job_parts (job_id, part_id)');
46+
}
47+
48+
public function sqLiteDown(Schema $schema): void
49+
{
50+
$this->addSql('DROP TABLE bulk_info_provider_import_job_parts');
51+
$this->addSql('DROP TABLE bulk_info_provider_import_jobs');
52+
}
53+
54+
public function postgreSQLUp(Schema $schema): void
55+
{
56+
$this->addSql('CREATE TABLE bulk_info_provider_import_jobs (id SERIAL PRIMARY KEY NOT NULL, name TEXT NOT NULL, field_mappings TEXT NOT NULL, search_results TEXT NOT NULL, status VARCHAR(20) NOT NULL, created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL, completed_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, prefetch_details BOOLEAN NOT NULL, created_by_id INT NOT NULL, CONSTRAINT FK_7F58C1EDB03A8386 FOREIGN KEY (created_by_id) REFERENCES users (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
57+
$this->addSql('CREATE INDEX IDX_7F58C1EDB03A8386 ON bulk_info_provider_import_jobs (created_by_id)');
58+
59+
$this->addSql('CREATE TABLE bulk_info_provider_import_job_parts (id SERIAL PRIMARY KEY NOT NULL, status VARCHAR(20) NOT NULL, reason TEXT DEFAULT NULL, completed_at TIMESTAMP(0) WITHOUT TIME ZONE DEFAULT NULL, job_id INT NOT NULL, part_id INT NOT NULL, CONSTRAINT FK_CD93F28FBE04EA9 FOREIGN KEY (job_id) REFERENCES bulk_info_provider_import_jobs (id) NOT DEFERRABLE INITIALLY IMMEDIATE, CONSTRAINT FK_CD93F28F4CE34BEC FOREIGN KEY (part_id) REFERENCES parts (id) NOT DEFERRABLE INITIALLY IMMEDIATE)');
60+
$this->addSql('CREATE INDEX IDX_CD93F28FBE04EA9 ON bulk_info_provider_import_job_parts (job_id)');
61+
$this->addSql('CREATE INDEX IDX_CD93F28F4CE34BEC ON bulk_info_provider_import_job_parts (part_id)');
62+
$this->addSql('CREATE UNIQUE INDEX unique_job_part ON bulk_info_provider_import_job_parts (job_id, part_id)');
63+
}
64+
65+
public function postgreSQLDown(Schema $schema): void
66+
{
67+
$this->addSql('DROP TABLE bulk_info_provider_import_job_parts');
68+
$this->addSql('DROP TABLE bulk_info_provider_import_jobs');
69+
}
70+
}

src/Controller/BulkInfoProviderImportController.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
namespace App\Controller;
2424

2525
use App\Entity\BulkInfoProviderImportJob;
26+
use App\Entity\BulkInfoProviderImportJobPart;
2627
use App\Entity\BulkImportJobStatus;
2728
use App\Entity\Parts\Part;
2829
use App\Entity\Parts\Supplier;
@@ -104,7 +105,6 @@ public function step1(Request $request, LoggerInterface $exceptionLogger): Respo
104105

105106
// Create and save the job
106107
$job = new BulkInfoProviderImportJob();
107-
$job->setPartIds(array_map(fn($part) => $part->getId(), $parts));
108108
$job->setFieldMappings($fieldMappings);
109109
$job->setPrefetchDetails($prefetchDetails);
110110
$user = $this->getUser();
@@ -113,6 +113,12 @@ public function step1(Request $request, LoggerInterface $exceptionLogger): Respo
113113
}
114114
$job->setCreatedBy($user);
115115

116+
// Create job parts for each part
117+
foreach ($parts as $part) {
118+
$jobPart = new BulkInfoProviderImportJobPart($job, $part);
119+
$job->addJobPart($jobPart);
120+
}
121+
116122
$this->entityManager->persist($job);
117123
$this->entityManager->flush();
118124

@@ -179,7 +185,7 @@ public function step1(Request $request, LoggerInterface $exceptionLogger): Respo
179185

180186
// Convert DTOs to result format with metadata
181187
$partResult['search_results'] = array_map(
182-
function($dto) use ($dtoMetadata) {
188+
function ($dto) use ($dtoMetadata) {
183189
$dtoKey = $dto->provider_key . '|' . $dto->provider_id;
184190
$metadata = $dtoMetadata[$dtoKey] ?? [];
185191
return [
@@ -372,8 +378,7 @@ public function step2(int $jobId): Response
372378
}
373379

374380
// Get the parts and deserialize search results
375-
$partRepository = $this->entityManager->getRepository(Part::class);
376-
$parts = $partRepository->getElementsFromIDArray($job->getPartIds());
381+
$parts = $job->getJobParts()->map(fn($jobPart) => $jobPart->getPart())->toArray();
377382
$searchResults = $this->deserializeSearchResults($job->getSearchResults(), $parts);
378383

379384
return $this->render('info_providers/bulk_import/step2.html.twig', [
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
7+
*
8+
* Copyright (C) 2019 - 2023 Jan Böhmer (https://github.com/jbtronics)
9+
*
10+
* This program is free software: you can redistribute it and/or modify
11+
* it under the terms of the GNU Affero General Public License as published
12+
* by the Free Software Foundation, either version 3 of the License, or
13+
* (at your option) any later version.
14+
*
15+
* This program is distributed in the hope that it will be useful,
16+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
* GNU Affero General Public License for more details.
19+
*
20+
* You should have received a copy of the GNU Affero General Public License
21+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*/
23+
24+
namespace App\DataTables\Filters\Constraints\Part;
25+
26+
use App\DataTables\Filters\Constraints\AbstractConstraint;
27+
use App\Entity\BulkInfoProviderImportJobPart;
28+
use Doctrine\ORM\QueryBuilder;
29+
30+
class BulkImportJobExistsConstraint extends AbstractConstraint
31+
{
32+
/** @var bool|null The value of our constraint */
33+
protected ?bool $value = null;
34+
35+
public function __construct()
36+
{
37+
parent::__construct('bulk_import_job_exists');
38+
}
39+
40+
/**
41+
* Gets the value of this constraint. Null means "don't filter", true means "filter for parts in bulk import jobs", false means "filter for parts not in bulk import jobs".
42+
*/
43+
public function getValue(): ?bool
44+
{
45+
return $this->value;
46+
}
47+
48+
/**
49+
* Sets the value of this constraint. Null means "don't filter", true means "filter for parts in bulk import jobs", false means "filter for parts not in bulk import jobs".
50+
*/
51+
public function setValue(?bool $value): void
52+
{
53+
$this->value = $value;
54+
}
55+
56+
public function isEnabled(): bool
57+
{
58+
return $this->value !== null;
59+
}
60+
61+
public function apply(QueryBuilder $queryBuilder): void
62+
{
63+
// Do not apply a filter if value is null (filter is set to ignore)
64+
if (!$this->isEnabled()) {
65+
return;
66+
}
67+
68+
// Use EXISTS subquery to avoid join conflicts
69+
$existsSubquery = $queryBuilder->getEntityManager()->createQueryBuilder();
70+
$existsSubquery->select('1')
71+
->from(BulkInfoProviderImportJobPart::class, 'bip_exists')
72+
->where('bip_exists.part = part.id');
73+
74+
if ($this->value === true) {
75+
// Filter for parts that ARE in bulk import jobs
76+
$queryBuilder->andWhere('EXISTS (' . $existsSubquery->getDQL() . ')');
77+
} else {
78+
// Filter for parts that are NOT in bulk import jobs
79+
$queryBuilder->andWhere('NOT EXISTS (' . $existsSubquery->getDQL() . ')');
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)