diff --git a/entities/attribute.py b/entities/attribute.py new file mode 100644 index 0000000..e2cde48 --- /dev/null +++ b/entities/attribute.py @@ -0,0 +1,139 @@ +from typing import Optional, Union + +from entities.common import EnumValue, Value + + +class AttributeUnit(EnumValue): + def get_name(self) -> str: + return 'unit' + + +class UnitlessUnit(AttributeUnit): + NONE = "none" + + +class AttributeType(EnumValue): + VOLTAGE = 'voltage' + STRING = 'string' + CURRENT = 'current' + RESISTANCE = 'resistance' + CAPACITANCE = 'capacitance' + POWER = 'power' + INDUCTANCE = 'inductance' + FREQUENCY = 'frequency' + + def get_name(self) -> str: + return 'type' + + +class Attribute(): + def __init__(self, name: str, value: Union[Value, str], attribute_type: AttributeType, unit: Optional[AttributeUnit]) -> None: + self.name = name + + self.value = Value(value) if isinstance(value, str) else value + self.unit = unit or UnitlessUnit.NONE + self.attribute_type = attribute_type + + def __str__(self) -> str: + return '(attribute "{}" {} {} {})'.format(self.name, self.attribute_type, self.unit, self.value) + + +class CapacitanceUnit(AttributeUnit): + PICOFARAD = 'picofarad' + NANOFARAD = 'nanofarad' + MICROFARAD = 'microfarad' + MILLIFARAD = 'millifarad' + FARAD = 'farad' + + +class CapacitanceAttribute(Attribute): + def __init__(self, name: str, value: Union[Value, str], unit: CapacitanceUnit) -> None: + super().__init__(name, value, AttributeType.CAPACITANCE, unit) + + +class CurrentUnit(AttributeUnit): + PICOAMPERE = 'picoampere' + NANOAMPERE = 'nanoampere' + MICROAMPERE = 'microampere' + MILLIAMPERE = 'milliampere' + AMPERE = 'ampere' + KILOAMPERE = 'kiloampere' + MEGAAMPERE = 'megaampere' + + +class CurrentAttribute(Attribute): + def __init__(self, name: str, value: Union[Value, str], unit: CurrentUnit) -> None: + super().__init__(name, value, AttributeType.CURRENT, unit) + + +class FrequencyUnit(AttributeUnit): + MICROHERTZ = 'microhertz' + MILLIHERTZ = 'millihertz' + HERTZ = 'hertz' + KILOHERTZ = 'kilohertz' + MEGAHERTZ = 'megahertz' + GIGAHERTZ = 'gigahertz' + + +class FrequencyAttribute(Attribute): + def __init__(self, name: str, value: Union[Value, str], unit: FrequencyUnit) -> None: + super().__init__(name, value, AttributeType.FREQUENCY, unit) + + +class InductanceUnit(AttributeUnit): + NANOHENRY = 'nanohenry' + MICROHENRY = 'microhenry' + MILLIHENRY = 'millihenry' + HENRY = 'henry' + + +class InductanceAttribute(Attribute): + def __init__(self, name: str, value: Union[Value, str], unit: InductanceUnit) -> None: + super().__init__(name, value, AttributeType.INDUCTANCE, unit) + + +class PowerUnit(AttributeUnit): + NANOWATT = 'nanowatt' + MICROWATT = 'microwatt' + MILLIWATT = 'milliwatt' + WATT = 'watt' + KILOWATT = 'kilowatt' + MEGAWATT = 'megawatt' + GIGAWATT = 'gigawatt' + + +class PowerAttribute(Attribute): + def __init__(self, name: str, value: Union[Value, str], unit: PowerUnit) -> None: + super().__init__(name, value, AttributeType.POWER, unit) + + +class ResistanceUnit(AttributeUnit): + MICROOHM = 'microohm' + MILLIOHM = 'milliohm' + OHM = 'ohm' + KILOOHM = 'kiloohm' + MEGAOHM = 'megaohm' + + +class ResistanceAttribute(Attribute): + def __init__(self, name: str, value: Union[Value, str], unit: ResistanceUnit) -> None: + super().__init__(name, value, AttributeType.RESISTANCE, unit) + + +class VoltageUnit(AttributeUnit): + NANOVOLT = 'nanovolt' + MICROVOLT = 'microvolt' + MILLIVOLT = 'millivolt' + VOLT = 'volt' + KILOVOLT = 'kilovolt' + MEGAVOLT = 'megavolt' + + +class VoltageAttribute(Attribute): + def __init__(self, name: str, value: Union[Value, str], unit: VoltageUnit) -> None: + super().__init__(name, value, AttributeType.VOLTAGE, unit) + + +class StringAttribute(Attribute): + def __init__(self, name: str, value: Union[Value, str]) -> None: + super().__init__(name, value, AttributeType.STRING, None) diff --git a/entities/device.py b/entities/device.py index ecde354..2599843 100644 --- a/entities/device.py +++ b/entities/device.py @@ -1,8 +1,9 @@ from os import makedirs, path -from typing import Iterable, List +from typing import Iterable, List, Optional from common import escape_string +from entities.attribute import Attribute from .common import ( Author, Category, Created, Deprecated, Description, GeneratedBy, Keywords, Name, StringValue, UUIDValue, Version @@ -36,12 +37,19 @@ def __init__(self, manufacturer: str): class Part(): - def __init__(self, mpn: str, manufacturer: Manufacturer): + def __init__(self, mpn: str, manufacturer: Manufacturer, attributes: Optional[List[Attribute]] = None): self.mpn = mpn self.manufacturer = manufacturer + self.attributes = attributes or [] def __str__(self) -> str: - return '(part "{}" {}\n)'.format(escape_string(self.mpn), self.manufacturer) + ret = '(part "{}" {}\n'.format(escape_string(self.mpn), self.manufacturer) + ret += indent_entities(self.attributes) + ret += ')' + return ret + + def add_attribute(self, attr: Attribute) -> None: + self.attributes.append(attr) class Device(): diff --git a/test_attribute.py b/test_attribute.py new file mode 100644 index 0000000..965e772 --- /dev/null +++ b/test_attribute.py @@ -0,0 +1,72 @@ +from typing import Optional + +import pytest + +from entities.attribute import ( + Attribute, AttributeType, AttributeUnit, CapacitanceAttribute, CapacitanceUnit, CurrentAttribute, CurrentUnit, + FrequencyAttribute, FrequencyUnit, InductanceAttribute, InductanceUnit, PowerAttribute, PowerUnit, + ResistanceAttribute, ResistanceUnit, StringAttribute, UnitlessUnit, VoltageAttribute, VoltageUnit +) + + +@pytest.mark.parametrize(['name', 'value', 'attribute_type', 'unit', 'output'], [ + ("n", "vvv", AttributeType.STRING, None, '(attribute "n" (type string) (unit none) (value "vvv"))'), + ("n", "vvv", AttributeType.STRING, UnitlessUnit.NONE, '(attribute "n" (type string) (unit none) (value "vvv"))'), + ("n", "0.1", AttributeType.CURRENT, CurrentUnit.MILLIAMPERE, '(attribute "n" (type current) (unit milliampere) (value "0.1"))'), + ("n", "0.1", AttributeType.CAPACITANCE, CapacitanceUnit.MILLIFARAD, '(attribute "n" (type capacitance) (unit millifarad) (value "0.1"))'), + ("n", "0.1", AttributeType.FREQUENCY, FrequencyUnit.KILOHERTZ, '(attribute "n" (type frequency) (unit kilohertz) (value "0.1"))'), + ("n", "0.1", AttributeType.INDUCTANCE, InductanceUnit.MICROHENRY, '(attribute "n" (type inductance) (unit microhenry) (value "0.1"))'), + ("n", "0.1", AttributeType.POWER, PowerUnit.WATT, '(attribute "n" (type power) (unit watt) (value "0.1"))'), + ("n", "0.1", AttributeType.RESISTANCE, ResistanceUnit.MEGAOHM, '(attribute "n" (type resistance) (unit megaohm) (value "0.1"))'), + ("n", "0.1", AttributeType.VOLTAGE, VoltageUnit.KILOVOLT, '(attribute "n" (type voltage) (unit kilovolt) (value "0.1"))'), + # add more for new types +]) +def test_attribute(name: str, value: str, attribute_type: AttributeType, unit: Optional[AttributeUnit], output: str) -> None: + attribute_s_exp = str(Attribute(name, value, attribute_type, unit)) + assert attribute_s_exp == output + + +@pytest.mark.parametrize(['attr', 'attribute_type'], [ + (StringAttribute("s", "a"), AttributeType.STRING), + (CurrentAttribute("c", "1", CurrentUnit.AMPERE), AttributeType.CURRENT), + (CapacitanceAttribute("c", "1", CapacitanceUnit.FARAD), AttributeType.CAPACITANCE), + (FrequencyAttribute("f", "1", FrequencyUnit.HERTZ), AttributeType.FREQUENCY), + (InductanceAttribute("i", "1", InductanceUnit.HENRY), AttributeType.INDUCTANCE), + (PowerAttribute("p", "1", PowerUnit.WATT), AttributeType.POWER), + (ResistanceAttribute("r", "1", ResistanceUnit.OHM), AttributeType.RESISTANCE), + (VoltageAttribute("v", "1", VoltageUnit.VOLT), AttributeType.VOLTAGE), + # add more for new types +]) +def test_typed_attribute_has_correct_type(attr: Attribute, attribute_type: AttributeType) -> None: + assert attr.attribute_type == attribute_type + + +@pytest.mark.parametrize('attr', [ + StringAttribute("s", "a"), + # add more for new unitless types +]) +def test_unitless_typed_types_have_unit_none(attr: Attribute) -> None: + assert attr.unit == UnitlessUnit.NONE + + +def test_none_unit_evaluates_to_unitless_none() -> None: + # we pass None as unit + a = Attribute("n", "v", AttributeType.STRING, None) + # check if it gets set to UnitlessUnit.NONE internally + assert a.unit == UnitlessUnit.NONE + + +@pytest.mark.parametrize(['typed', 'general'], [ + (StringAttribute("s", "a"), Attribute("s", "a", AttributeType.STRING, None)), + (StringAttribute("s", "a"), Attribute("s", "a", AttributeType.STRING, UnitlessUnit.NONE)), + (CurrentAttribute("c", "1", CurrentUnit.AMPERE), Attribute("c", "1", AttributeType.CURRENT, CurrentUnit.AMPERE)), + (CapacitanceAttribute("c", "1", CapacitanceUnit.FARAD), Attribute("c", "1", AttributeType.CAPACITANCE, CapacitanceUnit.FARAD)), + (FrequencyAttribute("f", "1", FrequencyUnit.HERTZ), Attribute("f", "1", AttributeType.FREQUENCY, FrequencyUnit.HERTZ)), + (InductanceAttribute("i", "1", InductanceUnit.HENRY), Attribute("i", "1", AttributeType.INDUCTANCE, InductanceUnit.HENRY)), + (PowerAttribute("p", "1", PowerUnit.WATT), Attribute("p", "1", AttributeType.POWER, PowerUnit.WATT)), + (ResistanceAttribute("r", "1", ResistanceUnit.OHM), Attribute("r", "1", AttributeType.RESISTANCE, ResistanceUnit.OHM)), + (VoltageAttribute("v", "1", VoltageUnit.VOLT), Attribute("v", "1", AttributeType.VOLTAGE, VoltageUnit.VOLT)), + # add more for new types +]) +def test_typed_vs_general_attribute_equivalence(typed: Attribute, general: Attribute) -> None: + assert str(typed) == str(general)