improved color palette generation; removed some predefined gradients
This commit is contained in:
parent
c4020080b3
commit
d4896e1978
@ -75,11 +75,35 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 941,
|
"execution_count": 3,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"collapsed": false
|
"collapsed": false
|
||||||
},
|
},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stderr",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"INFO: Recompiling stale cache file /home/tom/.julia/lib/v0.4/OnlineStats.ji for module OnlineStats.\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
" 0.366577 seconds (2.08 M allocations: 156.080 MB, 3.22% gc time)\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"maxabs(β - coef(o)) for\n",
|
||||||
|
"\n",
|
||||||
|
"glm: 0.005542500269197448\n",
|
||||||
|
"sgd: NaN\n",
|
||||||
|
"proxgrad: 0.004859362717011262\n",
|
||||||
|
"rda: 0.007882387662070361\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"using OnlineAI\n",
|
"using OnlineAI\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -87,7 +111,11 @@
|
|||||||
"numInputs = 2\n",
|
"numInputs = 2\n",
|
||||||
"numOutputs = 1\n",
|
"numOutputs = 1\n",
|
||||||
"hiddenLayerStructure = [3,3,2]\n",
|
"hiddenLayerStructure = [3,3,2]\n",
|
||||||
"net = buildClassificationNet(numInputs, numOutputs, hiddenLayerStructure; hiddenActivation = TanhActivation())\n",
|
"net = buildClassificationNet(numInputs, numOutputs, hiddenLayerStructure;\n",
|
||||||
|
" hiddenActivation = TanhActivation(),\n",
|
||||||
|
" finalActivation = TanhActivation(),\n",
|
||||||
|
"\n",
|
||||||
|
")\n",
|
||||||
"\n",
|
"\n",
|
||||||
"# show the network\n",
|
"# show the network\n",
|
||||||
"viz = visualize(net);"
|
"viz = visualize(net);"
|
||||||
@ -102,7 +130,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 942,
|
"execution_count": null,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"collapsed": false
|
"collapsed": false
|
||||||
},
|
},
|
||||||
@ -133,27 +161,16 @@
|
|||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"source": [
|
"source": [
|
||||||
"# Yikes... that looks tricky to separate..."
|
"# That looks tricky..."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 945,
|
"execution_count": null,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"collapsed": false
|
"collapsed": false
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [],
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"text/plain": [
|
|
||||||
"activateHidden (generic function with 1 method)"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"execution_count": 945,
|
|
||||||
"metadata": {},
|
|
||||||
"output_type": "execute_result"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
"source": [
|
||||||
"# function to sample from x's\n",
|
"# function to sample from x's\n",
|
||||||
"xsample() = rand(Distributions.Uniform(lim...)) \n",
|
"xsample() = rand(Distributions.Uniform(lim...)) \n",
|
||||||
@ -169,14 +186,19 @@
|
|||||||
"\n",
|
"\n",
|
||||||
"# take x matrix and convert to the first layer's activation\n",
|
"# take x matrix and convert to the first layer's activation\n",
|
||||||
"function activateHidden(net, x)\n",
|
"function activateHidden(net, x)\n",
|
||||||
" input = x\n",
|
"# input = x\n",
|
||||||
" for layer in net.layers[1:end-1]\n",
|
" @assert net.layers[end].nin == 2\n",
|
||||||
" proj = Array(nrows(x), layer.nout)\n",
|
" proj = Array(nrows(x), 2)\n",
|
||||||
" for i in 1:nrows(x)\n",
|
" for i in 1:nrows(x)\n",
|
||||||
|
" data = row(x,i)\n",
|
||||||
|
" for layer in net.layers[1:end-1]\n",
|
||||||
|
" #proj = Array(nrows(x), layer.nout)\n",
|
||||||
" OnlineAI.forward!(layer, row(proj,i), false)\n",
|
" OnlineAI.forward!(layer, row(proj,i), false)\n",
|
||||||
" row!(proj, i, layer.a)\n",
|
" data = layer.a\n",
|
||||||
|
"# row!(proj, i, layer.a)\n",
|
||||||
" end\n",
|
" end\n",
|
||||||
" input = proj\n",
|
" row!(proj, i, data)\n",
|
||||||
|
"# input = proj\n",
|
||||||
" end\n",
|
" end\n",
|
||||||
" vec(proj[:,1]), vec(proj[:,2])\n",
|
" vec(proj[:,1]), vec(proj[:,2])\n",
|
||||||
"end "
|
"end "
|
||||||
@ -184,22 +206,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 946,
|
"execution_count": null,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"collapsed": false
|
"collapsed": false
|
||||||
},
|
},
|
||||||
"outputs": [
|
"outputs": [],
|
||||||
{
|
|
||||||
"ename": "LoadError",
|
|
||||||
"evalue": "LoadError: MethodError: `convert` has no method matching convert(::Type{Array{T,N}}, ::Int64, ::Int64)\nThis may have arisen from a call to the constructor Array{T,N}(...),\nsince type constructors fall back to convert methods.\nClosest candidates are:\n convert{T,N}(::Type{Array{T,N}}, !Matched::DataArrays.DataArray{T,N}, ::Any)\n convert{T,R,N}(::Type{Array{T,N}}, !Matched::DataArrays.PooledDataArray{T,R,N}, ::Any)\n Array{T}(!Matched::Type{T}, ::Integer)\n ...\nwhile loading In[946], in expression starting on line 8",
|
|
||||||
"output_type": "error",
|
|
||||||
"traceback": [
|
|
||||||
"LoadError: MethodError: `convert` has no method matching convert(::Type{Array{T,N}}, ::Int64, ::Int64)\nThis may have arisen from a call to the constructor Array{T,N}(...),\nsince type constructors fall back to convert methods.\nClosest candidates are:\n convert{T,N}(::Type{Array{T,N}}, !Matched::DataArrays.DataArray{T,N}, ::Any)\n convert{T,R,N}(::Type{Array{T,N}}, !Matched::DataArrays.PooledDataArray{T,R,N}, ::Any)\n Array{T}(!Matched::Type{T}, ::Integer)\n ...\nwhile loading In[946], in expression starting on line 8",
|
|
||||||
"",
|
|
||||||
" in activateHidden at In[945]:17"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
"source": [
|
||||||
"# update net with new samples\n",
|
"# update net with new samples\n",
|
||||||
"for i in 1:10000\n",
|
"for i in 1:10000\n",
|
||||||
@ -232,7 +243,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 940,
|
"execution_count": null,
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"collapsed": false
|
"collapsed": false
|
||||||
},
|
},
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
179
src/colors.jl
179
src/colors.jl
@ -104,6 +104,10 @@ colorscheme(v::AVec) = ColorVector(v)
|
|||||||
colorscheme(m::AMat) = size(m,1) == 1 ? map(colorscheme, m) : [colorscheme(m[:,i]) for i in 1:size(m,2)]'
|
colorscheme(m::AMat) = size(m,1) == 1 ? map(colorscheme, m) : [colorscheme(m[:,i]) for i in 1:size(m,2)]'
|
||||||
colorscheme(c::Colorant) = ColorWrapper(c)
|
colorscheme(c::Colorant) = ColorWrapper(c)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
const _rainbowColors = [colorant"blue", colorant"purple", colorant"green", colorant"orange", colorant"red"]
|
const _rainbowColors = [colorant"blue", colorant"purple", colorant"green", colorant"orange", colorant"red"]
|
||||||
const _testColors = [colorant"darkblue", colorant"blueviolet", colorant"darkcyan",colorant"green",
|
const _testColors = [colorant"darkblue", colorant"blueviolet", colorant"darkcyan",colorant"green",
|
||||||
darken(colorant"yellow",0.3), colorant"orange", darken(colorant"red",0.2)]
|
darken(colorant"yellow",0.3), colorant"orange", darken(colorant"red",0.2)]
|
||||||
@ -121,33 +125,8 @@ const _testColors = [colorant"darkblue", colorant"blueviolet", colorant"darkcya
|
|||||||
:darkrainbow => map(darken, _rainbowColors),
|
:darkrainbow => map(darken, _rainbowColors),
|
||||||
:darktest => _testColors,
|
:darktest => _testColors,
|
||||||
:lighttest => map(c -> lighten(c, 0.3), _testColors),
|
:lighttest => map(c -> lighten(c, 0.3), _testColors),
|
||||||
:mlab => [RGB(0, 0.4470, 0.7410),RGB(0.4940, 0.1840, 0.5560),RGB(0.9290, 0.6940, 0.1250),
|
|
||||||
RGB(0.4660, 0.6740, 0.1880),RGB(0.3010, 0.7450, 0.9330),RGB(0.6350, 0.0780, 0.1840),
|
|
||||||
RGB(0.8500, 0.3250, 0.0980)],
|
|
||||||
:sb_deep => map(c->parse(Colorant,c), ["#4C72B0", "#55A868", "#C44E52", "#8172B2", "#CCB974", "#64B5CD"]),
|
|
||||||
:sb_muted => map(c->parse(Colorant,c), ["#4878CF", "#6ACC65", "#D65F5F", "#B47CC7", "#C4AD66", "#77BEDB"]),
|
|
||||||
:sb_pastl => map(c->parse(Colorant,c), ["#92C6FF", "#97F0AA", "#FF9F9A", "#D0BBFF", "#FFFEA3", "#B0E0E6"]),
|
|
||||||
:sb_bright => map(c->parse(Colorant,c), ["#003FFF", "#03ED3A", "#E8000B", "#8A2BE2", "#FFC400", "#00D7FF"]),
|
|
||||||
:sb_dark => map(c->parse(Colorant,c), ["#001C7F", "#017517", "#8C0900", "#7600A1", "#B8860B", "#006374"]),
|
|
||||||
:sb_colorblind=> map(c->parse(Colorant,c), ["#0072B2", "#009E73", "#D55E00", "#CC79A7", "#F0E442", "#56B4E9"]),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# # TODO: maybe try to include:
|
|
||||||
|
|
||||||
# SEABORN_PALETTES = dict(
|
|
||||||
# deep=["#4C72B0", "#55A868", "#C44E52",
|
|
||||||
# "#8172B2", "#CCB974", "#64B5CD"],
|
|
||||||
# muted=["#4878CF", "#6ACC65", "#D65F5F",
|
|
||||||
# "#B47CC7", "#C4AD66", "#77BEDB"],
|
|
||||||
# pastel=["#92C6FF", "#97F0AA", "#FF9F9A",
|
|
||||||
# "#D0BBFF", "#FFFEA3", "#B0E0E6"],
|
|
||||||
# bright=["#003FFF", "#03ED3A", "#E8000B",
|
|
||||||
# "#8A2BE2", "#FFC400", "#00D7FF"],
|
|
||||||
# dark=["#001C7F", "#017517", "#8C0900",
|
|
||||||
# "#7600A1", "#B8860B", "#006374"],
|
|
||||||
# colorblind=["#0072B2", "#009E73", "#D55E00",
|
|
||||||
# "#CC79A7", "#F0E442", "#56B4E9"]
|
|
||||||
# )
|
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
@ -229,6 +208,44 @@ function interpolate(v1::Real, v2::Real, w::Real)
|
|||||||
(1-w) * v1 + w * v2
|
(1-w) * v1 + w * v2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
# Methods to automatically generate gradients for color selection based on
|
||||||
|
# background color and a short list of seed colors
|
||||||
|
|
||||||
|
function adjust_lch(color, l, c)
|
||||||
|
lch = LCHab(color)
|
||||||
|
convert(RGB, LCHab(l, c, lch.h))
|
||||||
|
end
|
||||||
|
|
||||||
|
function lightness_from_background(bgcolor)
|
||||||
|
bglight = LCHab(bgcolor).l
|
||||||
|
0.45bglight + 55.0 * (bglight < 50.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gradient_from_list(cs)
|
||||||
|
zvalues = Plots.get_zvalues(length(cs))
|
||||||
|
indices = sortperm(zvalues)
|
||||||
|
sorted_colors = map(RGB, cs[indices])
|
||||||
|
sorted_zvalues = zvalues[indices]
|
||||||
|
ColorGradient(sorted_colors, sorted_zvalues)
|
||||||
|
end
|
||||||
|
|
||||||
|
function generate_colorgradient(bgcolor = colorant"white";
|
||||||
|
color_bases = [colorant"steelblue", colorant"indianred"],
|
||||||
|
lightness = lightness_from_background(bgcolor),
|
||||||
|
n = 9)
|
||||||
|
seed_colors = map(c -> adjust_lch(c,lightness,50), vcat(bgcolor, color_bases))
|
||||||
|
colors = distinguishable_colors(n,
|
||||||
|
seed_colors,
|
||||||
|
lchoices=Float64[lightness],
|
||||||
|
cchoices=Float64[50],
|
||||||
|
hchoices=linspace(0, 340, 20)
|
||||||
|
)[2:end]
|
||||||
|
gradient_from_list(colors)
|
||||||
|
end
|
||||||
|
|
||||||
# --------------------------------------------------------------
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
"Wraps a function, taking an index and returning a Colorant"
|
"Wraps a function, taking an index and returning a Colorant"
|
||||||
@ -304,44 +321,80 @@ const _lightColors = filter(islight, _allColors)
|
|||||||
const _sortedColorsForDarkBackground = vcat(_lightColors, reverse(_darkColors[2:end]))
|
const _sortedColorsForDarkBackground = vcat(_lightColors, reverse(_darkColors[2:end]))
|
||||||
const _sortedColorsForLightBackground = vcat(_darkColors, reverse(_lightColors[2:end]))
|
const _sortedColorsForLightBackground = vcat(_darkColors, reverse(_lightColors[2:end]))
|
||||||
|
|
||||||
const _defaultNumColors = 20
|
const _defaultNumColors = 17
|
||||||
|
|
||||||
function getPaletteUsingDistinguishableColors(bgcolor::Colorant, numcolors::Int = _defaultNumColors)
|
# function getPaletteUsingDistinguishableColors(bgcolor::Colorant, numcolors::Int = _defaultNumColors)
|
||||||
palette = distinguishable_colors(numcolors, bgcolor)[2:end]
|
# palette = distinguishable_colors(numcolors, bgcolor)[2:end]
|
||||||
|
|
||||||
# try to adjust lightness away from background color
|
# # try to adjust lightness away from background color
|
||||||
bg_lab = Lab(bgcolor)
|
# bg_lab = Lab(bgcolor)
|
||||||
palette = RGB{Float64}[begin
|
# palette = RGB{Float64}[begin
|
||||||
lab = Lab(rgb)
|
# lab = Lab(rgb)
|
||||||
Lab(
|
# Lab(
|
||||||
adjustAway(lab.l, bg_lab.l, 25, 75),
|
# adjustAway(lab.l, bg_lab.l, 25, 75),
|
||||||
lab.a,
|
# lab.a,
|
||||||
lab.b
|
# lab.b
|
||||||
)
|
# )
|
||||||
end for rgb in palette]
|
# end for rgb in palette]
|
||||||
|
# end
|
||||||
|
|
||||||
|
# function getPaletteUsingFixedColorList(bgcolor::Colorant, numcolors::Int = _defaultNumColors)
|
||||||
|
# palette = isdark(bgcolor) ? _sortedColorsForDarkBackground : _sortedColorsForLightBackground
|
||||||
|
# palette[1:min(numcolors,length(palette))]
|
||||||
|
# end
|
||||||
|
|
||||||
|
# function getPaletteUsingColorDiffFromBackground(bgcolor::Colorant, numcolors::Int = _defaultNumColors)
|
||||||
|
# colordiffs = [colordiff(c, bgcolor) for c in _allColors]
|
||||||
|
# mindiff = colordiffs[reverse(sortperm(colordiffs))[numcolors]]
|
||||||
|
# filter(c -> colordiff(c, bgcolor) >= mindiff, _allColors)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# --------------------------------------------------------------
|
||||||
|
|
||||||
|
# Methods to automatically generate gradients for color selection based on
|
||||||
|
# background color and a short list of seed colors
|
||||||
|
|
||||||
|
# here are some magic constants that could be changed if you really want
|
||||||
|
const _bgratio = [0.4]
|
||||||
|
const _lch_c_const = [50]
|
||||||
|
|
||||||
|
function adjust_lch(color, l, c)
|
||||||
|
lch = LCHab(color)
|
||||||
|
convert(RGB, LCHab(l, c, lch.h))
|
||||||
end
|
end
|
||||||
|
|
||||||
function getPaletteUsingFixedColorList(bgcolor::Colorant, numcolors::Int = _defaultNumColors)
|
function lightness_from_background(bgcolor)
|
||||||
palette = isdark(bgcolor) ? _sortedColorsForDarkBackground : _sortedColorsForLightBackground
|
bglight = LCHab(bgcolor).l
|
||||||
palette[1:min(numcolors,length(palette))]
|
_bgratio[1] * bglight + 100.0 * (1 - _bgratio[1]) * (bglight < 50.0)
|
||||||
end
|
end
|
||||||
|
|
||||||
function getPaletteUsingColorDiffFromBackground(bgcolor::Colorant, numcolors::Int = _defaultNumColors)
|
function gradient_from_list(cs)
|
||||||
colordiffs = [colordiff(c, bgcolor) for c in _allColors]
|
zvalues = Plots.get_zvalues(length(cs))
|
||||||
mindiff = colordiffs[reverse(sortperm(colordiffs))[numcolors]]
|
indices = sortperm(zvalues)
|
||||||
filter(c -> colordiff(c, bgcolor) >= mindiff, _allColors)
|
sorted_colors = map(RGB, cs[indices])
|
||||||
|
sorted_zvalues = zvalues[indices]
|
||||||
|
ColorGradient(sorted_colors, sorted_zvalues)
|
||||||
end
|
end
|
||||||
|
|
||||||
function getPaletteUsingGradientSymbol(palette, bgcolor::Colorant, numcolors::Int = _defaultNumColors) #; gradientsym::Symbol = :auto)
|
function generate_colorgradient(bgcolor = colorant"white";
|
||||||
# @show gradientsym
|
color_bases = [colorant"steelblue", colorant"indianred"],
|
||||||
if palette == :auto
|
lightness = lightness_from_background(bgcolor),
|
||||||
# grad = ColorGradient(_gradients[isdark(bgcolor) ? :lightrainbow : :darkrainbow])
|
n = _defaultNumColors)
|
||||||
# grad = ColorGradient(_gradients[isdark(bgcolor) ? :lighttest : :darktest])
|
seed_colors = map(c -> adjust_lch(c,lightness, _lch_c_const[1]), vcat(bgcolor, color_bases))
|
||||||
grad = ColorGradient(:mlab)
|
colors = distinguishable_colors(n,
|
||||||
# elseif typeof(palette) <: AVec || typeof(palette) <: ColorGradient
|
seed_colors,
|
||||||
# grad = ColorGradient(palette)
|
lchoices=Float64[lightness],
|
||||||
|
cchoices=Float64[_lch_c_const[1]],
|
||||||
|
hchoices=linspace(0, 340, 20)
|
||||||
|
)[2:end]
|
||||||
|
gradient_from_list(colors)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_color_palette(palette, bgcolor::Colorant, numcolors::Integer)
|
||||||
|
grad = if palette == :auto
|
||||||
|
generate_colorgradient(bgcolor)
|
||||||
else
|
else
|
||||||
grad = ColorGradient(palette)
|
ColorGradient(palette)
|
||||||
end
|
end
|
||||||
zrng = get_zvalues(numcolors)
|
zrng = get_zvalues(numcolors)
|
||||||
RGBA[getColorZ(grad, z) for z in zrng]
|
RGBA[getColorZ(grad, z) for z in zrng]
|
||||||
@ -396,24 +449,18 @@ function handlePlotColors(::PlottingPackage, d::Dict)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# d[:color_palette] = getPaletteUsingDistinguishableColors(bgcolor)
|
|
||||||
# d[:color_palette] = getPaletteUsingFixedColorList(bgcolor)
|
d[:color_palette] = get_color_palette(get(d, :color_palette, :auto), bgcolor, 100)
|
||||||
# d[:color_palette] = getPaletteUsingColorDiffFromBackground(bgcolor)
|
|
||||||
d[:color_palette] = getPaletteUsingGradientSymbol(get(d, :color_palette, :auto), bgcolor)
|
|
||||||
|
|
||||||
# set the foreground color (text, ticks, gridlines) to be white or black depending
|
# set the foreground color (text, ticks, gridlines) to be white or black depending
|
||||||
# on how dark the background is.
|
# on how dark the background is.
|
||||||
fgcolor = get(d, :foreground_color, :auto)
|
fgcolor = get(d, :foreground_color, :auto)
|
||||||
if fgcolor == :auto
|
fgcolor = if fgcolor == :auto
|
||||||
fgcolor = isdark(bgcolor) ? colorant"white" : colorant"black"
|
isdark(bgcolor) ? colorant"white" : colorant"black"
|
||||||
else
|
else
|
||||||
fgcolor = convertColor(fgcolor)
|
convertColor(fgcolor)
|
||||||
end
|
end
|
||||||
# if !haskey(d, :foreground_color) || d[:foreground_color] == :auto
|
|
||||||
# d[:foreground_color] = isdark(bgcolor) ? colorant"white" : colorant"black"
|
|
||||||
# else
|
|
||||||
# d[:foreground_color] = convertColor(d[:foreground_color])
|
|
||||||
# end
|
|
||||||
|
|
||||||
# bgcolor
|
# bgcolor
|
||||||
d[:background_color] = colorscheme(bgcolor)
|
d[:background_color] = colorscheme(bgcolor)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user