first iteration new rate v2
This commit is contained in:
parent
4bfadae267
commit
40a2ed3916
|
@ -40,7 +40,9 @@ There are three types of rating a device, depend on the aspect you are focusing
|
|||
2. `Functionality Rate`
|
||||
3. `Appearance Rate`
|
||||
4. `Final Rate`
|
||||
|
||||
1. ComputerRate
|
||||
2. MobileRate
|
||||
3. ManualRate
|
||||
To structure device events information, create two different main class Benchmark and Test.
|
||||
Benchmark class is part of the **quality rate** and Test class is part of the **functionality rate**.
|
||||
|
||||
|
@ -51,63 +53,17 @@ The following will explain in more detail the three types of rate. Rate class is
|
|||
|
||||
**1. Quality Rate**
|
||||
|
||||
Device components immutable characteristics list:
|
||||
|
||||
* Display (screen):
|
||||
- Size (inch)
|
||||
- Resolution (pixels)
|
||||
- Density (ppi)
|
||||
|
||||
* Processor:
|
||||
- Number of cores (Natural)
|
||||
- Speed (GHz)
|
||||
|
||||
* RAM:
|
||||
- Size (GB)
|
||||
- Speed (MHz)
|
||||
|
||||
* Data Storage:
|
||||
- Size (GB)
|
||||
|
||||
* Battery:
|
||||
- Size (mAh)
|
||||
- Voltage (V)
|
||||
|
||||
* Camera:
|
||||
- Resolution (MP)
|
||||
|
||||
* Benchmark:
|
||||
The act of gauging the performance of a device.
|
||||
This subclasses define which functionality aspects take into account in score.
|
||||
|
||||
- Benchmark DataStorage
|
||||
- Benchmark RamSysbench
|
||||
- Benchmark Processor
|
||||
- Benchmark Graphic Card (TODO)
|
||||
-
|
||||
|
||||
** Natural: a positive number that does not have decimals.
|
||||
Device components immutable characteristics and Benchmark, the act of gauging the performance of a device.
|
||||
|
||||
**2. Functionality Rate**
|
||||
|
||||
* Test:
|
||||
The act of testing usage condition of a device and its functionality aspects/characteristics.
|
||||
Following standard R2 specification*:
|
||||
|
||||
- Test DataStorage (SMART Test) //TODO which SMART test vars important for func aspect??
|
||||
- Test Processor (Stress Test) //TODO which SMART test vars important for func aspect??
|
||||
- Test Bios (Manual Test)
|
||||
- Test Audio (Manual Test)
|
||||
- Test Battery
|
||||
- Test Connectivity
|
||||
-
|
||||
Test, the act of testing usage condition of a device and its functionality aspects/characteristics. Following standard R2 specification*.
|
||||
|
||||
**3. Appearance Rate**
|
||||
|
||||
* Visual Test:
|
||||
It is a test of appearance that its aspects are represented with grade.
|
||||
It focuses mainly on the aesthetic or cosmetic defects of important parts of the device,
|
||||
such as the chassis, display (screen) and cameras.
|
||||
Mainly is compute using the results of a visual Test, it is a test of appearance that its aspects are represented with grade.
|
||||
It focuses mainly on the aesthetic or cosmetic defects of important parts of the device, such as the chassis, display (screen) and cameras.
|
||||
|
||||
|
||||
Below is explained in more detail how the calculations and formulas that are used to compute the score of a device.
|
||||
|
||||
|
|
|
@ -374,7 +374,8 @@ class Computer(Device):
|
|||
|
||||
1. The max Ethernet speed of the computer, 0 if ethernet
|
||||
adaptor exists but its speed is unknown, None if no eth
|
||||
adaptor exists.
|
||||
adaptor exists.# TODO add all grade tables (chassis defects, camera defects, buttons test, connectivity, ..)
|
||||
|
||||
2. The max WiFi speed of the computer, 0 if computer has
|
||||
WiFi but its speed is unknown, None if no WiFi adaptor
|
||||
exists.
|
||||
|
@ -452,14 +453,6 @@ class Mobile(Device):
|
|||
The Mobile Equipment Identifier as a hexadecimal string.
|
||||
"""
|
||||
|
||||
# TODO Add more characteristics, like display resolution, battery health, graphic card, ...
|
||||
display_size = Column(Float(decimal_return_scale=2))
|
||||
display_size.comment = 'Display size; Units: inches;'
|
||||
battery_size = Column(Float(decimal_return_scale=2))
|
||||
battery_size.comment = 'Battery size; Units: mAh'
|
||||
camera_resolution = Column(Float(decimal_return_scale=2))
|
||||
camera_resolution.comment = 'Camera resolution; Units: Megapixels'
|
||||
|
||||
@validates('imei')
|
||||
def validate_imei(self, _, value: int):
|
||||
if not imei.is_valid(str(value)):
|
||||
|
@ -763,3 +756,32 @@ class Manufacturer(db.Model):
|
|||
'COPY common.manufacturer FROM STDIN (FORMAT csv)',
|
||||
f
|
||||
)
|
||||
|
||||
|
||||
class Battery(Component):
|
||||
"""
|
||||
Battery component, mobile device
|
||||
"""
|
||||
size = Column(Float(decimal_return_scale=2))
|
||||
size.comment = 'Battery size; Units: 2000 mAh'
|
||||
voltage = Column()
|
||||
voltage.comment = 'Battery voltage; Units: 3,5 V'
|
||||
technology = Column()
|
||||
technology.comment = 'Battery technology used; Ex: Li-Ti'
|
||||
charge_counter = Column()
|
||||
charge_counter.comment = 'Number of time that battery has been charged'
|
||||
wireless = Column(Boolean, nullable=False, default=False)
|
||||
wireless.comment = 'If battery can charging wireless'
|
||||
health = Column(DBEnum)
|
||||
health.comment = 'Battery health indicator'
|
||||
status = Column(DBEnum)
|
||||
status.comment = 'Battery status indicator'
|
||||
|
||||
|
||||
class Camera(Component):
|
||||
"""
|
||||
Component camera in mobile devices
|
||||
"""
|
||||
|
||||
resolution = Column(Float(decimal_return_scale=2))
|
||||
resolution.comment = 'Camera resolution; Units: Megapixels'
|
||||
|
|
|
@ -94,20 +94,6 @@ class AggregateRatingVersions(Enum):
|
|||
|
||||
@unique
|
||||
class AppearanceRange(Enum):
|
||||
"""Grades the imperfections that aesthetically affect the device, but not its usage."""
|
||||
Z = '0. The device is new.'
|
||||
A = 'A. Is like new (without visual damage)'
|
||||
B = 'B. Is in really good condition (small visual damage in difficult places to spot)'
|
||||
C = 'C. Is in good condition (small visual damage in parts that are easy to spot, not screens)'
|
||||
D = 'D. Is acceptable (visual damage in visible parts, not screens)'
|
||||
E = 'E. Is unacceptable (considerable visual damage that can affect usage)'
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@unique
|
||||
class AppearanceRangev2(Enum):
|
||||
"""Based on usage condition of a device and its functionality aspects/characteristics."""
|
||||
Z = 'Z. The device is new'
|
||||
A = 'A. Is like new; without visual damage'
|
||||
|
@ -126,22 +112,6 @@ APPEARANCE_RANGE = 0.5, -0.3
|
|||
|
||||
@unique
|
||||
class FunctionalityRange(Enum):
|
||||
"""Grades the defects of a device that affect its usage."""
|
||||
# todo sync with https://github.com/ereuse/rdevicescore#input
|
||||
A = 'A. Everything works perfectly (buttons, and in case of screens there are no scratches)'
|
||||
B = 'B. There is a button difficult to press or a small scratch in an edge of a screen'
|
||||
C = 'C. A non-important button (or similar) doesn\'t work; screen has multiple scratches in edges'
|
||||
D = 'D. Multiple buttons don\'t work; screen has visual damage resulting in uncomfortable usage'
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
FUNCTIONALITY_RANGE = -0.25, 0.5
|
||||
|
||||
|
||||
@unique
|
||||
class FunctionalityRangev2(Enum):
|
||||
"""Grade the buttons and chassis that affect its usage, like screen defect or camera defects"""
|
||||
A = 'A. All the buttons works perfectly, no screen/camera defects and chassis without issues'
|
||||
B = 'B. There is a button difficult to press or unstable it, a screen/camera defect or chassis problem'
|
||||
|
@ -153,6 +123,9 @@ class FunctionalityRangev2(Enum):
|
|||
return self.name
|
||||
|
||||
|
||||
FUNCTIONALITY_RANGE = -0.25, 0.5
|
||||
|
||||
|
||||
@unique
|
||||
class BatteryHealthRange(Enum):
|
||||
"""Grade the battery health status, depending on self report Android system"""
|
||||
|
|
|
@ -31,8 +31,7 @@ from ereuse_devicehub.resources.device.models import Component, Computer, DataSt
|
|||
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, ErasureStandards, \
|
||||
FunctionalityRange, PhysicalErasureMethod, PriceSoftware, RATE_NEGATIVE, RATE_POSITIVE, \
|
||||
RatingRange, RatingSoftware, ReceiverRole, Severity, SnapshotExpectedEvents, SnapshotSoftware, \
|
||||
TestDataStorageLength, FUNCTIONALITY_RANGE, FunctionalityRangev2, AppearanceRangev2, BatteryHealthRange
|
||||
from ereuse_devicehub.resources.event.rate.workbench.v2_0 import QualityRate, FunctionalityRate
|
||||
TestDataStorageLength, FUNCTIONALITY_RANGE, FunctionalityRange, AppearanceRange, BatteryHealthRange
|
||||
from ereuse_devicehub.resources.models import STR_SM_SIZE, Thing
|
||||
from ereuse_devicehub.resources.user.models import User
|
||||
|
||||
|
@ -718,15 +717,15 @@ class TestConnectivity(Test):
|
|||
"""
|
||||
Test to check all this aspects related with functionality connections in devices
|
||||
"""
|
||||
|
||||
SIM = Column(Boolean)
|
||||
# TODO name for SIM (3G)
|
||||
celular_network = Column(Boolean)
|
||||
SIM.comment = 'Evaluate if SIM works'
|
||||
wifi = Column(Boolean)
|
||||
wifi.comment = 'Evaluate if wifi connection works correctly'
|
||||
bluetooth = Column(Boolean)
|
||||
bluetooth.comment = 'Evaluate if bluetooth works'
|
||||
usb = Column(DBEnum(USBPortRange))
|
||||
usb.comment = 'Evaluate if usb port was detected and charger plug works'
|
||||
usb_port = Column(DBEnum(USBPortRange))
|
||||
usb_port.comment = 'Evaluate if usb port was detected and charger plug works'
|
||||
|
||||
|
||||
class TestBattery(Test):
|
||||
|
@ -744,13 +743,18 @@ class TestBattery(Test):
|
|||
|
||||
class TestBios(Test):
|
||||
"""
|
||||
Test that determinate motherboard no beeps, codes or errors when power on,
|
||||
and a grade to reflect some possibles difficult to access or modify setting in the BIOS, like password protection..
|
||||
Test that determinate motherboard no beeps, codes or errors when power on
|
||||
"""
|
||||
bios_power_on = Column(Boolean())
|
||||
bios_power_on.comment = """
|
||||
Motherboards do a self check when powering up (R2 p.23), test PASS if no beeps, codes, or errors appears.
|
||||
"""
|
||||
|
||||
|
||||
class TestBiosDifficulty:
|
||||
"""
|
||||
Test to determinate a grade to reflect some possibles difficult to access or modify setting in the BIOS, like password protection..
|
||||
"""
|
||||
# TODO Eum(BiosAccesRange)
|
||||
bios_access_range = Column(BDEnum(BiosAccessRange))
|
||||
bios_access_range.comment = 'Range of difficult to acces BIOS'
|
||||
|
@ -763,8 +767,8 @@ class TestVisual(ManualRate):
|
|||
Like defects on chassis, display, ..
|
||||
"""
|
||||
# TODO Consider if add some new var in appearance aspect??
|
||||
appearance_range = Column(DBEnum(AppearanceRangev2))
|
||||
appearance_range.comment = AppearanceRangev2.__doc__
|
||||
appearance_range = Column(DBEnum(AppearanceRange))
|
||||
appearance_range.comment = AppearanceRange.__doc__
|
||||
|
||||
|
||||
class Rate(JoinedWithOneDeviceMixin, EventWithOneDevice):
|
||||
|
@ -844,7 +848,7 @@ class IndividualRate(Rate):
|
|||
pass
|
||||
|
||||
|
||||
class ManualRate(IndividualRate):
|
||||
class RateManual(IndividualRate):
|
||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||
labelling = Column(Boolean)
|
||||
labelling.comment = """Sets if there are labels stuck that should
|
||||
|
@ -865,111 +869,18 @@ class ManualRate(IndividualRate):
|
|||
raise NotImplementedError()
|
||||
|
||||
|
||||
# TODO is necessary?
|
||||
class WorkbenchComputer(ManualRate):
|
||||
class RateComputer(IndividualRate):
|
||||
"""
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class WorkbenchMobile(ManualRate):
|
||||
class RateMobile(IndividualRate):
|
||||
"""
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class AggregateRate(Rate):
|
||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||
manual_id = Column(UUID(as_uuid=True), ForeignKey(ManualRate.id))
|
||||
manual_id.comment = """The ManualEvent used to generate this
|
||||
aggregation, or None if none used.
|
||||
|
||||
An example of ManualEvent is using the web or the Android app
|
||||
to rate a device.
|
||||
"""
|
||||
manual = relationship(ManualRate,
|
||||
backref=backref('aggregate_rate_manual',
|
||||
lazy=True,
|
||||
order_by=lambda: AggregateRate.created,
|
||||
collection_class=OrderedSet),
|
||||
primaryjoin=manual_id == ManualRate.id)
|
||||
workbench_id = Column(UUID(as_uuid=True), ForeignKey(QualityRateComputer.id))
|
||||
workbench_id.comment = """The WorkbenchRate used to generate
|
||||
this aggregation, or None if none used.
|
||||
"""
|
||||
workbench = relationship(QualityRateComputer,
|
||||
backref=backref('aggregate_rate_workbench',
|
||||
lazy=True,
|
||||
order_by=lambda: AggregateRate.created,
|
||||
collection_class=OrderedSet),
|
||||
primaryjoin=workbench_id == QualityRateComputer.id)
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
kwargs.setdefault('version', StrictVersion('1.0'))
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# todo take value from LAST event (manual or workbench)
|
||||
|
||||
@property
|
||||
def processor(self):
|
||||
return self.workbench.processor
|
||||
|
||||
@property
|
||||
def ram(self):
|
||||
return self.workbench.ram
|
||||
|
||||
@property
|
||||
def data_storage(self):
|
||||
return self.workbench.data_storage
|
||||
|
||||
@property
|
||||
def graphic_card(self):
|
||||
return self.workbench.graphic_card
|
||||
|
||||
@property
|
||||
def data_storage_range(self):
|
||||
return self.workbench.data_storage_range
|
||||
|
||||
@property
|
||||
def ram_range(self):
|
||||
return self.workbench.ram_range
|
||||
|
||||
@property
|
||||
def processor_range(self):
|
||||
return self.workbench.processor_range
|
||||
|
||||
@property
|
||||
def graphic_card_range(self):
|
||||
return self.workbench.graphic_card_range
|
||||
|
||||
@property
|
||||
def bios(self):
|
||||
return self.workbench.bios
|
||||
|
||||
@property
|
||||
def functionality_range(self):
|
||||
return self.workbench.functionality_range
|
||||
|
||||
@property
|
||||
def appearance_range(self):
|
||||
return self.workbench.appearance_range
|
||||
|
||||
@property
|
||||
def bios_range(self):
|
||||
return self.workbench.bios_range
|
||||
|
||||
@property
|
||||
def labelling(self):
|
||||
return self.workbench.labelling
|
||||
|
||||
@classmethod
|
||||
def from_workbench_rate(cls, rate: QualityRate):
|
||||
aggregate = cls()
|
||||
aggregate.rating = rate.rating
|
||||
aggregate.software = rate.software
|
||||
aggregate.appearance = rate.appearance
|
||||
aggregate.functionality = rate.functionality
|
||||
aggregate.device = rate.device
|
||||
aggregate.workbench = rate
|
||||
return aggregate
|
||||
|
||||
|
||||
class QualityRate(Rate):
|
||||
"""
|
||||
The act of compute performance (quality) a device
|
||||
|
@ -982,7 +893,6 @@ class QualityRate(Rate):
|
|||
data_storage = Column(Float(decimal_return_scale=2), check_range('data_storage', *RATE_POSITIVE),
|
||||
comment='Data storage rate, like HHD, SSD.')
|
||||
|
||||
""" MOBILE QUALITY RATE """
|
||||
display = Column(Float(decimal_return_scale=2), check_range('display', *RATE_POSITIVE))
|
||||
display.comment = 'Display rate, screen resolution and size to calculate PPI and convert in score'
|
||||
battery = Column(Float(decimal_return_scale=2), check_range('battery', *RATE_POSITIVE),
|
||||
|
@ -990,7 +900,6 @@ class QualityRate(Rate):
|
|||
camera = Column(Float(decimal_return_scale=2), check_range('camera', *RATE_POSITIVE),
|
||||
comment='Camera rate take into account resolution')
|
||||
|
||||
""" COMPUTER QUALITY RATE """
|
||||
graphic_card = Column(Float(decimal_return_scale=2), check_range('graphic_card', *RATE_POSITIVE),
|
||||
comment='Graphic card score in performance, amount of memory and benchmark result')
|
||||
network_adapter = Column(Float(decimal_return_scale=2), check_range('network_adapter', *RATE_POSITIVE),
|
||||
|
@ -1047,8 +956,8 @@ class FunctionalityRate(Rate):
|
|||
functionality = Column(Float(decimal_return_scale=2), check_range('functionality', *FUNCTIONALITY_RANGE))
|
||||
functionality.comment = 'Functionality rate of a device'
|
||||
|
||||
functionality_range = Column(DBEnum(FunctionalityRangev2))
|
||||
functionality_range.comment = FunctionalityRangev2.__doc__
|
||||
functionality_range = Column(DBEnum(FunctionalityRange))
|
||||
functionality_range.comment = FunctionalityRange.__doc__
|
||||
|
||||
|
||||
class FinalRate(Rate):
|
||||
|
@ -1062,20 +971,27 @@ class FinalRate(Rate):
|
|||
4. ``Market value``.
|
||||
5. ``Cost of repair``.
|
||||
|
||||
We define 5 categories to measure:
|
||||
|
||||
There are different types of rating a device:
|
||||
|
||||
1. Rate Quality
|
||||
2. Rate Functionality
|
||||
3. Rate Final
|
||||
|
||||
• Functionality (F). Answers to “does the machine work well?” Condition tests fall in here.
|
||||
• Appearance (A). Aesthetic evaluation, surface deterioration.
|
||||
• Quality (Q). How good is the machine, in terms of performance. Benchmarks, power, and characteristics fall here. The quality score, that represents the performance of the device, the functionality score, that based on the correct working of the buttons, audio and connectivity aspects, and the appaerance score, that focused on the aesthetics and cosmetics aspects like visual damage on screen, buttons or cabinet.
|
||||
• Market value (MV). Perceived value, brand recognition, selling value.
|
||||
• Cost of repair, refurbish and manufacture. ( C )
|
||||
|
||||
List of source where can input information of rating a device:
|
||||
|
||||
1. When processing the device with Workbench Computer/Mobile.
|
||||
1. When processing the device with Workbench Computer or WB Mobile.
|
||||
2. Using the Android App (through Scan).
|
||||
3.
|
||||
3. ...
|
||||
4. Anytime after manually written in a form in the website.
|
||||
|
||||
There are three types of rating a device, depend on the aspect you are focusing on:
|
||||
|
||||
1. `Quality Rate`
|
||||
2. `Functionality Rate`
|
||||
3. `Appearance Rate`
|
||||
4. `Final Rate`
|
||||
"""
|
||||
|
||||
id = Column(UUID(as_uuid=True), ForeignKey(Rate.id), primary_key=True)
|
||||
|
@ -1179,7 +1095,6 @@ class FinalRate(Rate):
|
|||
def cost_of_repair_category(cls, quality: QualityRate):
|
||||
pass
|
||||
|
||||
|
||||
# todo take value from LAST event (manual or workbench)
|
||||
|
||||
@property
|
||||
|
|
|
@ -14,7 +14,7 @@ from ereuse_devicehub.resources.agent import schemas as s_agent
|
|||
from ereuse_devicehub.resources.device import schemas as s_device
|
||||
from ereuse_devicehub.resources.enums import AppearanceRange, Bios, FunctionalityRange, \
|
||||
PhysicalErasureMethod, PriceSoftware, RATE_POSITIVE, RatingRange, RatingSoftware, ReceiverRole, \
|
||||
Severity, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength, FunctionalityRangev2
|
||||
Severity, SnapshotExpectedEvents, SnapshotSoftware, TestDataStorageLength, FunctionalityRange
|
||||
from ereuse_devicehub.resources.event import models as m
|
||||
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
||||
from ereuse_devicehub.resources.schemas import Thing
|
||||
|
@ -213,7 +213,7 @@ class ManualRate(IndividualRate):
|
|||
labelling = Boolean(description=m.ManualRate.labelling.comment)
|
||||
|
||||
|
||||
class WorkbencComputer(ManualRate):
|
||||
class RateComputer(ManualRate):
|
||||
__doc__ = m.WorkbenchRate.__doc__
|
||||
processor = Float()
|
||||
ram = Float()
|
||||
|
@ -229,43 +229,10 @@ class WorkbencComputer(ManualRate):
|
|||
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
|
||||
|
||||
|
||||
class WorkbenchMobile(ManualRate):
|
||||
class RateMobile(ManualRate):
|
||||
pass
|
||||
|
||||
|
||||
class AggregateRate(Rate):
|
||||
__doc__ = m.AggregateRate.__doc__
|
||||
workbench = NestedOn(WorkbenchRate, dump_only=True,
|
||||
description=m.AggregateRate.workbench_id.comment)
|
||||
manual = NestedOn(ManualRate,
|
||||
dump_only=True,
|
||||
description=m.AggregateRate.manual_id.comment)
|
||||
processor = Float(dump_only=True)
|
||||
ram = Float(dump_only=True)
|
||||
data_storage = Float(dump_only=True)
|
||||
graphic_card = Float(dump_only=True)
|
||||
bios = EnumField(Bios, dump_only=True)
|
||||
bios_range = EnumField(Bios,
|
||||
description=m.WorkbenchRate.bios_range.comment,
|
||||
data_key='biosRange')
|
||||
appearance_range = EnumField(AppearanceRange,
|
||||
required=True,
|
||||
data_key='appearanceRange',
|
||||
description=m.ManualRate.appearance_range.comment)
|
||||
functionality_range = EnumField(FunctionalityRange,
|
||||
required=True,
|
||||
data_key='functionalityRange',
|
||||
description=m.ManualRate.functionality_range.comment)
|
||||
labelling = Boolean(description=m.ManualRate.labelling.comment)
|
||||
data_storage_range = EnumField(RatingRange, dump_only=True, data_key='dataStorageRange')
|
||||
ram_range = EnumField(RatingRange, dump_only=True, data_key='ramRange')
|
||||
processor_range = EnumField(RatingRange, dump_only=True, data_key='processorRange')
|
||||
graphic_card_range = EnumField(RatingRange, dump_only=True, data_key='graphicCardRange')
|
||||
|
||||
|
||||
""" RATE v2 CODE"""
|
||||
|
||||
|
||||
class QualityRate(Rate):
|
||||
__doc__ = m.QualityRate.__doc__
|
||||
|
||||
|
@ -310,7 +277,7 @@ class FinalRate(Rate):
|
|||
required=True,
|
||||
data_key='appearanceRangev2',
|
||||
description=m.ManualRate.appearance_range.comment)
|
||||
functionality_range = EnumField(FunctionalityRangev2,
|
||||
functionality_range = EnumField(FunctionalityRange,
|
||||
required=True,
|
||||
data_key='functionalityRangev2',
|
||||
description=m.ManualRate.functionality_range.comment)
|
||||
|
|
Reference in New Issue