Programm versteht auch eine Polylinie als Weg an dem die Nummerierung entlang gehen kann.
This commit is contained in:
+95
-4
@@ -68,6 +68,97 @@
|
|||||||
"use_polyline": false
|
"use_polyline": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"Nummerierung2": {
|
||||||
|
"description": "Test-Szene mit komplexem Layout - 8 MA-1@@ Symbole in verschiedenen Anordnungen",
|
||||||
|
"ma_groups": [
|
||||||
|
{
|
||||||
|
"name": "MA-1@@_top_left",
|
||||||
|
"count": 1,
|
||||||
|
"layout_type": "single",
|
||||||
|
"base_x": 200,
|
||||||
|
"base_y": 1400,
|
||||||
|
"spacing": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MA-1@@_top_right",
|
||||||
|
"count": 1,
|
||||||
|
"layout_type": "single",
|
||||||
|
"base_x": 1200,
|
||||||
|
"base_y": 1400,
|
||||||
|
"spacing": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MA-1@@_middle_left",
|
||||||
|
"count": 1,
|
||||||
|
"layout_type": "single",
|
||||||
|
"base_x": 300,
|
||||||
|
"base_y": 900,
|
||||||
|
"spacing": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MA-1@@_middle_right",
|
||||||
|
"count": 1,
|
||||||
|
"layout_type": "single",
|
||||||
|
"base_x": 850,
|
||||||
|
"base_y": 900,
|
||||||
|
"spacing": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MA-1@@_bottom_center",
|
||||||
|
"count": 1,
|
||||||
|
"layout_type": "single",
|
||||||
|
"base_x": 620,
|
||||||
|
"base_y": 400,
|
||||||
|
"spacing": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MA-1@@_right_top",
|
||||||
|
"count": 1,
|
||||||
|
"layout_type": "single",
|
||||||
|
"base_x": 1600,
|
||||||
|
"base_y": 1200,
|
||||||
|
"spacing": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MA-1@@_right_middle",
|
||||||
|
"count": 1,
|
||||||
|
"layout_type": "single",
|
||||||
|
"base_x": 1600,
|
||||||
|
"base_y": 700,
|
||||||
|
"spacing": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "MA-1@@_right_bottom",
|
||||||
|
"count": 1,
|
||||||
|
"layout_type": "single",
|
||||||
|
"base_x": 1520,
|
||||||
|
"base_y": 400,
|
||||||
|
"spacing": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"renaming_frames": [
|
||||||
|
{
|
||||||
|
"comment": "Polyline-Pfad: Verbindet alle MA-1@@ in kreisförmiger Bewegung",
|
||||||
|
"type": "polyline_path",
|
||||||
|
"name": "MA-1@@",
|
||||||
|
"kennzeichnung": "A01+UH00",
|
||||||
|
"layer_name": "ILS_MOTOR",
|
||||||
|
"direction": "POLYLINE_PATH",
|
||||||
|
"path_points": [
|
||||||
|
{"x": 200, "y": 1400},
|
||||||
|
{"x": 1200, "y": 1400},
|
||||||
|
{"x": 1600, "y": 1200},
|
||||||
|
{"x": 1600, "y": 700},
|
||||||
|
{"x": 1520, "y": 400},
|
||||||
|
{"x": 620, "y": 400},
|
||||||
|
{"x": 300, "y": 900},
|
||||||
|
{"x": 850, "y": 900},
|
||||||
|
{"x": 200, "y": 1400}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -107,22 +198,22 @@
|
|||||||
"_description": "ATTDEF Positionen im inneren Block RENAMER_ATTRIB_MA",
|
"_description": "ATTDEF Positionen im inneren Block RENAMER_ATTRIB_MA",
|
||||||
"NAME": {
|
"NAME": {
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": 50,
|
"y": -10,
|
||||||
"height": 21.25
|
"height": 21.25
|
||||||
},
|
},
|
||||||
"KENNZEICHNUNG": {
|
"KENNZEICHNUNG": {
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": 20,
|
"y": -35,
|
||||||
"height": 21.25
|
"height": 21.25
|
||||||
},
|
},
|
||||||
"LAYER_NAME": {
|
"LAYER_NAME": {
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": 0,
|
"y": -60,
|
||||||
"height": 21.25
|
"height": 21.25
|
||||||
},
|
},
|
||||||
"DIRECTION": {
|
"DIRECTION": {
|
||||||
"x": 10,
|
"x": 10,
|
||||||
"y": -80,
|
"y": -85,
|
||||||
"height": 21.25
|
"height": 21.25
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+157
-32
@@ -24,9 +24,10 @@ PROJECT_CFG = os.getenv('PROJECT_CFG', os.path.join(PROJECT, 'cfg'))
|
|||||||
class TestDataGenerator:
|
class TestDataGenerator:
|
||||||
"""Generiert Test-DXF-Dateien für verschiedene Szenarien"""
|
"""Generiert Test-DXF-Dateien für verschiedene Szenarien"""
|
||||||
|
|
||||||
def __init__(self, filename, test_type, config_file=None):
|
def __init__(self, filename, test_type, config_file=None, scene_name=None):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.test_type = test_type
|
self.test_type = test_type
|
||||||
|
self.scene_name = scene_name
|
||||||
self.doc = None
|
self.doc = None
|
||||||
self.msp = None
|
self.msp = None
|
||||||
self.config = None
|
self.config = None
|
||||||
@@ -330,13 +331,14 @@ class TestDataGenerator:
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def create_renamer_attrib_block(self, block_name, attrib_tags):
|
def create_renamer_attrib_block(self, block_name, attrib_tags, frame_type='MA'):
|
||||||
"""
|
"""
|
||||||
Erstellt einen Block mit ATTDEF-Attributen für Renamer-Frames
|
Erstellt einen Block mit ATTDEF-Attributen für Renamer-Frames
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
block_name: Name des Attribut-Blocks (z.B. 'RENAMER_ATTRIB_MA')
|
block_name: Name des Attribut-Blocks (z.B. 'RENAMER_ATTRIB_MA')
|
||||||
attrib_tags: Liste der Attribut-Tags (z.B. ['NAME', 'KENNZEICHNUNG', ...])
|
attrib_tags: Liste der Attribut-Tags (z.B. ['NAME', 'KENNZEICHNUNG', ...])
|
||||||
|
frame_type: 'MA' oder 'MULTI' für Frame-Typ
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Block-Definition
|
Block-Definition
|
||||||
@@ -346,9 +348,20 @@ class TestDataGenerator:
|
|||||||
|
|
||||||
attrib_block = self.doc.blocks.new(block_name)
|
attrib_block = self.doc.blocks.new(block_name)
|
||||||
|
|
||||||
# Definiere Standard-Positionen und Werte für Attribute
|
# Hole Positionen aus Config, falls vorhanden
|
||||||
# Font-Größe auf ein Viertel reduziert (85.0 → 21.25)
|
if self.config and 'general' in self.config:
|
||||||
attrib_definitions = {
|
general = self.config['general']
|
||||||
|
if frame_type == 'MA' and 'ma_frame' in general:
|
||||||
|
config_positions = general['ma_frame'].get('attdef_positions', {})
|
||||||
|
elif frame_type == 'MULTI' and 'multi_frame' in general:
|
||||||
|
config_positions = general['multi_frame'].get('attdef_positions', {})
|
||||||
|
else:
|
||||||
|
config_positions = {}
|
||||||
|
else:
|
||||||
|
config_positions = {}
|
||||||
|
|
||||||
|
# Definiere Standard-Positionen und Werte für Attribute (Fallback)
|
||||||
|
default_definitions = {
|
||||||
'NAME': {'insert': (10, 50), 'default': 'MA@@', 'height': 21.25},
|
'NAME': {'insert': (10, 50), 'default': 'MA@@', 'height': 21.25},
|
||||||
'NAME1': {'insert': (10, 80), 'default': 'BG1@@@', 'height': 21.25},
|
'NAME1': {'insert': (10, 80), 'default': 'BG1@@@', 'height': 21.25},
|
||||||
'NAME2': {'insert': (10, 60), 'default': 'MB1@@@', 'height': 21.25},
|
'NAME2': {'insert': (10, 60), 'default': 'MB1@@@', 'height': 21.25},
|
||||||
@@ -364,14 +377,25 @@ class TestDataGenerator:
|
|||||||
|
|
||||||
# Füge nur die angeforderten Attribute hinzu
|
# Füge nur die angeforderten Attribute hinzu
|
||||||
for tag in attrib_tags:
|
for tag in attrib_tags:
|
||||||
if tag in attrib_definitions:
|
if tag in default_definitions:
|
||||||
attdef = attrib_definitions[tag]
|
default_def = default_definitions[tag]
|
||||||
|
|
||||||
|
# Verwende Config-Werte, falls vorhanden, sonst Defaults
|
||||||
|
if tag in config_positions and not tag.startswith('_'):
|
||||||
|
config_pos = config_positions[tag]
|
||||||
|
x = config_pos.get('x', default_def['insert'][0])
|
||||||
|
y = config_pos.get('y', default_def['insert'][1])
|
||||||
|
height = config_pos.get('height', default_def['height'])
|
||||||
|
else:
|
||||||
|
x, y = default_def['insert']
|
||||||
|
height = default_def['height']
|
||||||
|
|
||||||
attrib_block.add_attdef(
|
attrib_block.add_attdef(
|
||||||
tag=tag,
|
tag=tag,
|
||||||
insert=attdef['insert'],
|
insert=(x, y),
|
||||||
text=attdef['default'],
|
text=default_def['default'],
|
||||||
dxfattribs={
|
dxfattribs={
|
||||||
'height': attdef['height'],
|
'height': height,
|
||||||
'layer': 'ILS_RENAMER',
|
'layer': 'ILS_RENAMER',
|
||||||
'color': 1,
|
'color': 1,
|
||||||
'style': 'textstyle3'
|
'style': 'textstyle3'
|
||||||
@@ -412,8 +436,8 @@ class TestDataGenerator:
|
|||||||
'LAYER_NAME1', 'LAYER_NAME2', 'LAYER_NAME3', 'DIRECTION', 'ID']
|
'LAYER_NAME1', 'LAYER_NAME2', 'LAYER_NAME3', 'DIRECTION', 'ID']
|
||||||
attrib_block_name = 'RENAMER_ATTRIB_MULTI'
|
attrib_block_name = 'RENAMER_ATTRIB_MULTI'
|
||||||
|
|
||||||
# Erstelle Attribut-Block (wiederverwendbar)
|
# Erstelle Attribut-Block (wiederverwendbar) mit Config-Unterstützung
|
||||||
self.create_renamer_attrib_block(attrib_block_name, attrib_tags)
|
self.create_renamer_attrib_block(attrib_block_name, attrib_tags, frame_type=frame_type)
|
||||||
|
|
||||||
# Erstelle äußeren Renamer-Block mit eindeutigem Namen (inkl. Position und Größe)
|
# Erstelle äußeren Renamer-Block mit eindeutigem Namen (inkl. Position und Größe)
|
||||||
# Jeder Rahmen bekommt einen eindeutigen Namen basierend auf Position
|
# Jeder Rahmen bekommt einen eindeutigen Namen basierend auf Position
|
||||||
@@ -462,13 +486,79 @@ class TestDataGenerator:
|
|||||||
dxfattribs={'layer': 'ILS_RENAMER'}
|
dxfattribs={'layer': 'ILS_RENAMER'}
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_nummerierung(self):
|
def create_polyline_renamer_frame(self, path_points, name, kennzeichnung, layer_name, direction):
|
||||||
|
"""
|
||||||
|
Erstellt einen Renamer-Rahmen mit Polyline-Pfad
|
||||||
|
|
||||||
|
Struktur:
|
||||||
|
- Block (RENAMER_POLYLINE_xxx):
|
||||||
|
1. LWPOLYLINE (Pfad durch die Punkte)
|
||||||
|
2. Block-Referenz mit Attributen (RENAMER_ATTRIB_MA)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path_points: Liste von Dictionaries mit 'x' und 'y' Koordinaten
|
||||||
|
name: NAME-Attribut
|
||||||
|
kennzeichnung: KENNZEICHNUNG-Attribut
|
||||||
|
layer_name: LAYER_NAME-Attribut
|
||||||
|
direction: DIRECTION-Attribut (z.B. 'POLYLINE_PATH')
|
||||||
|
"""
|
||||||
|
# Erstelle Attribut-Block (wiederverwendbar)
|
||||||
|
attrib_tags = ['NAME', 'KENNZEICHNUNG', 'LAYER_NAME', 'DIRECTION']
|
||||||
|
attrib_block_name = 'RENAMER_ATTRIB_MA'
|
||||||
|
self.create_renamer_attrib_block(attrib_block_name, attrib_tags, frame_type='MA')
|
||||||
|
|
||||||
|
# Erstelle eindeutigen Block-Namen basierend auf erstem Punkt
|
||||||
|
first_point = path_points[0] if path_points else {'x': 0, 'y': 0}
|
||||||
|
block_name = f"RENAMER_POLYLINE_{name.replace('@', '').replace('-', '_')}_{int(first_point['x'])}_{int(first_point['y'])}"
|
||||||
|
|
||||||
|
if block_name not in self.doc.blocks:
|
||||||
|
renamer_block = self.doc.blocks.new(block_name)
|
||||||
|
|
||||||
|
# 1. Füge LWPOLYLINE hinzu (Pfad)
|
||||||
|
# Konvertiere Punkte von Dict zu Tupeln
|
||||||
|
points = [(p['x'], p['y']) for p in path_points]
|
||||||
|
|
||||||
|
renamer_block.add_lwpolyline(points, dxfattribs={
|
||||||
|
'layer': 'ILS_RENAMER',
|
||||||
|
'color': 1
|
||||||
|
})
|
||||||
|
|
||||||
|
# 2. Füge Block-Referenz mit Attributen hinzu
|
||||||
|
# Position neben dem ersten Punkt des Pfades (mit kleinem Offset)
|
||||||
|
first_x = points[0][0]
|
||||||
|
first_y = points[0][1]
|
||||||
|
attrib_insert_pos = (first_x + 10, first_y + 30) # Offset vom ersten Punkt
|
||||||
|
attrib_blockref = renamer_block.add_blockref(
|
||||||
|
attrib_block_name,
|
||||||
|
insert=attrib_insert_pos,
|
||||||
|
dxfattribs={'layer': 'ILS_RENAMER'}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Setze Attribut-Werte
|
||||||
|
attrib_blockref.add_auto_attribs({
|
||||||
|
'NAME': name,
|
||||||
|
'KENNZEICHNUNG': kennzeichnung,
|
||||||
|
'LAYER_NAME': layer_name,
|
||||||
|
'DIRECTION': direction
|
||||||
|
})
|
||||||
|
|
||||||
|
# Füge Block-Referenz im Modelspace ein (am Ursprung, da Pfad absolute Koordinaten hat)
|
||||||
|
blockref = self.msp.add_blockref(
|
||||||
|
block_name,
|
||||||
|
insert=(0, 0),
|
||||||
|
dxfattribs={'layer': 'ILS_RENAMER'}
|
||||||
|
)
|
||||||
|
|
||||||
|
def generate_nummerierung(self, scene_name='Nummerierung1'):
|
||||||
"""
|
"""
|
||||||
Generiert Testdaten für Nummerierung-Szenario
|
Generiert Testdaten für Nummerierung-Szenario
|
||||||
|
|
||||||
Verwendet entweder die JSON-Config oder Hard-coded Defaults
|
Verwendet entweder die JSON-Config oder Hard-coded Defaults
|
||||||
|
|
||||||
|
Args:
|
||||||
|
scene_name: Name der Testszene (z.B. 'Nummerierung1', 'Nummerierung2')
|
||||||
"""
|
"""
|
||||||
print("Generiere Nummerierung-Testdaten...")
|
print(f"Generiere Nummerierung-Testdaten ({scene_name})...")
|
||||||
|
|
||||||
# Erstelle io-Block direkt (ohne Referenzdatei)
|
# Erstelle io-Block direkt (ohne Referenzdatei)
|
||||||
io_block = self.create_io_block()
|
io_block = self.create_io_block()
|
||||||
@@ -478,9 +568,9 @@ class TestDataGenerator:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Wenn Config geladen ist, verwende sie, ansonsten Hard-coded Defaults
|
# Wenn Config geladen ist, verwende sie, ansonsten Hard-coded Defaults
|
||||||
if self.config and 'test_scenes' in self.config and 'Nummerierung1' in self.config['test_scenes']:
|
if self.config and 'test_scenes' in self.config and scene_name in self.config['test_scenes']:
|
||||||
print("Verwende JSON-Config für Nummerierung1...")
|
print(f"Verwende JSON-Config für {scene_name}...")
|
||||||
self._generate_nummerierung_from_config()
|
self._generate_nummerierung_from_config(scene_name)
|
||||||
else:
|
else:
|
||||||
print("Verwende Hard-coded Defaults für Nummerierung...")
|
print("Verwende Hard-coded Defaults für Nummerierung...")
|
||||||
self._generate_nummerierung_hardcoded()
|
self._generate_nummerierung_hardcoded()
|
||||||
@@ -548,9 +638,14 @@ class TestDataGenerator:
|
|||||||
|
|
||||||
blockref.add_auto_attribs(attrib_values)
|
blockref.add_auto_attribs(attrib_values)
|
||||||
|
|
||||||
def _generate_nummerierung_from_config(self):
|
def _generate_nummerierung_from_config(self, scene_name='Nummerierung1'):
|
||||||
"""Generiert Nummerierung-Testdaten aus JSON-Config"""
|
"""
|
||||||
scene = self.config['test_scenes']['Nummerierung1']
|
Generiert Nummerierung-Testdaten aus JSON-Config
|
||||||
|
|
||||||
|
Args:
|
||||||
|
scene_name: Name der Testszene in der Config
|
||||||
|
"""
|
||||||
|
scene = self.config['test_scenes'][scene_name]
|
||||||
ma_defaults = self.config.get('ma_defaults', {})
|
ma_defaults = self.config.get('ma_defaults', {})
|
||||||
general = self.config.get('general', {})
|
general = self.config.get('general', {})
|
||||||
|
|
||||||
@@ -570,16 +665,29 @@ class TestDataGenerator:
|
|||||||
|
|
||||||
# Generiere Renamer-Rahmen aus Config
|
# Generiere Renamer-Rahmen aus Config
|
||||||
for frame_config in scene.get('renaming_frames', []):
|
for frame_config in scene.get('renaming_frames', []):
|
||||||
self.create_renamer_frame(
|
frame_type = frame_config.get('type', 'rectangle')
|
||||||
insert_point=(frame_config['x'], frame_config['y']),
|
|
||||||
width=frame_config['width'],
|
if frame_type == 'polyline_path':
|
||||||
height=frame_config['height'],
|
# Polyline-Pfad Frame
|
||||||
name=frame_config['name'],
|
self.create_polyline_renamer_frame(
|
||||||
kennzeichnung=frame_config['kennzeichnung'],
|
path_points=frame_config['path_points'],
|
||||||
layer_name=frame_config['layer_name'],
|
name=frame_config['name'],
|
||||||
direction=frame_config['direction'],
|
kennzeichnung=frame_config['kennzeichnung'],
|
||||||
use_polyline=frame_config.get('use_polyline', False)
|
layer_name=frame_config['layer_name'],
|
||||||
)
|
direction=frame_config['direction']
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Standard Rectangle Frame
|
||||||
|
self.create_renamer_frame(
|
||||||
|
insert_point=(frame_config['x'], frame_config['y']),
|
||||||
|
width=frame_config['width'],
|
||||||
|
height=frame_config['height'],
|
||||||
|
name=frame_config['name'],
|
||||||
|
kennzeichnung=frame_config['kennzeichnung'],
|
||||||
|
layer_name=frame_config['layer_name'],
|
||||||
|
direction=frame_config['direction'],
|
||||||
|
use_polyline=frame_config.get('use_polyline', False)
|
||||||
|
)
|
||||||
|
|
||||||
def _generate_nummerierung_hardcoded(self):
|
def _generate_nummerierung_hardcoded(self):
|
||||||
"""Generiert Nummerierung-Testdaten mit Hard-coded Werten (Fallback)"""
|
"""Generiert Nummerierung-Testdaten mit Hard-coded Werten (Fallback)"""
|
||||||
@@ -727,7 +835,17 @@ class TestDataGenerator:
|
|||||||
self.create_document()
|
self.create_document()
|
||||||
|
|
||||||
if self.test_type == 'nummerierung':
|
if self.test_type == 'nummerierung':
|
||||||
self.generate_nummerierung()
|
# Bestimme Scene-Name: entweder explizit angegeben oder automatisch aus Filename
|
||||||
|
scene_name = self.scene_name
|
||||||
|
if not scene_name:
|
||||||
|
# Versuche Scene-Name aus Filename zu extrahieren (z.B. "Nummerierung2.dxf" -> "Nummerierung2")
|
||||||
|
import re
|
||||||
|
match = re.match(r'(Nummerierung\d+)', self.filename, re.IGNORECASE)
|
||||||
|
if match:
|
||||||
|
scene_name = match.group(1)
|
||||||
|
else:
|
||||||
|
scene_name = 'Nummerierung1' # Default
|
||||||
|
self.generate_nummerierung(scene_name)
|
||||||
elif self.test_type == 'sps':
|
elif self.test_type == 'sps':
|
||||||
print("SPS-Testdaten noch nicht implementiert")
|
print("SPS-Testdaten noch nicht implementiert")
|
||||||
elif self.test_type == 'todraw':
|
elif self.test_type == 'todraw':
|
||||||
@@ -780,6 +898,13 @@ def main():
|
|||||||
help='JSON-Konfigurationsdatei (Standard: create_tests.json)'
|
help='JSON-Konfigurationsdatei (Standard: create_tests.json)'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
'--scene',
|
||||||
|
type=str,
|
||||||
|
default=None,
|
||||||
|
help='Name der Testszene (z.B. Nummerierung1, Nummerierung2). Falls nicht angegeben, wird aus Filename extrahiert.'
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Stelle sicher, dass Dateiname .dxf Endung hat
|
# Stelle sicher, dass Dateiname .dxf Endung hat
|
||||||
@@ -787,7 +912,7 @@ def main():
|
|||||||
args.filename += '.dxf'
|
args.filename += '.dxf'
|
||||||
|
|
||||||
# Generiere Testdaten
|
# Generiere Testdaten
|
||||||
generator = TestDataGenerator(args.filename, args.type, config_file=args.config)
|
generator = TestDataGenerator(args.filename, args.type, config_file=args.config, scene_name=args.scene)
|
||||||
|
|
||||||
if generator.generate():
|
if generator.generate():
|
||||||
generator.save(args.output_dir)
|
generator.save(args.output_dir)
|
||||||
|
|||||||
Reference in New Issue
Block a user