rework of forklift agent loop
This commit is contained in:
parent
b37847b304
commit
5004058725
133
ForkliftAgent.py
133
ForkliftAgent.py
@ -1,8 +1,10 @@
|
|||||||
|
from copy import deepcopy
|
||||||
from typing import Tuple, List
|
from typing import Tuple, List
|
||||||
|
|
||||||
from AgentBase import AgentBase
|
from AgentBase import AgentBase
|
||||||
from PatchAgent import PatchAgent
|
from PatchAgent import PatchAgent
|
||||||
from PatchType import PatchType
|
from PatchType import PatchType
|
||||||
|
from data.GameConstants import GameConstants
|
||||||
from data.Item import Item
|
from data.Item import Item
|
||||||
from data.Order import Order
|
from data.Order import Order
|
||||||
from data.enum.Direction import Direction
|
from data.enum.Direction import Direction
|
||||||
@ -16,7 +18,7 @@ from util.PathDefinitions import GridLocation, GridWithWeights
|
|||||||
|
|
||||||
class ForkliftAgent(AgentBase):
|
class ForkliftAgent(AgentBase):
|
||||||
|
|
||||||
def __init__(self, model, game_constants, client_delivery: PatchAgent, drop_off: PatchAgent,
|
def __init__(self, model, game_constants: GameConstants, client_delivery: PatchAgent, drop_off: PatchAgent,
|
||||||
graph: GridWithWeights):
|
graph: GridWithWeights):
|
||||||
super().__init__(model)
|
super().__init__(model)
|
||||||
self.action_queue: List[Action] = []
|
self.action_queue: List[Action] = []
|
||||||
@ -28,11 +30,20 @@ class ForkliftAgent(AgentBase):
|
|||||||
self.drop_off: PatchAgent = drop_off
|
self.drop_off: PatchAgent = drop_off
|
||||||
self.graph = graph
|
self.graph = graph
|
||||||
self.game_constants = game_constants
|
self.game_constants = game_constants
|
||||||
self.current_order: Order = None
|
|
||||||
self.current_item = None
|
|
||||||
self.item_station_completed = False
|
self.item_station_completed = False
|
||||||
self.current_order_delivered_items: List[Item] = []
|
self.current_order_delivered_items: List[Item] = []
|
||||||
self.ready_for_execution = False
|
self.ready_for_execution = False
|
||||||
|
self.last_delviered_item = None
|
||||||
|
|
||||||
|
self.current_item: Item = None
|
||||||
|
self.current_order = None
|
||||||
|
self.base: GridLocation = None
|
||||||
|
self.goal: GridLocation = None
|
||||||
|
|
||||||
|
def set_base(self, drop_off: PatchAgent):
|
||||||
|
self.drop_off = drop_off
|
||||||
|
self.base = self.drop_off.location
|
||||||
|
self.goal = self.base
|
||||||
|
|
||||||
def queue_movement_actions(self, movement_actions: List[Action]):
|
def queue_movement_actions(self, movement_actions: List[Action]):
|
||||||
self.action_queue.extend(movement_actions)
|
self.action_queue.extend(movement_actions)
|
||||||
@ -43,120 +54,94 @@ class ForkliftAgent(AgentBase):
|
|||||||
action_type = action.action_type
|
action_type = action.action_type
|
||||||
|
|
||||||
if action_type == ActionType.ROTATE_UP:
|
if action_type == ActionType.ROTATE_UP:
|
||||||
print("rotate {} --> {}".format(self.current_rotation, action_type))
|
# print("rotate {} --> {}".format(self.current_rotation, action_type))
|
||||||
self.current_rotation = Direction.top
|
self.current_rotation = Direction.top
|
||||||
|
|
||||||
elif action_type == ActionType.ROTATE_RIGHT:
|
elif action_type == ActionType.ROTATE_RIGHT:
|
||||||
print("rotate {} --> {}".format(self.current_rotation, action_type))
|
# print("rotate {} --> {}".format(self.current_rotation, action_type))
|
||||||
self.current_rotation = Direction.right
|
self.current_rotation = Direction.right
|
||||||
|
|
||||||
elif action_type == ActionType.ROTATE_DOWN:
|
elif action_type == ActionType.ROTATE_DOWN:
|
||||||
print("rotate {} --> {}".format(self.current_rotation, action_type))
|
# print("rotate {} --> {}".format(self.current_rotation, action_type))
|
||||||
self.current_rotation = Direction.down
|
self.current_rotation = Direction.down
|
||||||
|
|
||||||
elif action_type == ActionType.ROTATE_LEFT:
|
elif action_type == ActionType.ROTATE_LEFT:
|
||||||
print("rotate {} --> {}".format(self.current_rotation, action_type))
|
# print("rotate {} --> {}".format(self.current_rotation, action_type))
|
||||||
self.current_rotation = Direction.left
|
self.current_rotation = Direction.left
|
||||||
|
|
||||||
elif action_type == ActionType.MOVE:
|
elif action_type == ActionType.MOVE:
|
||||||
if self.current_rotation == Direction.top:
|
if self.current_rotation == Direction.top:
|
||||||
print("move {} --> {}".format(self.current_position, action_type))
|
# print("move {} --> {}".format(self.current_position, action_type))
|
||||||
self.current_position = (self.current_position[0], self.current_position[1] + 1)
|
self.current_position = (self.current_position[0], self.current_position[1] + 1)
|
||||||
|
|
||||||
elif self.current_rotation == Direction.down:
|
elif self.current_rotation == Direction.down:
|
||||||
print("move {} --> {}".format(self.current_position, action_type))
|
# print("move {} --> {}".format(self.current_position, action_type))
|
||||||
self.current_position = (self.current_position[0], self.current_position[1] - 1)
|
self.current_position = (self.current_position[0], self.current_position[1] - 1)
|
||||||
|
|
||||||
elif self.current_rotation == Direction.right:
|
elif self.current_rotation == Direction.right:
|
||||||
print("move {} --> {}".format(self.current_position, action_type))
|
# print("move {} --> {}".format(self.current_position, action_type))
|
||||||
self.current_position = (self.current_position[0] + 1, self.current_position[1])
|
self.current_position = (self.current_position[0] + 1, self.current_position[1])
|
||||||
|
|
||||||
elif self.current_rotation == Direction.left:
|
elif self.current_rotation == Direction.left:
|
||||||
print("move {} --> {}".format(self.current_position, action_type))
|
# print("move {} --> {}".format(self.current_position, action_type))
|
||||||
self.current_position = (self.current_position[0] - 1, self.current_position[1])
|
self.current_position = (self.current_position[0] - 1, self.current_position[1])
|
||||||
|
|
||||||
def plan_actions(self):
|
def step(self) -> None:
|
||||||
if len(self.current_order.items) > 0:
|
if len(self.action_queue) > 0:
|
||||||
i = self.current_order.items.pop(0)
|
self.move()
|
||||||
if self.current_item is None:
|
elif self.ready_for_execution:
|
||||||
self.current_order_delivered_items.clear()
|
if self.current_position != self.goal:
|
||||||
self.current_item = i
|
|
||||||
print("PLAN MOVEMENT")
|
|
||||||
|
|
||||||
# get item
|
|
||||||
pathFinder = PathFinderOnStates(
|
pathFinder = PathFinderOnStates(
|
||||||
self.game_constants,
|
self.game_constants,
|
||||||
self.drop_off.location,
|
self.goal,
|
||||||
PathFinderState(self.current_position,
|
|
||||||
self.current_rotation,
|
|
||||||
0,
|
|
||||||
Action(ActionType.NONE),
|
|
||||||
[])
|
|
||||||
)
|
|
||||||
actions = pathFinder.get_action_list()
|
|
||||||
self.queue_movement_actions(actions)
|
|
||||||
|
|
||||||
elif not self.item_station_completed:
|
|
||||||
# go through station
|
|
||||||
|
|
||||||
packing_station: GridLocation = None
|
|
||||||
stations = dict(self.graph.packingStations)
|
|
||||||
if i.real_type == ItemType.SHELF:
|
|
||||||
packing_station = stations[PatchType.packingShelf]
|
|
||||||
elif i.real_type == ItemType.REFRIGERATOR:
|
|
||||||
packing_station = stations[PatchType.packingRefrigerator]
|
|
||||||
elif i.real_type == ItemType.DOOR:
|
|
||||||
packing_station = stations[PatchType.packingC]
|
|
||||||
|
|
||||||
pathFinder = PathFinderOnStates(
|
|
||||||
self.game_constants,
|
|
||||||
packing_station,
|
|
||||||
PathFinderState(self.current_position,
|
PathFinderState(self.current_position,
|
||||||
self.current_rotation,
|
self.current_rotation,
|
||||||
0,
|
0,
|
||||||
Action(
|
Action(
|
||||||
desired_item=i,
|
desired_item=None,
|
||||||
action_type=ActionType.PICK_ITEM
|
action_type=ActionType.PICK_ITEM
|
||||||
),
|
),
|
||||||
[])
|
[])
|
||||||
)
|
)
|
||||||
actions = pathFinder.get_action_list()
|
actions = pathFinder.get_action_list()
|
||||||
self.queue_movement_actions(actions)
|
self.queue_movement_actions(actions)
|
||||||
self.item_station_completed = True
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# go to client delivery area
|
if self.current_order is not None and self.goal == self.base:
|
||||||
pathFinder = PathFinderOnStates(
|
self.current_item = self.current_order.items.pop(0)
|
||||||
self.game_constants,
|
packing_station: GridLocation = None
|
||||||
self.client_delivery.location,
|
stations = dict(self.graph.packingStations)
|
||||||
PathFinderState(self.current_position,
|
|
||||||
self.current_rotation,
|
if self.current_item.real_type == ItemType.SHELF:
|
||||||
0,
|
packing_station = stations[PatchType.packingShelf]
|
||||||
Action(ActionType.NONE),
|
elif self.current_item.real_type == ItemType.REFRIGERATOR:
|
||||||
[])
|
packing_station = stations[PatchType.packingRefrigerator]
|
||||||
)
|
elif self.current_item.real_type == ItemType.DOOR:
|
||||||
actions = pathFinder.get_action_list()
|
packing_station = stations[PatchType.packingDoor]
|
||||||
self.queue_movement_actions(actions)
|
|
||||||
self.queue_movement_actions(
|
self.goal = packing_station
|
||||||
[Action(ActionType.DROP_ITEM)]
|
|
||||||
)
|
elif self.goal in [i[1] for i in self.graph.packingStations]:
|
||||||
|
self.goal = self.client_delivery.location
|
||||||
|
|
||||||
|
elif self.goal == self.client_delivery.location:
|
||||||
|
if self.current_order is not None and len(self.current_order.items) == 0:
|
||||||
|
|
||||||
self.current_order_delivered_items.append(self.current_item)
|
self.current_order_delivered_items.append(self.current_item)
|
||||||
self.current_item = None
|
self.current_order.items = deepcopy(self.current_order_delivered_items)
|
||||||
self.item_station_completed = False
|
|
||||||
|
|
||||||
def step(self) -> None:
|
|
||||||
if len(self.action_queue) > 0:
|
|
||||||
self.move()
|
|
||||||
elif self.ready_for_execution and len(self.orderList) > 0:
|
|
||||||
if (self.current_order is not None and len(self.current_order.items)) == 0:
|
|
||||||
self.fulfilled_orders.append(self.current_order)
|
self.fulfilled_orders.append(self.current_order)
|
||||||
|
|
||||||
|
self.current_item = None
|
||||||
self.current_order = None
|
self.current_order = None
|
||||||
|
|
||||||
if self.current_order is None:
|
self.goal = self.base
|
||||||
self.current_order = self.orderList.pop(0)
|
else:
|
||||||
|
self.current_order_delivered_items.append(self.current_item)
|
||||||
|
self.goal = self.base
|
||||||
|
self.current_item = None
|
||||||
|
|
||||||
self.plan_actions()
|
elif self.goal == self.base and self.current_order is None:
|
||||||
|
self.current_order_delivered_items.clear()
|
||||||
|
self.current_order = self.orderList.pop(0)
|
||||||
|
|
||||||
def creation_log(self):
|
def creation_log(self):
|
||||||
print("Created Forklift Agent [id: {}]".format(self.unique_id))
|
print("Created Forklift Agent [id: {}]".format(self.unique_id))
|
||||||
|
17
GameModel.py
17
GameModel.py
@ -47,6 +47,7 @@ class GameModel(Model):
|
|||||||
self.grid = MultiGrid(height, width, True)
|
self.grid = MultiGrid(height, width, True)
|
||||||
self.schedule = RandomActivation(self)
|
self.schedule = RandomActivation(self)
|
||||||
self.current_item_recognition = None
|
self.current_item_recognition = None
|
||||||
|
self.current_item = None
|
||||||
|
|
||||||
self.client_delivery: PatchAgent = None
|
self.client_delivery: PatchAgent = None
|
||||||
self.drop_off: PatchAgent = None
|
self.drop_off: PatchAgent = None
|
||||||
@ -79,8 +80,8 @@ class GameModel(Model):
|
|||||||
self.initialize_grid(graph, item_display_pos)
|
self.initialize_grid(graph, item_display_pos)
|
||||||
self.orderList: List[Order] = InitialStateFactory.generate_order_list(orders)
|
self.orderList: List[Order] = InitialStateFactory.generate_order_list(orders)
|
||||||
self.fulfilled_orders: List[Order] = []
|
self.fulfilled_orders: List[Order] = []
|
||||||
self.forklift_agent.orderList = self.orderList
|
|
||||||
self.forklift_agent.fulfilled_orders = self.fulfilled_orders
|
self.forklift_agent.fulfilled_orders = self.fulfilled_orders
|
||||||
|
self.forklift_agent.set_base(self.drop_off)
|
||||||
self.classificator = classificator
|
self.classificator = classificator
|
||||||
|
|
||||||
print("############## RECOGNISE ITEMS ##############")
|
print("############## RECOGNISE ITEMS ##############")
|
||||||
@ -104,6 +105,7 @@ class GameModel(Model):
|
|||||||
print("PATHFINDING")
|
print("PATHFINDING")
|
||||||
print(actions)
|
print(actions)
|
||||||
self.forklift_agent.queue_movement_actions(actions)
|
self.forklift_agent.queue_movement_actions(actions)
|
||||||
|
self.current_order = self.forklift_agent.current_order
|
||||||
|
|
||||||
def initialize_grid(self, graph: GridWithWeights, item_display_pos):
|
def initialize_grid(self, graph: GridWithWeights, item_display_pos):
|
||||||
print("INITIALIZING GRID")
|
print("INITIALIZING GRID")
|
||||||
@ -174,9 +176,11 @@ class GameModel(Model):
|
|||||||
self.grid.place_agent(agent, p)
|
self.grid.place_agent(agent, p)
|
||||||
|
|
||||||
def update_item_display(self):
|
def update_item_display(self):
|
||||||
for index, item in enumerate(self.forklift_agent.current_order_delivered_items):
|
self.current_item = self.forklift_agent.current_item
|
||||||
if item is not None:
|
for i in range(4):
|
||||||
self.item_display_agents[index].image = item.image
|
self.item_display_agents[i].image = None
|
||||||
|
if len(self.forklift_agent.current_order_delivered_items) > i:
|
||||||
|
self.item_display_agents[i].image = self.forklift_agent.current_order_delivered_items[i].image
|
||||||
|
|
||||||
def step(self):
|
def step(self):
|
||||||
self.schedule.step()
|
self.schedule.step()
|
||||||
@ -219,12 +223,15 @@ class GameModel(Model):
|
|||||||
# print("ORDER {}, PRIO: {}".format(new_orders[i].id, new_orders[i].priority))
|
# print("ORDER {}, PRIO: {}".format(new_orders[i].id, new_orders[i].priority))
|
||||||
|
|
||||||
self.orderList = new_orders
|
self.orderList = new_orders
|
||||||
|
self.forklift_agent.orderList = self.orderList
|
||||||
|
|
||||||
print("FINISHED CLIENT ORDER SORTING")
|
print("FINISHED CLIENT ORDER SORTING")
|
||||||
self.phase = Phase.EXECUTION
|
self.phase = Phase.EXECUTION
|
||||||
|
|
||||||
if self.phase == Phase.EXECUTION:
|
if self.phase == Phase.EXECUTION:
|
||||||
print("Execution")
|
self.current_order = self.forklift_agent.current_order
|
||||||
|
pass
|
||||||
|
# print("Execution")
|
||||||
|
|
||||||
def recognise_item(self, item: Item):
|
def recognise_item(self, item: Item):
|
||||||
val = image_classification(self.picture_visualization.img, self.classificator)
|
val = image_classification(self.picture_visualization.img, self.classificator)
|
||||||
|
19
main.py
19
main.py
@ -1,4 +1,3 @@
|
|||||||
import math
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from mesa.visualization.ModularVisualization import ModularServer
|
from mesa.visualization.ModularVisualization import ModularServer
|
||||||
@ -7,20 +6,15 @@ from tensorflow import keras
|
|||||||
|
|
||||||
from ForkliftAgent import ForkliftAgent
|
from ForkliftAgent import ForkliftAgent
|
||||||
from GameModel import GameModel
|
from GameModel import GameModel
|
||||||
from InitialStateFactory import InitialStateFactory
|
|
||||||
from ItemDisplayAgent import ItemDisplayAgent
|
from ItemDisplayAgent import ItemDisplayAgent
|
||||||
from PatchAgent import PatchAgent
|
from PatchAgent import PatchAgent
|
||||||
from PatchType import PatchType
|
from PatchType import PatchType
|
||||||
from PictureVisualizationAgent import PictureVisualizationAgent
|
from PictureVisualizationAgent import PictureVisualizationAgent
|
||||||
from data.Order import Order
|
|
||||||
from data.enum.Direction import Direction
|
from data.enum.Direction import Direction
|
||||||
from tensorflow import keras
|
|
||||||
|
|
||||||
from data.enum.Priority import Priority
|
|
||||||
from genetic_order.GeneticOrder import GeneticOrder
|
|
||||||
from util.PathDefinitions import GridWithWeights
|
from util.PathDefinitions import GridWithWeights
|
||||||
from visualization.DisplayAttributeElement import DisplayAttributeElement
|
from visualization.DisplayAttributeElement import DisplayAttributeElement
|
||||||
from visualization.DisplayItemListAttribute import DisplayItemListAttributeElement
|
from visualization.DisplayItemListAttribute import DisplayItemListAttributeElement
|
||||||
|
from visualization.DisplayOrder import DisplayOrder
|
||||||
from visualization.DisplayOrderList import DisplayOrderList
|
from visualization.DisplayOrderList import DisplayOrderList
|
||||||
|
|
||||||
colors = [
|
colors = [
|
||||||
@ -104,26 +98,27 @@ if __name__ == '__main__':
|
|||||||
diagram = GridWithWeights(gridWidth, gridHeight)
|
diagram = GridWithWeights(gridWidth, gridHeight)
|
||||||
diagram.walls = [(6, 5), (6, 6), (6, 7), (6, 8), (2, 3), (2, 4), (3, 4), (4, 4), (6, 4)]
|
diagram.walls = [(6, 5), (6, 6), (6, 7), (6, 8), (2, 3), (2, 4), (3, 4), (4, 4), (6, 4)]
|
||||||
diagram.puddles = [(2, 2), (2, 5), (2, 6), (5, 4)]
|
diagram.puddles = [(2, 2), (2, 5), (2, 6), (5, 4)]
|
||||||
diagram.packingStations = [(PatchType.packingShelf, (4, 8)), (PatchType.packingRefrigerator, (4, 6)), (PatchType.packingDoor, (4, 2))]
|
diagram.packingStations = [(PatchType.packingShelf, (4, 8)), (PatchType.packingRefrigerator, (4, 6)),
|
||||||
|
(PatchType.packingDoor, (4, 2))]
|
||||||
|
|
||||||
grid = CanvasGrid(agent_portrayal, gridWidth, gridHeight, scale * gridWidth, scale * gridHeight)
|
grid = CanvasGrid(agent_portrayal, gridWidth, gridHeight, scale * gridWidth, scale * gridHeight)
|
||||||
|
|
||||||
display_items = [(6, 11), (7, 11), (8, 11), (9, 11)]
|
display_items = [(6, 11), (7, 11), (8, 11), (9, 11)]
|
||||||
|
|
||||||
readyText = DisplayAttributeElement("phase")
|
readyText = DisplayAttributeElement("phase")
|
||||||
# current_item = DisplayPictureElement("current_item_recognition")
|
|
||||||
provided_itesm = DisplayItemListAttributeElement("provided_items")
|
provided_itesm = DisplayItemListAttributeElement("provided_items")
|
||||||
recognised_items = DisplayItemListAttributeElement("recognised_items")
|
recognised_items = DisplayItemListAttributeElement("recognised_items")
|
||||||
|
current_order = DisplayOrder("current_order")
|
||||||
|
current_item = DisplayAttributeElement("current_item")
|
||||||
ordersText = DisplayOrderList("orderList")
|
ordersText = DisplayOrderList("orderList")
|
||||||
fulfilled_orders = DisplayOrderList("fulfilled_orders")
|
fulfilled_orders = DisplayOrderList("fulfilled_orders")
|
||||||
|
|
||||||
model = keras.models.load_model("imageClasification/my_model")
|
model = keras.models.load_model("imageClasification/my_model")
|
||||||
|
|
||||||
server = ModularServer(GameModel,
|
server = ModularServer(GameModel,
|
||||||
[grid, readyText, provided_itesm, recognised_items, ordersText,
|
[grid, readyText, current_item, current_order, fulfilled_orders, ordersText],
|
||||||
fulfilled_orders],
|
|
||||||
"Automatyczny Wózek Widłowy",
|
"Automatyczny Wózek Widłowy",
|
||||||
dict(width=gridHeight, height=gridWidth, graph=diagram, items=50, orders=3,
|
dict(width=gridHeight, height=gridWidth, graph=diagram, items=60, orders=20,
|
||||||
classificator=model, item_display_pos=display_items))
|
classificator=model, item_display_pos=display_items))
|
||||||
|
|
||||||
server.port = 8888
|
server.port = 8888
|
||||||
|
46
visualization/DisplayOrder.py
Normal file
46
visualization/DisplayOrder.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
from collections import Counter
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from mesa.visualization.modules import TextElement
|
||||||
|
|
||||||
|
from data.Order import Order
|
||||||
|
|
||||||
|
|
||||||
|
class DisplayOrder(TextElement):
|
||||||
|
def __init__(self, attr_name):
|
||||||
|
'''
|
||||||
|
Create a new text attribute element.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
attr_name: The name of the attribute to extract from the model.
|
||||||
|
|
||||||
|
Example return: "happy: 10"
|
||||||
|
'''
|
||||||
|
self.attr_name = attr_name
|
||||||
|
|
||||||
|
def render(self, model):
|
||||||
|
val = getattr(model, self.attr_name)
|
||||||
|
res = self.attr_name
|
||||||
|
if val is not None:
|
||||||
|
o = val
|
||||||
|
itemList = map(lambda x: x.real_type, o.items)
|
||||||
|
itemCounter = Counter(itemList)
|
||||||
|
|
||||||
|
item_str = "<ul>"
|
||||||
|
for e in itemCounter:
|
||||||
|
key = e
|
||||||
|
val = itemCounter[key]
|
||||||
|
|
||||||
|
# key_str = ""
|
||||||
|
# if key == ItemType.DOOR:
|
||||||
|
# key_str = "Door"
|
||||||
|
# elif key == ItemType.SHELF:
|
||||||
|
# key_str = "Shelf"
|
||||||
|
|
||||||
|
item_str += f"<li>{str(key)}:{str(val)}</li>"
|
||||||
|
|
||||||
|
item_str += "</ul>"
|
||||||
|
|
||||||
|
res += f"<li> items: {item_str} priority: {o.priority} <br> Client: {vars(o.client_params)} </li>"
|
||||||
|
|
||||||
|
return res
|
@ -4,7 +4,6 @@ from typing import List
|
|||||||
from mesa.visualization.modules import TextElement
|
from mesa.visualization.modules import TextElement
|
||||||
|
|
||||||
from data.Order import Order
|
from data.Order import Order
|
||||||
from data.enum.ItemType import ItemType
|
|
||||||
|
|
||||||
|
|
||||||
class DisplayOrderList(TextElement):
|
class DisplayOrderList(TextElement):
|
||||||
@ -21,7 +20,8 @@ class DisplayOrderList(TextElement):
|
|||||||
|
|
||||||
def render(self, model):
|
def render(self, model):
|
||||||
val = getattr(model, self.attr_name)
|
val = getattr(model, self.attr_name)
|
||||||
|
res = ""
|
||||||
|
if val is not None:
|
||||||
orderList: List[Order] = val
|
orderList: List[Order] = val
|
||||||
|
|
||||||
res = self.attr_name + ": <ol>"
|
res = self.attr_name + ": <ol>"
|
||||||
|
Loading…
Reference in New Issue
Block a user