Merge branch 'dev'
This commit is contained in:
commit
27362deeeb
64
examples/bundles.yml
Normal file
64
examples/bundles.yml
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
templates: # defining templates to be used later on
|
||||||
|
- &molex_f
|
||||||
|
type: Molex KK 254
|
||||||
|
gender: female
|
||||||
|
- &con_i2c
|
||||||
|
pinout: [GND, +5V, SCL, SDA]
|
||||||
|
- &wire_i2c
|
||||||
|
mm2: 0.14
|
||||||
|
length: 0.2
|
||||||
|
colors: [BK, RD, YE, GN]
|
||||||
|
|
||||||
|
nodes:
|
||||||
|
X1:
|
||||||
|
<<: *molex_f # copying items from the template
|
||||||
|
pinout: [GND, +5V, SCL, SDA, MISO, MOSI, SCK, N/C]
|
||||||
|
X2:
|
||||||
|
<<: *molex_f
|
||||||
|
<<: *con_i2c # it is possible to copy from more than one template
|
||||||
|
X3:
|
||||||
|
<<: *molex_f
|
||||||
|
<<: *con_i2c
|
||||||
|
X4:
|
||||||
|
<<: *molex_f
|
||||||
|
pinout: [GND, +12V, MISO, MOSI, SCK]
|
||||||
|
X5:
|
||||||
|
type: Molex Micro-Fit
|
||||||
|
gender: male
|
||||||
|
pinout: [GND, +12V]
|
||||||
|
|
||||||
|
wires:
|
||||||
|
W1:
|
||||||
|
<<: *wire_i2c
|
||||||
|
type: bundle
|
||||||
|
W2:
|
||||||
|
<<: *wire_i2c
|
||||||
|
type: bundle
|
||||||
|
W3:
|
||||||
|
mm2: 0.14
|
||||||
|
length: 0.2
|
||||||
|
type: bundle
|
||||||
|
colors: [BK, BU, OG, VT]
|
||||||
|
W4:
|
||||||
|
mm2: 0.5
|
||||||
|
length: 0.35
|
||||||
|
colors: [BK, RD]
|
||||||
|
type: bundle
|
||||||
|
|
||||||
|
connections:
|
||||||
|
-
|
||||||
|
- X1: [1-4]
|
||||||
|
- W1: [1-4]
|
||||||
|
- X2: [1-4]
|
||||||
|
-
|
||||||
|
- X1: [1-4]
|
||||||
|
- W2: [1-4]
|
||||||
|
- X3: [1-4]
|
||||||
|
-
|
||||||
|
- X1: [1,5-7]
|
||||||
|
- W3: [1-4]
|
||||||
|
- X4: [1,3-5]
|
||||||
|
-
|
||||||
|
- X5: [1,2]
|
||||||
|
- W4: [1,2]
|
||||||
|
- X4: [1,2]
|
||||||
@ -21,7 +21,7 @@ nodes:
|
|||||||
<<: *con_i2c
|
<<: *con_i2c
|
||||||
X4:
|
X4:
|
||||||
<<: *molex_f
|
<<: *molex_f
|
||||||
pinout: [GND, +12V, MISO, MOSI, flachstecker]
|
pinout: [GND, +12V, MISO, MOSI, SCK]
|
||||||
X5:
|
X5:
|
||||||
type: Molex Micro-Fit
|
type: Molex Micro-Fit
|
||||||
gender: male
|
gender: male
|
||||||
@ -30,16 +30,20 @@ nodes:
|
|||||||
wires:
|
wires:
|
||||||
W1:
|
W1:
|
||||||
<<: *wire_i2c
|
<<: *wire_i2c
|
||||||
|
show_name: false
|
||||||
W2:
|
W2:
|
||||||
<<: *wire_i2c
|
<<: *wire_i2c
|
||||||
|
show_name: false
|
||||||
W3:
|
W3:
|
||||||
mm2: 0.14
|
mm2: 0.14
|
||||||
length: 0.2
|
length: 0.2
|
||||||
colors: [BK, BU, OG, VT]
|
colors: [BK, BU, OG, VT]
|
||||||
|
show_name: false
|
||||||
W4:
|
W4:
|
||||||
mm2: 0.5
|
mm2: 0.5
|
||||||
length: 0.35
|
length: 0.35
|
||||||
colors: [BK, RD]
|
colors: [BK, RD]
|
||||||
|
show_name: false
|
||||||
|
|
||||||
connections:
|
connections:
|
||||||
-
|
-
|
||||||
@ -58,28 +62,3 @@ connections:
|
|||||||
- X5: [1,2]
|
- X5: [1,2]
|
||||||
- W4: [1,2]
|
- W4: [1,2]
|
||||||
- X4: [1,2]
|
- X4: [1,2]
|
||||||
|
|
||||||
# -
|
|
||||||
# - X1: 1
|
|
||||||
# - W1: 1
|
|
||||||
# - X2: 1
|
|
||||||
# -
|
|
||||||
# - X1: [2,3,4]
|
|
||||||
# - W1: [2,3,4]
|
|
||||||
# - X2: [4,3,2]
|
|
||||||
# -
|
|
||||||
# - X1: [5-10]
|
|
||||||
# - W1: [5-7,10,9,8]
|
|
||||||
# - X2: [10-5]
|
|
||||||
# -
|
|
||||||
# - X1: 11
|
|
||||||
# - W1: s
|
|
||||||
# -
|
|
||||||
# - X1: [1-5]
|
|
||||||
# - W1: [11-15]
|
|
||||||
# -
|
|
||||||
# - W1: [12-15]
|
|
||||||
# - X2: [2-5]
|
|
||||||
# -
|
|
||||||
# - X1: [12,14]
|
|
||||||
# - X1: [13,15]
|
|
||||||
|
|||||||
@ -11,10 +11,11 @@ nodes:
|
|||||||
wires:
|
wires:
|
||||||
W1:
|
W1:
|
||||||
mm2: 0.25
|
mm2: 0.25
|
||||||
|
show_equiv: true
|
||||||
length: 0.2
|
length: 0.2
|
||||||
color_code: IEC
|
color_code: IEC
|
||||||
num_wires: 10
|
num_wires: 10
|
||||||
shield: true
|
type: bundle
|
||||||
|
|
||||||
ferrules:
|
ferrules:
|
||||||
F_test:
|
F_test:
|
||||||
@ -25,9 +26,6 @@ connections:
|
|||||||
- X1: [1-3]
|
- X1: [1-3]
|
||||||
- W1: [1-3]
|
- W1: [1-3]
|
||||||
- X2: [1-3]
|
- X2: [1-3]
|
||||||
-
|
|
||||||
- X1: 4
|
|
||||||
- W1: s
|
|
||||||
-
|
-
|
||||||
- F_test
|
- F_test
|
||||||
- W1: [4-10]
|
- W1: [4-10]
|
||||||
|
|||||||
116
src/wireviz.py
116
src/wireviz.py
@ -6,7 +6,6 @@ COLOR_CODES = {'DIN': ['WH','BN','GN','YE','GY','PK','BU','RD','BK','VT'], # ,'G
|
|||||||
'IEC': ['BN','RD','OG','YE','GN','BU','VT','GY','WH','BK'],
|
'IEC': ['BN','RD','OG','YE','GN','BU','VT','GY','WH','BK'],
|
||||||
'BW': ['BK','WH']}
|
'BW': ['BK','WH']}
|
||||||
|
|
||||||
# TODO: parse and render double-colored cables ('RDBU' etc)
|
|
||||||
color_hex = {
|
color_hex = {
|
||||||
'BK': '#000000',
|
'BK': '#000000',
|
||||||
'WH': '#ffffff',
|
'WH': '#ffffff',
|
||||||
@ -15,7 +14,7 @@ color_hex = {
|
|||||||
'RD': '#ff0000',
|
'RD': '#ff0000',
|
||||||
'OG': '#ff8000',
|
'OG': '#ff8000',
|
||||||
'YE': '#ffff00',
|
'YE': '#ffff00',
|
||||||
'GN': '#009900',
|
'GN': '#00ff00',
|
||||||
'TQ': '#00ffff',
|
'TQ': '#00ffff',
|
||||||
'BU': '#0066ff',
|
'BU': '#0066ff',
|
||||||
'VT': '#8000ff',
|
'VT': '#8000ff',
|
||||||
@ -79,17 +78,25 @@ class Harness:
|
|||||||
dot.body.append('// Graph generated by WireViz')
|
dot.body.append('// Graph generated by WireViz')
|
||||||
dot.body.append('// https://github.com/formatc1702/WireViz')
|
dot.body.append('// https://github.com/formatc1702/WireViz')
|
||||||
font = 'arial'
|
font = 'arial'
|
||||||
dot.attr('graph', rankdir='LR', ranksep='2', bgcolor='transparent', fontname=font)
|
dot.attr('graph', rankdir='LR',
|
||||||
dot.attr('node', shape='record', style='rounded,filled', fillcolor='white', fontname=font)
|
ranksep='2',
|
||||||
dot.attr('edge', style='bold', fontname=font)
|
bgcolor='transparent',
|
||||||
|
nodesep='0.33',
|
||||||
|
fontname=font)
|
||||||
|
dot.attr('node', shape='record',
|
||||||
|
style='filled',
|
||||||
|
fillcolor='white',
|
||||||
|
fontname=font)
|
||||||
|
dot.attr('edge', style='bold',
|
||||||
|
fontname=font)
|
||||||
|
|
||||||
# prepare ports on connectors depending on which side they will connect
|
# prepare ports on connectors depending on which side they will connect
|
||||||
for k, c in self.cables.items():
|
for k, c in self.cables.items():
|
||||||
for x in c.connections:
|
for x in c.connections:
|
||||||
if x[1] is not None: # connect to left
|
if x.from_port is not None: # connect to left
|
||||||
self.nodes[x[0]].ports_right = True
|
self.nodes[x.from_name].ports_right = True
|
||||||
if x[4] is not None: # connect to right
|
if x.to_port is not None: # connect to right
|
||||||
self.nodes[x[3]].ports_left = True
|
self.nodes[x.to_name].ports_left = True
|
||||||
|
|
||||||
for k, n in self.nodes.items():
|
for k, n in self.nodes.items():
|
||||||
# a = attributes
|
# a = attributes
|
||||||
@ -118,9 +125,9 @@ class Harness:
|
|||||||
loop_dir = 'e'
|
loop_dir = 'e'
|
||||||
else:
|
else:
|
||||||
raise Exception('No side for loops')
|
raise Exception('No side for loops')
|
||||||
for x in n.loops:
|
for loop in n.loops:
|
||||||
dot.edge('{name}:p{port_from}{loop_side}:{loop_dir}'.format(name=n.name, port_from=x[0], port_to=x[1], loop_side=loop_side, loop_dir=loop_dir),
|
dot.edge('{name}:p{port_from}{loop_side}:{loop_dir}'.format(name=n.name, port_from=loop[0], port_to=loop[1], loop_side=loop_side, loop_dir=loop_dir),
|
||||||
'{name}:p{port_to}{loop_side}:{loop_dir}'.format(name=n.name, port_from=x[0], port_to=x[1], loop_side=loop_side, loop_dir=loop_dir))
|
'{name}:p{port_to}{loop_side}:{loop_dir}'.format(name=n.name, port_from=loop[0], port_to=loop[1], loop_side=loop_side, loop_dir=loop_dir))
|
||||||
|
|
||||||
for k, c in self.cables.items():
|
for k, c in self.cables.items():
|
||||||
# a = attributes
|
# a = attributes
|
||||||
@ -147,26 +154,69 @@ class Harness:
|
|||||||
p[1].append('<ws>Shield')
|
p[1].append('<ws>Shield')
|
||||||
# l = label
|
# l = label
|
||||||
l = [c.name if c.show_name else '', a, p]
|
l = [c.name if c.show_name else '', a, p]
|
||||||
dot.node(k, label=nested(l))
|
if c.type == 'bundle':
|
||||||
|
# create subgraph for wire bundle, add to main graph afterwards
|
||||||
|
bun = Graph(name='cluster_{}'.format(k))
|
||||||
|
labeltext = ' | '.join(p for p in a if p) + '\n ' # newline to add space between label and wires
|
||||||
|
bun.attr('graph', label=labeltext,
|
||||||
|
style='filled, dashed',
|
||||||
|
fillcolor='white')
|
||||||
|
bun.attr('node', shape='point',
|
||||||
|
label='',
|
||||||
|
fixedsize='true',
|
||||||
|
width='0', height='0')
|
||||||
|
for i, x in enumerate(c.colors,1):
|
||||||
|
bun.node('{}_w{}l'.format(k,i))
|
||||||
|
bun.node('{}_w{}r'.format(k,i))
|
||||||
|
else:
|
||||||
|
dot.node(k, label=nested(l))
|
||||||
|
|
||||||
|
# add bundle subgraph to main graph
|
||||||
|
if c.type == 'bundle':
|
||||||
|
dot.subgraph(bun)
|
||||||
|
|
||||||
# connections
|
# connections
|
||||||
|
existing_connections = [] # for bundles, avoid multiple edges between a bundle's wire's start and end node
|
||||||
for x in c.connections:
|
for x in c.connections:
|
||||||
if isinstance(x[2], int): # check if it's an actual wire and not a shield
|
if isinstance(x.via_port, int): # check if it's an actual wire and not a shield
|
||||||
search_color = c.colors[x[2]-1]
|
search_color = c.colors[x.via_port-1]
|
||||||
if search_color in color_hex:
|
if search_color in color_hex:
|
||||||
dot.attr('edge',color='#000000:{wire_color}:#000000'.format(wire_color=color_hex[search_color]))
|
dot.attr('edge',color='#000000:{wire_color}:#000000'.format(wire_color=color_hex[search_color]))
|
||||||
else: # color name not found
|
else: # color name not found
|
||||||
dot.attr('edge',color='#000000')
|
dot.attr('edge',color='#000000')
|
||||||
else: # it's a shield connection
|
else: # it's a shield connection
|
||||||
dot.attr('edge',color='#000000')
|
dot.attr('edge',color='#000000')
|
||||||
if x[1] is not None: # connect to left
|
|
||||||
dot.edge('{from_name}:p{from_port}r'.format(from_name=x[0],from_port=x[1]),
|
if c.type == 'bundle':
|
||||||
'{via_name}:w{via_wire}{via_subport}'.format(via_name=c.name, via_wire=x[2], via_subport='i' if c.show_pinout else ''))
|
labeltext = '{sp}{color}'.format(color=translate_color(c.colors[x.via_port-1], self.color_mode), sp=' ' * 35)
|
||||||
# self.nodes[x[0]].ports_right = True
|
if x.via_port not in existing_connections:
|
||||||
if x[4] is not None: # connect to right
|
dot.edge('{via_name}_w{via_wire}l'.format(via_name=c.name, via_wire=x.via_port),
|
||||||
dot.edge('{via_name}:w{via_wire}{via_subport}'.format(via_name=c.name, via_wire=x[2], via_subport='o' if c.show_pinout else ''),
|
'{via_name}_w{via_wire}r'.format(via_name=c.name, via_wire=x.via_port),
|
||||||
'{to_name}:p{to_port}l'.format(to_name=x[3], to_port=x[4]))
|
taillabel=labeltext,
|
||||||
# self.nodes[x[3]].ports_left = True
|
labelangle='60',
|
||||||
|
labeldist='0')
|
||||||
|
existing_connections.append(x.via_port)
|
||||||
|
|
||||||
|
if x.from_port is not None: # connect to left
|
||||||
|
if c.type == 'bundle':
|
||||||
|
dot.edge('{from_name}:p{from_port}r'.format(from_name=x.from_name, from_port=x.from_port),
|
||||||
|
'{via_name}_w{via_wire}l:w'.format(via_name=c.name, via_wire=x.via_port),
|
||||||
|
headlabel='{}{}:{}'.format(' ' * 12, x.from_name, x.from_port),
|
||||||
|
labelangle='-60',
|
||||||
|
labeldist='0')
|
||||||
|
else:
|
||||||
|
dot.edge('{from_name}:p{from_port}r'.format(from_name=x.from_name, from_port=x.from_port),
|
||||||
|
'{via_name}:w{via_wire}{via_subport}'.format(via_name=c.name, via_wire=x.via_port, via_subport='i' if c.show_pinout else ''))
|
||||||
|
if x.to_port is not None: # connect to right
|
||||||
|
if c.type == 'bundle':
|
||||||
|
dot.edge('{via_name}_w{via_wire}r:e'.format(via_name=c.name, via_wire=x.via_port),
|
||||||
|
'{to_name}:p{to_port}l'.format(to_name=x.to_name, to_port=x.to_port),
|
||||||
|
taillabel='{}:{}{}'.format(x.to_name, x.to_port,' ' * 12),
|
||||||
|
labelangle='60',
|
||||||
|
labeldist='0')
|
||||||
|
else:
|
||||||
|
dot.edge('{via_name}:w{via_wire}{via_subport}'.format(via_name=c.name, via_wire=x.via_port, via_subport='o' if c.show_pinout else ''),
|
||||||
|
'{to_name}:p{to_port}l'.format(to_name=x.to_name, to_port=x.to_port))
|
||||||
|
|
||||||
return dot
|
return dot
|
||||||
|
|
||||||
@ -184,8 +234,8 @@ class Node:
|
|||||||
gender: str = None
|
gender: str = None
|
||||||
num_pins: int = None
|
num_pins: int = None
|
||||||
pinout: List[Any] = field(default_factory=list)
|
pinout: List[Any] = field(default_factory=list)
|
||||||
show_name: bool = False
|
show_name: bool = True
|
||||||
show_num_pins: bool = False
|
show_num_pins: bool = True
|
||||||
|
|
||||||
def __post_init__(self):
|
def __post_init__(self):
|
||||||
self.ports_left = False
|
self.ports_left = False
|
||||||
@ -206,6 +256,7 @@ class Node:
|
|||||||
@dataclass
|
@dataclass
|
||||||
class Cable:
|
class Cable:
|
||||||
name: str
|
name: str
|
||||||
|
type: str = None
|
||||||
mm2: float = None
|
mm2: float = None
|
||||||
awg: int = None
|
awg: int = None
|
||||||
show_equiv: bool = False
|
show_equiv: bool = False
|
||||||
@ -214,7 +265,7 @@ class Cable:
|
|||||||
shield: bool = False
|
shield: bool = False
|
||||||
colors: List[Any] = field(default_factory=list)
|
colors: List[Any] = field(default_factory=list)
|
||||||
color_code: str = None
|
color_code: str = None
|
||||||
show_name: bool = False
|
show_name: bool = True
|
||||||
show_pinout: bool = False
|
show_pinout: bool = False
|
||||||
show_num_wires: bool = True
|
show_num_wires: bool = True
|
||||||
|
|
||||||
@ -252,11 +303,20 @@ class Cable:
|
|||||||
if len(from_pin) != len(to_pin):
|
if len(from_pin) != len(to_pin):
|
||||||
raise Exception('from_pin must have the same number of elements as to_pin')
|
raise Exception('from_pin must have the same number of elements as to_pin')
|
||||||
for i, x in enumerate(from_pin):
|
for i, x in enumerate(from_pin):
|
||||||
self.connections.append((from_name, from_pin[i], via_pin[i], to_name, to_pin[i]))
|
# self.connections.append((from_name, from_pin[i], via_pin[i], to_name, to_pin[i]))
|
||||||
|
self.connections.append(Connection(from_name, from_pin[i], via_pin[i], to_name, to_pin[i]))
|
||||||
|
|
||||||
def connect_all_straight(self, from_name, to_name):
|
def connect_all_straight(self, from_name, to_name):
|
||||||
self.connect(from_name, 'auto', 'auto', to_name, 'auto')
|
self.connect(from_name, 'auto', 'auto', to_name, 'auto')
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Connection:
|
||||||
|
from_name: Any
|
||||||
|
from_port: Any
|
||||||
|
via_port: Any
|
||||||
|
to_name: Any
|
||||||
|
to_port: Any
|
||||||
|
|
||||||
def nested(input):
|
def nested(input):
|
||||||
l = []
|
l = []
|
||||||
for x in input:
|
for x in input:
|
||||||
@ -286,7 +346,7 @@ def translate_color(input, color_mode):
|
|||||||
if color_mode == 'full':
|
if color_mode == 'full':
|
||||||
output = color_full[input].lower()
|
output = color_full[input].lower()
|
||||||
elif color_mode == 'FULL':
|
elif color_mode == 'FULL':
|
||||||
output = color_hex[input].upper()
|
output = color_full[input].upper()
|
||||||
elif color_mode == 'hex':
|
elif color_mode == 'hex':
|
||||||
output = color_hex[input].lower()
|
output = color_hex[input].lower()
|
||||||
elif color_mode == 'HEX':
|
elif color_mode == 'HEX':
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import wireviz
|
|||||||
filename = '../examples/example1.yml'
|
filename = '../examples/example1.yml'
|
||||||
filename = '../examples/example2.yml'
|
filename = '../examples/example2.yml'
|
||||||
filename = '../examples/ferrules.yml'
|
filename = '../examples/ferrules.yml'
|
||||||
|
filename = '../examples/bundles.yml'
|
||||||
|
|
||||||
def check_designators(what, where):
|
def check_designators(what, where):
|
||||||
for i, x in enumerate(what):
|
for i, x in enumerate(what):
|
||||||
|
|||||||
66
todo.md
66
todo.md
@ -1,28 +1,58 @@
|
|||||||
# To-do:
|
# To-do:
|
||||||
|
|
||||||
* Set global parameters (show_pins, ...) and allow override on per-item basis
|
## Support for more connector types
|
||||||
|
|
||||||
* Generic connectors
|
* Generic connectors
|
||||||
* ferrules
|
* Ferrules
|
||||||
* blade terminals
|
* Blade terminals
|
||||||
* loose ends
|
* Loose ends / stubs
|
||||||
* graphical representation?
|
* Graphical representation?
|
||||||
* Support for cable splicing (as connector type)
|
* Inline connectors (IDC)
|
||||||
* new wire look?
|
* Possibly join two logical wires into one physical wire, add up length for BOM creation
|
||||||
* distinguish between cables and wire bundles
|
* Designators like W1_1, W1_2 or similar to group them?
|
||||||
* improve nomenclature
|
|
||||||
|
## Support for more wire types
|
||||||
|
|
||||||
|
* Coax cables
|
||||||
|
* Graphical representation
|
||||||
|
* Twisted pairs
|
||||||
|
* Logical representation
|
||||||
|
* Graphical representation
|
||||||
|
* Ribbon cables
|
||||||
|
* Folds
|
||||||
|
* Splits
|
||||||
|
* Orientation of IDC connectors
|
||||||
|
|
||||||
|
## Support for more links/connections
|
||||||
|
|
||||||
|
* Cable splicing
|
||||||
|
* as pseudo-connector?
|
||||||
|
* Heatshrink / sheathing
|
||||||
|
|
||||||
|
## Visualization
|
||||||
|
|
||||||
|
* Parse and render double-colored, striped cables ('RDBU' etc)
|
||||||
|
* Show from/to inside wire node (better netlist)
|
||||||
|
* Implemented in wire bundles only
|
||||||
|
* Display picture of connector underneath (including pin 1 location)
|
||||||
|
|
||||||
|
## Export
|
||||||
|
|
||||||
|
* Export to PDF with frame, title block, ...
|
||||||
|
* Automatic BOM generation
|
||||||
|
|
||||||
|
## Other
|
||||||
|
|
||||||
|
* Set global parameters (show_pins, ...) and allow override on per-item basis
|
||||||
|
* Improve nomenclature
|
||||||
* terminal (connector, ferrule, blade, loose)
|
* terminal (connector, ferrule, blade, loose)
|
||||||
* link (cable, wire bundle)
|
* link (cable, wire bundle)
|
||||||
* show from/to inside wire node
|
|
||||||
* Allow custom GraphViz code before/after WireViz-generated code
|
* Allow custom GraphViz code before/after WireViz-generated code
|
||||||
* Display picture of connector underneath (including pin 1 location)
|
* Make "unit tests" for different features/situations
|
||||||
* export to PDF with frame, title block, ...
|
* Missing parameters
|
||||||
* Automatic BOM generation
|
* Connection formats
|
||||||
* Allow
|
|
||||||
* make "unit tests" for different features/situations
|
|
||||||
* missing parameters
|
|
||||||
* connection formats
|
|
||||||
* single wire 1
|
* single wire 1
|
||||||
* multiple wires [1,2,3]
|
* multiple wires [1,2,3]
|
||||||
* wire ranges [1-10]
|
* wire ranges [1-10]
|
||||||
* loops
|
* Loops
|
||||||
* ...
|
* ...
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user