change uuid for system_uuid
This commit is contained in:
parent
a19126887a
commit
0f916e5964
|
@ -818,7 +818,7 @@ class Computer(Device):
|
|||
transfer_state.comment = TransferState.__doc__
|
||||
receiver_id = db.Column(UUID(as_uuid=True), db.ForeignKey(User.id), nullable=True)
|
||||
receiver = db.relationship(User, primaryjoin=receiver_id == User.id)
|
||||
uuid = db.Column(UUID(as_uuid=True), nullable=True)
|
||||
system_uuid = db.Column(UUID(as_uuid=True), nullable=True)
|
||||
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
if args:
|
||||
|
|
|
@ -1,17 +1,30 @@
|
|||
import datetime
|
||||
|
||||
from marshmallow import post_load, pre_load, fields as f
|
||||
from marshmallow.fields import Boolean, Date, DateTime, Float, Integer, List, Str, String, UUID, Dict
|
||||
from marshmallow import fields as f
|
||||
from marshmallow import post_load, pre_load
|
||||
from marshmallow.fields import (
|
||||
UUID,
|
||||
Boolean,
|
||||
Date,
|
||||
DateTime,
|
||||
Dict,
|
||||
Float,
|
||||
Integer,
|
||||
List,
|
||||
Str,
|
||||
String,
|
||||
)
|
||||
from marshmallow.validate import Length, OneOf, Range
|
||||
from sqlalchemy.util import OrderedSet
|
||||
from stdnum import imei, meid
|
||||
from teal.enums import Layouts
|
||||
from teal.marshmallow import EnumField, SanitizedStr, URL, ValidationError
|
||||
from teal.marshmallow import URL, EnumField, SanitizedStr, ValidationError
|
||||
from teal.resource import Schema
|
||||
|
||||
from ereuse_devicehub.marshmallow import NestedOn
|
||||
from ereuse_devicehub.resources import enums
|
||||
from ereuse_devicehub.resources.device import models as m, states
|
||||
from ereuse_devicehub.resources.device import models as m
|
||||
from ereuse_devicehub.resources.device import states
|
||||
from ereuse_devicehub.resources.models import STR_BIG_SIZE, STR_SIZE
|
||||
from ereuse_devicehub.resources.schemas import Thing, UnitCodes
|
||||
|
||||
|
@ -20,58 +33,90 @@ class Device(Thing):
|
|||
__doc__ = m.Device.__doc__
|
||||
id = Integer(description=m.Device.id.comment, dump_only=True)
|
||||
hid = SanitizedStr(lower=True, description=m.Device.hid.comment)
|
||||
tags = NestedOn('Tag',
|
||||
many=True,
|
||||
collection_class=OrderedSet,
|
||||
description='A set of tags that identify the device.')
|
||||
model = SanitizedStr(lower=True,
|
||||
validate=Length(max=STR_BIG_SIZE),
|
||||
description=m.Device.model.comment)
|
||||
manufacturer = SanitizedStr(lower=True,
|
||||
validate=Length(max=STR_SIZE),
|
||||
description=m.Device.manufacturer.comment)
|
||||
serial_number = SanitizedStr(lower=True,
|
||||
validate=Length(max=STR_BIG_SIZE),
|
||||
data_key='serialNumber')
|
||||
brand = SanitizedStr(validate=Length(max=STR_BIG_SIZE), description=m.Device.brand.comment)
|
||||
generation = Integer(validate=Range(1, 100), description=m.Device.generation.comment)
|
||||
tags = NestedOn(
|
||||
'Tag',
|
||||
many=True,
|
||||
collection_class=OrderedSet,
|
||||
description='A set of tags that identify the device.',
|
||||
)
|
||||
model = SanitizedStr(
|
||||
lower=True,
|
||||
validate=Length(max=STR_BIG_SIZE),
|
||||
description=m.Device.model.comment,
|
||||
)
|
||||
manufacturer = SanitizedStr(
|
||||
lower=True,
|
||||
validate=Length(max=STR_SIZE),
|
||||
description=m.Device.manufacturer.comment,
|
||||
)
|
||||
serial_number = SanitizedStr(
|
||||
lower=True, validate=Length(max=STR_BIG_SIZE), data_key='serialNumber'
|
||||
)
|
||||
brand = SanitizedStr(
|
||||
validate=Length(max=STR_BIG_SIZE), description=m.Device.brand.comment
|
||||
)
|
||||
generation = Integer(
|
||||
validate=Range(1, 100), description=m.Device.generation.comment
|
||||
)
|
||||
version = SanitizedStr(description=m.Device.version)
|
||||
weight = Float(validate=Range(0.1, 5), unit=UnitCodes.kgm, description=m.Device.weight.comment)
|
||||
width = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.width.comment)
|
||||
height = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.height.comment)
|
||||
depth = Float(validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.depth.comment)
|
||||
weight = Float(
|
||||
validate=Range(0.1, 5), unit=UnitCodes.kgm, description=m.Device.weight.comment
|
||||
)
|
||||
width = Float(
|
||||
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.width.comment
|
||||
)
|
||||
height = Float(
|
||||
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.height.comment
|
||||
)
|
||||
depth = Float(
|
||||
validate=Range(0.1, 5), unit=UnitCodes.m, description=m.Device.depth.comment
|
||||
)
|
||||
# TODO TimeOut 2. Comment actions and lots if there are time out.
|
||||
actions = NestedOn('Action', many=True, dump_only=True, description=m.Device.actions.__doc__)
|
||||
actions = NestedOn(
|
||||
'Action', many=True, dump_only=True, description=m.Device.actions.__doc__
|
||||
)
|
||||
# TODO TimeOut 2. Comment actions_one and lots if there are time out.
|
||||
actions_one = NestedOn('Action', many=True, load_only=True, collection_class=OrderedSet)
|
||||
problems = NestedOn('Action', many=True, dump_only=True, description=m.Device.problems.__doc__)
|
||||
actions_one = NestedOn(
|
||||
'Action', many=True, load_only=True, collection_class=OrderedSet
|
||||
)
|
||||
problems = NestedOn(
|
||||
'Action', many=True, dump_only=True, description=m.Device.problems.__doc__
|
||||
)
|
||||
url = URL(dump_only=True, description=m.Device.url.__doc__)
|
||||
# TODO TimeOut 2. Comment actions and lots if there are time out.
|
||||
lots = NestedOn('Lot',
|
||||
many=True,
|
||||
dump_only=True,
|
||||
description='The lots where this device is directly under.')
|
||||
lots = NestedOn(
|
||||
'Lot',
|
||||
many=True,
|
||||
dump_only=True,
|
||||
description='The lots where this device is directly under.',
|
||||
)
|
||||
rate = NestedOn('Rate', dump_only=True, description=m.Device.rate.__doc__)
|
||||
price = NestedOn('Price', dump_only=True, description=m.Device.price.__doc__)
|
||||
tradings = Dict(dump_only=True, description='')
|
||||
physical = EnumField(states.Physical, dump_only=True, description=m.Device.physical.__doc__)
|
||||
traking = EnumField(states.Traking, dump_only=True, description=m.Device.physical.__doc__)
|
||||
usage = EnumField(states.Usage, dump_only=True, description=m.Device.physical.__doc__)
|
||||
physical = EnumField(
|
||||
states.Physical, dump_only=True, description=m.Device.physical.__doc__
|
||||
)
|
||||
traking = EnumField(
|
||||
states.Traking, dump_only=True, description=m.Device.physical.__doc__
|
||||
)
|
||||
usage = EnumField(
|
||||
states.Usage, dump_only=True, description=m.Device.physical.__doc__
|
||||
)
|
||||
revoke = UUID(dump_only=True)
|
||||
physical_possessor = NestedOn('Agent', dump_only=True, data_key='physicalPossessor')
|
||||
production_date = DateTime('iso',
|
||||
description=m.Device.updated.comment,
|
||||
data_key='productionDate')
|
||||
working = NestedOn('Action',
|
||||
many=True,
|
||||
dump_only=True,
|
||||
description=m.Device.working.__doc__)
|
||||
production_date = DateTime(
|
||||
'iso', description=m.Device.updated.comment, data_key='productionDate'
|
||||
)
|
||||
working = NestedOn(
|
||||
'Action', many=True, dump_only=True, description=m.Device.working.__doc__
|
||||
)
|
||||
variant = SanitizedStr(description=m.Device.variant.comment)
|
||||
sku = SanitizedStr(description=m.Device.sku.comment)
|
||||
image = URL(description=m.Device.image.comment)
|
||||
allocated = Boolean(description=m.Device.allocated.comment)
|
||||
devicehub_id = SanitizedStr(data_key='devicehubID',
|
||||
description=m.Device.devicehub_id.comment)
|
||||
devicehub_id = SanitizedStr(
|
||||
data_key='devicehubID', description=m.Device.devicehub_id.comment
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def from_actions_to_actions_one(self, data: dict):
|
||||
|
@ -91,52 +136,73 @@ class Device(Thing):
|
|||
@post_load
|
||||
def validate_snapshot_actions(self, data):
|
||||
"""Validates that only snapshot-related actions can be uploaded."""
|
||||
from ereuse_devicehub.resources.action.models import EraseBasic, Test, Rate, Install, \
|
||||
Benchmark
|
||||
from ereuse_devicehub.resources.action.models import (
|
||||
Benchmark,
|
||||
EraseBasic,
|
||||
Install,
|
||||
Rate,
|
||||
Test,
|
||||
)
|
||||
|
||||
for action in data['actions_one']:
|
||||
if not isinstance(action, (Install, EraseBasic, Rate, Test, Benchmark)):
|
||||
raise ValidationError('You cannot upload {}'.format(action),
|
||||
field_names=['actions'])
|
||||
raise ValidationError(
|
||||
'You cannot upload {}'.format(action), field_names=['actions']
|
||||
)
|
||||
|
||||
|
||||
class Computer(Device):
|
||||
__doc__ = m.Computer.__doc__
|
||||
# TODO TimeOut 1. Comment components if there are time out.
|
||||
components = NestedOn('Component',
|
||||
many=True,
|
||||
dump_only=True,
|
||||
collection_class=OrderedSet,
|
||||
description='The components that are inside this computer.')
|
||||
chassis = EnumField(enums.ComputerChassis,
|
||||
description=m.Computer.chassis.comment)
|
||||
ram_size = Integer(dump_only=True,
|
||||
data_key='ramSize',
|
||||
description=m.Computer.ram_size.__doc__)
|
||||
data_storage_size = Integer(dump_only=True,
|
||||
data_key='dataStorageSize',
|
||||
description=m.Computer.data_storage_size.__doc__)
|
||||
processor_model = Str(dump_only=True,
|
||||
data_key='processorModel',
|
||||
description=m.Computer.processor_model.__doc__)
|
||||
graphic_card_model = Str(dump_only=True,
|
||||
data_key='graphicCardModel',
|
||||
description=m.Computer.graphic_card_model.__doc__)
|
||||
network_speeds = List(Integer(dump_only=True),
|
||||
dump_only=True,
|
||||
data_key='networkSpeeds',
|
||||
description=m.Computer.network_speeds.__doc__)
|
||||
privacy = NestedOn('Action',
|
||||
many=True,
|
||||
dump_only=True,
|
||||
collection_class=set,
|
||||
description=m.Computer.privacy.__doc__)
|
||||
amount = Integer(validate=f.validate.Range(min=0, max=100),
|
||||
description=m.Computer.amount.__doc__)
|
||||
components = NestedOn(
|
||||
'Component',
|
||||
many=True,
|
||||
dump_only=True,
|
||||
collection_class=OrderedSet,
|
||||
description='The components that are inside this computer.',
|
||||
)
|
||||
chassis = EnumField(enums.ComputerChassis, description=m.Computer.chassis.comment)
|
||||
ram_size = Integer(
|
||||
dump_only=True, data_key='ramSize', description=m.Computer.ram_size.__doc__
|
||||
)
|
||||
data_storage_size = Integer(
|
||||
dump_only=True,
|
||||
data_key='dataStorageSize',
|
||||
description=m.Computer.data_storage_size.__doc__,
|
||||
)
|
||||
processor_model = Str(
|
||||
dump_only=True,
|
||||
data_key='processorModel',
|
||||
description=m.Computer.processor_model.__doc__,
|
||||
)
|
||||
graphic_card_model = Str(
|
||||
dump_only=True,
|
||||
data_key='graphicCardModel',
|
||||
description=m.Computer.graphic_card_model.__doc__,
|
||||
)
|
||||
network_speeds = List(
|
||||
Integer(dump_only=True),
|
||||
dump_only=True,
|
||||
data_key='networkSpeeds',
|
||||
description=m.Computer.network_speeds.__doc__,
|
||||
)
|
||||
privacy = NestedOn(
|
||||
'Action',
|
||||
many=True,
|
||||
dump_only=True,
|
||||
collection_class=set,
|
||||
description=m.Computer.privacy.__doc__,
|
||||
)
|
||||
amount = Integer(
|
||||
validate=f.validate.Range(min=0, max=100), description=m.Computer.amount.__doc__
|
||||
)
|
||||
# author_id = NestedOn(s_user.User, only_query='author_id')
|
||||
owner_id = UUID(data_key='ownerID')
|
||||
transfer_state = EnumField(enums.TransferState, description=m.Computer.transfer_state.comment)
|
||||
transfer_state = EnumField(
|
||||
enums.TransferState, description=m.Computer.transfer_state.comment
|
||||
)
|
||||
receiver_id = UUID(data_key='receiverID')
|
||||
uuid = UUID(required=False)
|
||||
system_uuid = UUID(required=False)
|
||||
|
||||
|
||||
class Desktop(Computer):
|
||||
|
@ -155,29 +221,36 @@ class Server(Computer):
|
|||
class DisplayMixin:
|
||||
__doc__ = m.DisplayMixin.__doc__
|
||||
size = Float(description=m.DisplayMixin.size.comment, validate=Range(2, 150))
|
||||
technology = EnumField(enums.DisplayTech,
|
||||
description=m.DisplayMixin.technology.comment)
|
||||
resolution_width = Integer(data_key='resolutionWidth',
|
||||
validate=Range(10, 20000),
|
||||
description=m.DisplayMixin.resolution_width.comment,
|
||||
)
|
||||
resolution_height = Integer(data_key='resolutionHeight',
|
||||
validate=Range(10, 20000),
|
||||
description=m.DisplayMixin.resolution_height.comment,
|
||||
)
|
||||
technology = EnumField(
|
||||
enums.DisplayTech, description=m.DisplayMixin.technology.comment
|
||||
)
|
||||
resolution_width = Integer(
|
||||
data_key='resolutionWidth',
|
||||
validate=Range(10, 20000),
|
||||
description=m.DisplayMixin.resolution_width.comment,
|
||||
)
|
||||
resolution_height = Integer(
|
||||
data_key='resolutionHeight',
|
||||
validate=Range(10, 20000),
|
||||
description=m.DisplayMixin.resolution_height.comment,
|
||||
)
|
||||
refresh_rate = Integer(data_key='refreshRate', validate=Range(10, 1000))
|
||||
contrast_ratio = Integer(data_key='contrastRatio', validate=Range(100, 100000))
|
||||
touchable = Boolean(description=m.DisplayMixin.touchable.comment)
|
||||
aspect_ratio = String(dump_only=True, description=m.DisplayMixin.aspect_ratio.__doc__)
|
||||
aspect_ratio = String(
|
||||
dump_only=True, description=m.DisplayMixin.aspect_ratio.__doc__
|
||||
)
|
||||
widescreen = Boolean(dump_only=True, description=m.DisplayMixin.widescreen.__doc__)
|
||||
|
||||
|
||||
class NetworkMixin:
|
||||
__doc__ = m.NetworkMixin.__doc__
|
||||
|
||||
speed = Integer(validate=Range(min=10, max=10000),
|
||||
unit=UnitCodes.mbps,
|
||||
description=m.NetworkAdapter.speed.comment)
|
||||
speed = Integer(
|
||||
validate=Range(min=10, max=10000),
|
||||
unit=UnitCodes.mbps,
|
||||
description=m.NetworkAdapter.speed.comment,
|
||||
)
|
||||
wireless = Boolean(required=True)
|
||||
|
||||
|
||||
|
@ -198,16 +271,22 @@ class Mobile(Device):
|
|||
|
||||
imei = Integer(description=m.Mobile.imei.comment)
|
||||
meid = Str(description=m.Mobile.meid.comment)
|
||||
ram_size = Integer(validate=Range(min=128, max=36000),
|
||||
data_key='ramSize',
|
||||
unit=UnitCodes.mbyte,
|
||||
description=m.Mobile.ram_size.comment)
|
||||
data_storage_size = Integer(validate=Range(0, 10 ** 8),
|
||||
data_key='dataStorageSize',
|
||||
description=m.Mobile.data_storage_size)
|
||||
display_size = Float(validate=Range(min=0.1, max=30.0),
|
||||
data_key='displaySize',
|
||||
description=m.Mobile.display_size.comment)
|
||||
ram_size = Integer(
|
||||
validate=Range(min=128, max=36000),
|
||||
data_key='ramSize',
|
||||
unit=UnitCodes.mbyte,
|
||||
description=m.Mobile.ram_size.comment,
|
||||
)
|
||||
data_storage_size = Integer(
|
||||
validate=Range(0, 10**8),
|
||||
data_key='dataStorageSize',
|
||||
description=m.Mobile.data_storage_size,
|
||||
)
|
||||
display_size = Float(
|
||||
validate=Range(min=0.1, max=30.0),
|
||||
data_key='displaySize',
|
||||
description=m.Mobile.display_size.comment,
|
||||
)
|
||||
|
||||
@pre_load
|
||||
def convert_check_imei(self, data):
|
||||
|
@ -243,17 +322,21 @@ class Component(Device):
|
|||
class GraphicCard(Component):
|
||||
__doc__ = m.GraphicCard.__doc__
|
||||
|
||||
memory = Integer(validate=Range(0, 10000),
|
||||
unit=UnitCodes.mbyte,
|
||||
description=m.GraphicCard.memory.comment)
|
||||
memory = Integer(
|
||||
validate=Range(0, 10000),
|
||||
unit=UnitCodes.mbyte,
|
||||
description=m.GraphicCard.memory.comment,
|
||||
)
|
||||
|
||||
|
||||
class DataStorage(Component):
|
||||
__doc__ = m.DataStorage.__doc__
|
||||
|
||||
size = Integer(validate=Range(0, 10 ** 8),
|
||||
unit=UnitCodes.mbyte,
|
||||
description=m.DataStorage.size.comment)
|
||||
size = Integer(
|
||||
validate=Range(0, 10**8),
|
||||
unit=UnitCodes.mbyte,
|
||||
description=m.DataStorage.size.comment,
|
||||
)
|
||||
interface = EnumField(enums.DataStorageInterface)
|
||||
privacy = NestedOn('Action', dump_only=True)
|
||||
|
||||
|
@ -269,16 +352,21 @@ class SolidStateDrive(DataStorage):
|
|||
class Motherboard(Component):
|
||||
__doc__ = m.Motherboard.__doc__
|
||||
|
||||
slots = Integer(validate=Range(0, 20),
|
||||
description=m.Motherboard.slots.comment)
|
||||
slots = Integer(validate=Range(0, 20), description=m.Motherboard.slots.comment)
|
||||
usb = Integer(validate=Range(0, 20), description=m.Motherboard.usb.comment)
|
||||
firewire = Integer(validate=Range(0, 20), description=m.Motherboard.firewire.comment)
|
||||
firewire = Integer(
|
||||
validate=Range(0, 20), description=m.Motherboard.firewire.comment
|
||||
)
|
||||
serial = Integer(validate=Range(0, 20), description=m.Motherboard.serial.comment)
|
||||
pcmcia = Integer(validate=Range(0, 20), description=m.Motherboard.pcmcia.comment)
|
||||
bios_date = Date(validate=Range(datetime.date(year=1980, month=1, day=1),
|
||||
datetime.date(year=2030, month=1, day=1)),
|
||||
data_key='biosDate',
|
||||
description=m.Motherboard.bios_date)
|
||||
bios_date = Date(
|
||||
validate=Range(
|
||||
datetime.date(year=1980, month=1, day=1),
|
||||
datetime.date(year=2030, month=1, day=1),
|
||||
),
|
||||
data_key='biosDate',
|
||||
description=m.Motherboard.bios_date,
|
||||
)
|
||||
ram_slots = Integer(validate=Range(1), data_key='ramSlots')
|
||||
ram_max_size = Integer(validate=Range(1), data_key='ramMaxSize')
|
||||
|
||||
|
@ -290,22 +378,32 @@ class NetworkAdapter(NetworkMixin, Component):
|
|||
class Processor(Component):
|
||||
__doc__ = m.Processor.__doc__
|
||||
|
||||
speed = Float(validate=Range(min=0.1, max=15),
|
||||
unit=UnitCodes.ghz,
|
||||
description=m.Processor.speed.comment)
|
||||
cores = Integer(validate=Range(min=1, max=10), description=m.Processor.cores.comment)
|
||||
threads = Integer(validate=Range(min=1, max=20), description=m.Processor.threads.comment)
|
||||
address = Integer(validate=OneOf({8, 16, 32, 64, 128, 256}),
|
||||
description=m.Processor.address.comment)
|
||||
speed = Float(
|
||||
validate=Range(min=0.1, max=15),
|
||||
unit=UnitCodes.ghz,
|
||||
description=m.Processor.speed.comment,
|
||||
)
|
||||
cores = Integer(
|
||||
validate=Range(min=1, max=10), description=m.Processor.cores.comment
|
||||
)
|
||||
threads = Integer(
|
||||
validate=Range(min=1, max=20), description=m.Processor.threads.comment
|
||||
)
|
||||
address = Integer(
|
||||
validate=OneOf({8, 16, 32, 64, 128, 256}),
|
||||
description=m.Processor.address.comment,
|
||||
)
|
||||
abi = SanitizedStr(lower=True, description=m.Processor.abi.comment)
|
||||
|
||||
|
||||
class RamModule(Component):
|
||||
__doc__ = m.RamModule.__doc__
|
||||
|
||||
size = Integer(validate=Range(min=128, max=17000),
|
||||
unit=UnitCodes.mbyte,
|
||||
description=m.RamModule.size.comment)
|
||||
size = Integer(
|
||||
validate=Range(min=128, max=17000),
|
||||
unit=UnitCodes.mbyte,
|
||||
description=m.RamModule.size.comment,
|
||||
)
|
||||
speed = Integer(validate=Range(min=100, max=10000), unit=UnitCodes.mhz)
|
||||
interface = EnumField(enums.RamInterface)
|
||||
format = EnumField(enums.RamFormat)
|
||||
|
@ -323,7 +421,9 @@ class Battery(Component):
|
|||
__doc__ = m.Battery.__doc__
|
||||
|
||||
wireless = Boolean(description=m.Battery.wireless.comment)
|
||||
technology = EnumField(enums.BatteryTechnology, description=m.Battery.technology.comment)
|
||||
technology = EnumField(
|
||||
enums.BatteryTechnology, description=m.Battery.technology.comment
|
||||
)
|
||||
size = Integer(required=True, description=m.Battery.size.comment)
|
||||
|
||||
|
||||
|
@ -393,12 +493,18 @@ class WirelessAccessPoint(Networking):
|
|||
class Printer(Device):
|
||||
__doc__ = m.Printer.__doc__
|
||||
|
||||
wireless = Boolean(required=True, missing=False, description=m.Printer.wireless.comment)
|
||||
scanning = Boolean(required=True, missing=False, description=m.Printer.scanning.comment)
|
||||
technology = EnumField(enums.PrinterTechnology,
|
||||
required=True,
|
||||
description=m.Printer.technology.comment)
|
||||
monochrome = Boolean(required=True, missing=True, description=m.Printer.monochrome.comment)
|
||||
wireless = Boolean(
|
||||
required=True, missing=False, description=m.Printer.wireless.comment
|
||||
)
|
||||
scanning = Boolean(
|
||||
required=True, missing=False, description=m.Printer.scanning.comment
|
||||
)
|
||||
technology = EnumField(
|
||||
enums.PrinterTechnology, required=True, description=m.Printer.technology.comment
|
||||
)
|
||||
monochrome = Boolean(
|
||||
required=True, missing=True, description=m.Printer.monochrome.comment
|
||||
)
|
||||
|
||||
|
||||
class LabelPrinter(Printer):
|
||||
|
|
Reference in a new issue