Use an enum for Bus Interface values, remove the getter/setter boilerplate from Gpu Entity

This commit is contained in:
Timothy Warren 2022-10-14 11:16:53 -04:00
parent 2c2ef3f3b9
commit e5c1e6537a
8 changed files with 194 additions and 605 deletions

View File

@ -3,6 +3,7 @@
namespace App\Controller;
use App\Entity\Gpu;
use App\Enum\SlotKeyEnum;
use App\Form\GpuType;
use App\Traits\FormControllerTrait;
use Doctrine\ORM\EntityManagerInterface;
@ -27,16 +28,27 @@ class GpuController extends AbstractController
#[Route('/', name: 'gpu_index', methods: ['GET'])]
public function index(): Response
{
$acquiredItems = $this->entityManager->getRepository(self::ENTITY)->findBy([
'acquired' => TRUE,
], []);
$newItems = $this->entityManager->getRepository(self::ENTITY)->findBy([
'acquired' => FALSE,
], []);
$items = $this->entityManager->getRepository(self::ENTITY)->findAll();
$acquiredItems = array_filter($items, fn (Gpu $item) => $item->isAcquired());
$newItems = array_filter($items, fn ($item) => !$item->isAcquired());
$filter = fn (array $itemTypes) => array_filter($acquiredItems, fn (Gpu $item) => in_array($item->getCardKey(), $itemTypes));
$cardTypes = SlotKeyEnum::getGroups();
$pcieCardTypes = $cardTypes['PCI Express'];
$agpCardTypes = $cardTypes['AGP'];
$pciCardTypes = array_merge($cardTypes['PCI 32-bit'], $cardTypes['PCI 64-bit']);
$isaCardTypes = $cardTypes['ISA'];
return $this->render('gpu/index.html.twig', [
'not_acquired' => $newItems,
'acquired' => $acquiredItems,
'gpus' => [
'not_acquired' => $newItems,
'acquired' => $acquiredItems,
'pcie' => $filter($pcieCardTypes),
'agp' => $filter($agpCardTypes),
'pci' => $filter($pciCardTypes),
'isa' => $filter($isaCardTypes),
]
]);
}

View File

@ -4,6 +4,9 @@ namespace App\Entity;
use InvalidArgumentException;
/**
* Remove the need for all the Doctrine getter/setter Entity boilerplate
*/
trait GetSetTrait
{
public function __call(string $name, array $arguments): mixed
@ -12,14 +15,38 @@ trait GetSetTrait
return $this->{$name}(...$arguments);
}
// Apparently Doctrine first tries the method with the same
// name as the property, instead of with the get prefix
if (property_exists($this, $name) && empty($arguments)) {
return $this->{$name};
}
// Getters
if (empty($arguments))
{
// Apparently Doctrine first tries the method with the same
// name as the property
if (property_exists($this, $name))
{
return $this->{$name};
}
if (str_starts_with('set', $name)) {
$var = lcfirst(str_replace('set', '', $name));
if (str_starts_with($name, 'get'))
{
$var = lcfirst(substr($name, 3));
if (property_exists($this, $var))
{
return $this->{$var};
}
}
if (str_starts_with($name, 'is'))
{
$var = lcfirst(substr($name, 2));
if (property_exists($this, $var))
{
return $this->{$var};
}
}
}
// Setters
if (str_starts_with($name, 'set')) {
$var = lcfirst(substr($name, 3));
if (property_exists($this, $var)) {
$this->{$name} = $arguments[0];
}

View File

@ -2,9 +2,10 @@
namespace App\Entity;
use App\Enum\CardBusEnum;
use App\Enum\SlotKeyEnum;
use App\Types\CardBusEnumType;
use App\Types\SlotKeyEnumType;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Table(name: 'gpu', schema: 'collection')]
@ -40,10 +41,10 @@ class Gpu
private ?string $alternateModelName = '';
#[ORM\Column(name: 'card_key', type: SlotKeyEnumType::NAME, nullable: TRUE, enumType: SlotKeyEnum::class)]
private $cardKey = SlotKeyEnum::PCIE_X16;
private SlotKeyEnum $cardKey = SlotKeyEnum::PCIE_X16;
#[ORM\Column(name: 'bus_interface', nullable: TRUE)]
private ?string $busInterface;
#[ORM\Column(name: 'bus_interface', type: CardBusEnumType::NAME, nullable: TRUE, enumType: CardBusEnum::class)]
private ?CardBusEnum $busInterface;
#[ORM\Column(name: 'slot_width')]
private int $slotWidth = 1;
@ -122,405 +123,4 @@ class Gpu
#[ORM\Column(name: 'notes', type: 'text', nullable: TRUE)]
private ?string $notes = '';
public function getId(): ?int
{
return $this->id;
}
public function getModelName(): ?string
{
return $this->modelName;
}
public function setModelName(string $modelName): self
{
$this->modelName = $modelName;
return $this;
}
public function getAlternateModelName(): ?string
{
return $this->alternateModelName;
}
public function setAlternateModelName(?string $alternateModelName): self
{
$this->alternateModelName = $alternateModelName;
return $this;
}
public function getCardKey()
{
return $this->cardKey;
}
public function setCardKey($cardKey): self
{
$this->cardKey = $cardKey;
return $this;
}
public function getBusInterface(): ?string
{
return $this->busInterface;
}
public function setBusInterface(string $busInterface): self
{
$this->busInterface = $busInterface;
return $this;
}
public function getSlotWidth(): ?int
{
return $this->slotWidth;
}
public function setSlotWidth(int $slotWidth): self
{
$this->slotWidth = $slotWidth;
return $this;
}
public function getMolexPower(): ?int
{
return $this->molexPower;
}
public function setMolexPower(int $molexPower): self
{
$this->molexPower = $molexPower;
return $this;
}
public function getPcie6power(): ?int
{
return $this->pcie6power;
}
public function setPcie6power(int $pcie6power): self
{
$this->pcie6power = $pcie6power;
return $this;
}
public function getPcie8power(): ?int
{
return $this->pcie8power;
}
public function setPcie8power(int $pcie8power): self
{
$this->pcie8power = $pcie8power;
return $this;
}
public function getTdp(): ?int
{
return $this->tdp;
}
public function setTdp(?int $tdp): self
{
$this->tdp = $tdp;
return $this;
}
public function getBaseClock(): ?int
{
return $this->baseClock;
}
public function setBaseClock(int $baseClock): self
{
$this->baseClock = $baseClock;
return $this;
}
public function getBoostClock(): ?int
{
return $this->boostClock;
}
public function setBoostClock(int $boostClock): self
{
$this->boostClock = $boostClock;
return $this;
}
public function getMemoryClock(): ?int
{
return $this->memoryClock;
}
public function setMemoryClock(int $memoryClock): self
{
$this->memoryClock = $memoryClock;
return $this;
}
public function getMemorySize(): ?int
{
return $this->memorySize;
}
public function setMemorySize(int $memorySize): self
{
$this->memorySize = $memorySize;
return $this;
}
public function getMemoryBus(): ?int
{
return $this->memoryBus;
}
public function setMemoryBus(int $memoryBus): self
{
$this->memoryBus = $memoryBus;
return $this;
}
public function getMemoryType(): ?string
{
return $this->memoryType;
}
public function setMemoryType(string $memoryType): self
{
$this->memoryType = $memoryType;
return $this;
}
public function getShadingUnits(): ?int
{
return $this->shadingUnits;
}
public function setShadingUnits(int $shadingUnits): self
{
$this->shadingUnits = $shadingUnits;
return $this;
}
public function getTmus(): ?int
{
return $this->tmus;
}
public function setTmus(int $tmus): self
{
$this->tmus = $tmus;
return $this;
}
public function getRops(): ?int
{
return $this->rops;
}
public function setRops(int $rops): self
{
$this->rops = $rops;
return $this;
}
public function getComputeUnits(): ?int
{
return $this->computeUnits;
}
public function setComputeUnits(int $computeUnits): self
{
$this->computeUnits = $computeUnits;
return $this;
}
public function getL1cache(): ?string
{
return $this->l1cache;
}
public function setL1cache(string $l1cache): self
{
$this->l1cache = $l1cache;
return $this;
}
public function getL2cache(): ?string
{
return $this->l2cache;
}
public function setL2cache(string $l2cache): self
{
$this->l2cache = $l2cache;
return $this;
}
public function getDirectXSupport(): ?string
{
return $this->directXSupport;
}
public function setDirectXSupport(string $directXSupport): self
{
$this->directXSupport = $directXSupport;
return $this;
}
public function getOpenGLSupport(): ?string
{
return $this->openGLSupport;
}
public function setOpenGLSupport(string $openGLSupport): self
{
$this->openGLSupport = $openGLSupport;
return $this;
}
public function getOpenCLSupport(): ?string
{
return $this->openCLSupport;
}
public function setOpenCLSupport(string $openCLSupport): self
{
$this->openCLSupport = $openCLSupport;
return $this;
}
public function getVulkanSupport(): ?string
{
return $this->vulkanSupport;
}
public function setVulkanSupport(string $vulkanSupport): self
{
$this->vulkanSupport = $vulkanSupport;
return $this;
}
public function getShaderModel(): ?string
{
return $this->shaderModel;
}
public function setShaderModel(string $shaderModel): self
{
$this->shaderModel = $shaderModel;
return $this;
}
public function getLink(): ?string
{
return $this->link;
}
public function setLink(string $link): self
{
$this->link = $link;
return $this;
}
public function getCount(): ?int
{
return $this->count;
}
public function setCount(int $count): self
{
$this->count = $count;
return $this;
}
public function isAcquired(): ?bool
{
return $this->acquired;
}
public function setAcquired(bool $acquired): self
{
$this->acquired = $acquired;
return $this;
}
public function getNotes(): ?string
{
return $this->notes;
}
public function setNotes(?string $notes): self
{
$this->notes = $notes;
return $this;
}
public function getGpuBrand(): ?Brand
{
return $this->gpuBrand;
}
public function setGpuBrand(?Brand $gpuBrand): self
{
$this->gpuBrand = $gpuBrand;
return $this;
}
public function getGpuCore(): ?GpuCore
{
return $this->gpuCore;
}
public function setGpuCore(?GpuCore $gpuCore): self
{
$this->gpuCore = $gpuCore;
return $this;
}
public function getBoardBrand(): ?Brand
{
return $this->boardBrand;
}
public function setBoardBrand(?Brand $boardBrand): self
{
$this->boardBrand = $boardBrand;
return $this;
}
}

View File

@ -3,5 +3,48 @@
namespace App\Enum;
enum CardBusEnum: string {
case PCIE_10_1 = 'PCIe 1.0 x1';
case PCIE_10_16 = 'PCIe 1.0 x16';
case PCIE_11_16 = 'PCIe 1.1 x16';
case PCIE_20_16 = 'PCIe 2.0 x16';
case PCIE_30_8 = 'PCIe 3.0 x8';
case PCIE_30_16 = 'PCIe 3.0 x16';
case PCIE_40_4 = 'PCIe 4.0 x4';
case PCIE_40_8 = 'PCIe 4.0 x8';
case PCIE_40_16 = 'PCIe 4.0 x16';
case PCI_33 = 'PCI 33';
case PCI_33V_33 = '3.3V PCI 33';
case AGP_1X = 'AGP 1x';
case AGP_2X = 'AGP 2x';
case AGP_4X = 'AGP 4x';
case AGP_8X = 'AGP 8x';
case ISA_8 = '8-bit ISA';
case ISA_16 = '16-bit ISA';
case ISA_VLB = 'VLB';
public static function getGroups(): array
{
$filter = static fn (string $starts_with) =>
array_filter(self::cases(), fn (CardBusEnum $case) =>
str_starts_with($case->name, $starts_with)
);
$pcie = $filter('PCIE_');
$agp = $filter('AGP_');
$pci = $filter('PCI_');
$isa = $filter('ISA_');
$pcie16 = array_filter($pcie, fn (CardBusEnum $case) => str_ends_with($case->name, '_16'));
$pcieOther = array_udiff($pcie, $pcie16, fn ($a, $b) => $a->name <=> $b->name);
return [
'PCI Express x16' => $pcie16,
'PCI Express Other' => $pcieOther,
'AGP' => $agp,
'PCI' => $pci,
'ISA' => $isa,
];
}
}

View File

@ -23,13 +23,10 @@ enum SlotKeyEnum: string {
case PCI_64_UNIVERSAL = 'Universal 64-bit PCI';
case PCI_X = 'PCI-X';
case VLB = 'VESA Local Bus';
case ISA_VLB = 'VESA Local Bus';
case ISA_8 = '8-bit ISA';
case ISA_16 = '16-bit ISA';
case OLD1 = 'PCI 5V';
public static function getCases(): array
{
$cases = self::cases();
@ -44,13 +41,13 @@ enum SlotKeyEnum: string {
public static function getGroups(): array
{
$filter = static fn (string $starts_with) =>
array_filter(self::cases(), fn (SlotKeyEnum $case) =>
str_starts_with($case->name, $starts_with)
);
return [
'PCI Express' => [
self::PCIE_X16,
self::PCIE_X8,
self::PCIE_X4,
self::PCIE_X1,
],
'PCI Express' => $filter('PCIE_'),
'AGP' => [
self::AGP_UNIVERSAL,
self::AGP_33V,
@ -70,7 +67,7 @@ enum SlotKeyEnum: string {
'ISA' => [
self::ISA_16,
self::ISA_8,
self::VLB,
self::ISA_VLB,
]
];
}

View File

@ -9,72 +9,76 @@ use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\
{AbstractType, Extension\Core\Type\EnumType, FormBuilderInterface};
use Symfony\Component\OptionsResolver\OptionsResolver;
use UnitEnum;
class GpuType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$brandQueryBuilder = static fn (EntityRepository $e) => $e->createQueryBuilder('b')->orderBy('b.name', 'ASC');
class GpuType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$brandQueryBuilder = static fn(EntityRepository $e) => $e->createQueryBuilder('b')->orderBy('b.name', 'ASC');
$builder
->add('gpuBrand', EntityType::class, [
'class' => Brand::class,
'query_builder' => $brandQueryBuilder,
])
->add('modelName')
->add('gpuCore', EntityType::class, [
'class' => GpuCore::class,
'query_builder' => static fn (EntityRepository $e) => $e->createQueryBuilder('gc')->orderBy('gc.brand', 'ASC')->orderBy('gc.name', 'ASC'),
$builder
->add('gpuBrand', EntityType::class, [
'class' => Brand::class,
'query_builder' => $brandQueryBuilder,
])
->add('modelName')
->add('gpuCore', EntityType::class, [
'class' => GpuCore::class,
'query_builder' => static fn(EntityRepository $e) => $e->createQueryBuilder('gc')->orderBy('gc.brand', 'ASC')->orderBy('gc.name', 'ASC'),
'placeholder' => 'Unknown',
'empty_data' => NULL,
'required' => false,
])
->add('boardBrand', EntityType::class, [
'class' => Brand::class,
'query_builder' => $brandQueryBuilder,
'empty_data' => NULL,
'required' => FALSE,
])
->add('boardBrand', EntityType::class, [
'class' => Brand::class,
'query_builder' => $brandQueryBuilder,
'empty_data' => NULL,
'placeholder' => 'Unknown',
'required' => false,
])
->add('alternateModelName')
->add('cardKey', EnumType::class, [
'required' => FALSE,
])
->add('alternateModelName')
->add('cardKey', EnumType::class, [
'class' => SlotKeyEnum::class,
'choices' => SlotKeyEnum::getGroups(),
'choice_label' => fn (\UnitEnum $choice):string => $choice->value,
'choice_label' => fn(UnitEnum $choice): string => $choice->value,
])
->add('busInterface')
->add('slotWidth')
->add('molexPower', null, ['label' => 'Molex Power Connectors'])
->add('pcie6power', null, ['label' => 'PCIe 6-pin Power Connectors'])
->add('pcie8power', null, ['label' => 'PCIe 8-pin Power Connectors'])
->add('tdp', null, ['label' => 'TDP (Watts)', 'empty_data' => '0'])
->add('baseClock', null, ['label' => 'GPU Base Clock (MHz)'])
->add('boostClock', null, ['label' => 'GPU Boost Clock (MHz)'])
->add('memoryClock', null, ['label' => 'Memory Speed (MHz)'])
->add('memorySize', null, ['label' => 'Memory Size (MB)'])
->add('memoryBus', null, ['label' => 'Memory Bus Size (bits)'])
->add('memoryType')
->add('shadingUnits')
->add('tmus', null, ['label' => 'TMUs'])
->add('rops', null, ['label' => 'ROPs'])
->add('computeUnits')
->add('l1cache', null, ['label' => 'L1 Cache'])
->add('l2cache', null, ['label' => 'L2 Cache'])
->add('directXSupport', null, ['label' => 'DirectX Support'])
->add('openGLSupport', null, ['label' => 'OpenGL Support'])
->add('openCLSupport', null, ['label' => 'OpenCL Support'])
->add('vulkanSupport')
->add('shaderModel')
->add('link')
->add('count')
->add('acquired')
->add('notes');
}
->add('busInterface', EnumType::class, [
'class' => CardBusEnum::class,
'choices' => CardBusEnum::getGroups(),
'choice_label' => fn(UnitEnum $choice): string => $choice->value,
])
->add('slotWidth')
->add('molexPower', NULL, ['label' => 'Molex Power Connectors'])
->add('pcie6power', NULL, ['label' => 'PCIe 6-pin Power Connectors'])
->add('pcie8power', NULL, ['label' => 'PCIe 8-pin Power Connectors'])
->add('tdp', NULL, ['label' => 'TDP (Watts)', 'empty_data' => '0'])
->add('baseClock', NULL, ['label' => 'GPU Base Clock (MHz)'])
->add('boostClock', NULL, ['label' => 'GPU Boost Clock (MHz)'])
->add('memoryClock', NULL, ['label' => 'Memory Speed (MHz)'])
->add('memorySize', NULL, ['label' => 'Memory Size (MB)'])
->add('memoryBus', NULL, ['label' => 'Memory Bus Size (bits)'])
->add('memoryType')
->add('shadingUnits')
->add('tmus', NULL, ['label' => 'TMUs'])
->add('rops', NULL, ['label' => 'ROPs'])
->add('computeUnits')
->add('l1cache', NULL, ['label' => 'L1 Cache'])
->add('l2cache', NULL, ['label' => 'L2 Cache'])
->add('directXSupport', NULL, ['label' => 'DirectX Support'])
->add('openGLSupport', NULL, ['label' => 'OpenGL Support'])
->add('openCLSupport', NULL, ['label' => 'OpenCL Support'])
->add('vulkanSupport')
->add('shaderModel')
->add('link')
->add('count')
->add('acquired')
->add('notes');
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Gpu::class,
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Gpu::class,
]);
}
}

View File

@ -23,15 +23,28 @@
id="classifications"
>
<li class="tabs-title is-active" aria-selected="true">
<a href="#in_collection">In Collection</a>
<a href="#not_acquired">On the Way</a>
</li>
<li class="tabs-title">
<a href="#on_the_way">On the Way</a>
<a href="#pcie">PCIe</a>
</li>
<li class="tabs-title">
<a href="#agp">AGP</a>
</li>
<li class="tabs-title">
<a href="#pci">PCI</a>
</li>
<li class="tabs-title">
<a href="#isa">ISA</a>
</li>
<li class="tabs-title" >
<a href="#acquired">All In Collection</a>
</li>
</ul>
<div class="tabs-content" data-tabs-content="classifications">
<div class="tabs-panel is-active" id="in_collection">
{% for label, gpus in gpus %}
<div class="tabs-panel {% if label == 'not_acquired' %}is-active{% endif %}" id="{{ label }}">
<table class="hover scroll sortable stack">
<thead>
<tr>
@ -61,12 +74,11 @@
<th>Shader Model</th>
<th>Link</th>
<th>Count</th>
<th>Acquired</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
{% for gpu in acquired %}
{% for gpu in gpus %}
<tr>
<td>
<ul>
@ -92,114 +104,7 @@
<td>{% if gpu.boardBrand %}{{ gpu.boardBrand.name }}{% else %}Unknown{% endif %}</td>
<td>{{ gpu.alternateModelName }}</td>
<td>{{ gpu.cardKey.value }}</td>
<td>{{ gpu.busInterface }}</td>
<td>{{ gpu.slotWidth }}</td>
<td>
{% if gpu.pcie6power > 0 or gpu.pcie8power > 0 %}
{% if gpu.pcie6power > 0 %}
{{ gpu.pcie6power }} PCIe 6-pin
{% endif %}
{% if gpu.pcie8power > 0 %}
{{ gpu.pcie8power }} PCIe 8-pin
{% endif %}
{% elseif gpu.molexPower > 0 %}
{{ gpu.molexPower }} Molex
{% else %}
Slot
{% endif %}
</td>
<td>{{ gpu.tdp }}</td>
<td>
{% if gpu.boostClock %}
{{ gpu.baseClock }} -> {{ gpu.boostClock }}
{% else %}
{{ gpu.baseClock }}
{% endif %}
</td>
<td>{{ gpu.memorySize }}MiB {{ gpu.memoryClock }}MHz {{ gpu.memoryBus }}bit {{ gpu.memoryType }}</td>
<td>{{ gpu.shadingUnits }}</td>
<td>{{ gpu.tmus }}</td>
<td>{{ gpu.rops }}</td>
<td>{{ gpu.computeUnits }}</td>
<td>{{ gpu.l1cache }}</td>
<td>{{ gpu.l2cache }}</td>
<td>{{ gpu.directXSupport }}</td>
<td>{{ gpu.openGLSupport }}</td>
<td>{{ gpu.openCLSupport }}</td>
<td>{{ gpu.vulkanSupport }}</td>
<td>{{ gpu.shaderModel }}</td>
<td>{{ gpu.link }}</td>
<td>{{ gpu.count }}</td>
<td>{{ gpu.acquired ? 'Yes' : 'No' }}</td>
<td>{{ gpu.notes }}</td>
</tr>
{% else %}
<tr>
<td colspan="32">no records found</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="tabs-panel" id="on_the_way">
<table class="hover scroll sortable stack">
<thead>
<tr>
<th>&nbsp;</th>
<th>Id</th>
<th>Model Name</th>
<th>GPU</th>
<th>Card Brand</th>
<th>Alternate Model Name</th>
<th>Card Keying</th>
<th>Bus Interface</th>
<th>Slot Width</th>
<th>Power</th>
<th>Tdp</th>
<th>GPU Clock</th>
<th>Memory</th>
<th>Shading Units</th>
<th>TMUs</th>
<th>ROPs</th>
<th>Compute Units</th>
<th>L1 cache</th>
<th>L2 cache</th>
<th>DirectX Support</th>
<th>OpenGL</th>
<th>OpenCL</th>
<th>Vulkan</th>
<th>Shader Model</th>
<th>Link</th>
<th>Count</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
{% for gpu in not_acquired %}
<tr>
<td>
<ul>
<li>
<a href="{{ path('gpu_show', {'id': gpu.id}) }}">View 👁</a>
</li>
<li>
<a href="{{ path('gpu_edit', {'id': gpu.id}) }}">Edit <span class="edit-icon">&#9998;</span></a>
</li>
</ul>
</td>
<td>{{ gpu.id }}</td>
<td>{{ gpu.gpuBrand.name }} {{ gpu.modelName }}</td>
<td>
{% if gpu.gpuCore.variant %}
{{ gpu.gpuCore.name }} ({{ gpu.gpuCore.variant }})
{% else %}
{{ gpu.gpuCore.name }}
{% endif %}
</td>
<td>{% if gpu.boardBrand %}{{ gpu.boardBrand.name }}{% else %}Unknown{% endif %}</td>
<td>{{ gpu.alternateModelName }}</td>
<td>{{ gpu.cardKey.value }}</td>
<td>{{ gpu.busInterface }}</td>
<td>{{ gpu.busInterface.value }}</td>
<td>{{ gpu.slotWidth }}</td>
<td>
{% if gpu.pcie6power > 0 or gpu.pcie8power > 0 %}
@ -247,5 +152,6 @@
</tbody>
</table>
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -46,7 +46,7 @@
</tr>
<tr>
<th>BusInterface</th>
<td>{{ gpu.busInterface }}</td>
<td>{{ gpu.busInterface.value }}</td>
</tr>
<tr>
<th>SlotWidth</th>