Skip to content

Commit

Permalink
Merge pull request 'Add grid lines and Y-axis labels to Bar Chart' (#111
Browse files Browse the repository at this point in the history
) from nostrbandstats3 into main

Reviewed-on: pulls/111
  • Loading branch information
vicariousdrama committed Jul 27, 2023
2 parents c5207ed + 91f9102 commit 5937738
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 29 deletions.
Binary file removed images/nostrbandstats-activepubkeys.png
Binary file not shown.
Binary file modified images/nostrbandstats-activepubkeysperday.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/nostrbandstats-badgescreatedperday.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/nostrbandstats-zapsperday.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed images/nostrbandstats.png
Binary file not shown.
15 changes: 10 additions & 5 deletions scripts/nostrbandstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def fetchData(self):
# set date range for charts based on data
activeRelays, _ = vicariouschart.getNestedField(self.transformStats, "daily.datasets.active_relays")
self.dateBegin = activeRelays[0]["d"]
self.dateEnd = activeRelays[-1]["d"]
self.dateEnd = activeRelays[-2]["d"] # second to last to only include full day

def transformData(self):
# make a copy to work with
Expand All @@ -75,26 +75,31 @@ def transformData(self):
if "c" in o:
listk.append(o["c"])
valueAdded = True
if valueAdded: datasets[flatk] = listk
if valueAdded:
listk.pop() # remove last entry as its a partial day
datasets[flatk] = listk
self.transformStats = tstats

def renderChart(self, pageSuffix, headerText, baseKey, colorIndex):
attributionSize = int(self.height * 14/320)
labelSize = int(self.height * 12/320)
showGridLines = True
gridLegendWidth = 0
if showGridLines: gridLegendWidth = 50
self.headerText = headerText
self.pageSuffix = pageSuffix
super().startImage()
nestedField = f"daily.datasets.{baseKey}_flat"
chartStats, _ = vicariouschart.getNestedField(self.transformStats, nestedField)
vicariouschart.drawBarChart(draw=self.draw, left=0, top=self.getInsetTop(),
width=self.width, height=self.getInsetHeight(),
width=self.width, height=self.getInsetHeight()-10,
theList=chartStats, fieldName=None,
showLabels=True, chartLabel="", grouping=1,
valueColor=self.graphDataColors[colorIndex],
showAverage=True, averageColor=self.graphAverageColor,
movingAverageDuration=7, movingAverageColor="#33ff33", movingAverageWidth=2,
borderColor=self.graphBorderColor)
vicariouschart.drawLabel(self.draw, self.dateBegin, labelSize, "tl", 0 + 2, self.getInsetTop()+2 )
borderColor=self.graphBorderColor, showGridLines=showGridLines)
vicariouschart.drawLabel(self.draw, self.dateBegin, labelSize, "tl", gridLegendWidth + 0 + 2, self.getInsetTop()+2 )
vicariouschart.drawLabel(self.draw, self.dateEnd, labelSize, "tr", self.width - 3, self.getInsetTop()+2)
vicarioustext.drawbottomlefttext(self.draw, self.attributionLine, attributionSize, 0, self.height, ImageColor.getrgb(self.attributionColor))
super().finishImage()
Expand Down
96 changes: 72 additions & 24 deletions scripts/vicariouschart.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,74 @@
import time
import vicarioustext

def getShortTextOfNumber(n=0):
s=str(n)
ul = len(s) // 3
ut = ""
if ul == 0: return s
if ul == 1: ut = "K"
if ul == 2: ut = "M"
if ul == 3: ut = "B"
um = len(s) % 3
if um == 0: return "." + str(s[2:4]) + ut
if um == 1: return str(s[:1]) + "." + str(s[1:2]) + ut
if um == 2: return str(s[:2]) + "." + str(s[2:3]) + ut

def drawBarChart(draw, left=0, top=0, width=480, height=320, theList=[], fieldName=None,
showLabels=False, chartLabel="", grouping=1, valueColor="#ff8888",
showAverage=True, averageColor="#8888ff",
movingAverageDuration=0, movingAverageColor="#88ff88",
movingAverageWidth=3, borderColor="#888888"
movingAverageWidth=3, borderColor="#888888",
showGridLines=False, gridLineColor="#444444"
):
low, high, avg, _, _ = getFieldMinMaxAvgValues(theList, fieldName)
lowx, highx = -1, -1
lowy, highy = -1, -1
lowFound, highFound = False, False
minfloor = .8
maxy = top + height - 1
ma = []
# chart area
chartLow = low * minfloor
chartHigh = high
chartLeft = left
chartTop = top
chartWidth = width
chartHeight = height
# grid lines needs space for labels
gridLegendWidth = 50
if showGridLines and (high - low > 0):
chartLeft += gridLegendWidth
chartWidth -= gridLegendWidth
difference = high - low
units = int(math.pow(10, len(str(difference)) - 1))
chartHigh = int(math.ceil(high / units) * units)
chartLow = int(math.floor(low / units) * units)
if chartHigh - (units//2) > high: chartHigh -= (units//2)
if chartLow + (units//2) < low: chartLow += (units//2)
chartDifference = (chartHigh - chartLow)
gridSteps = 5
gridStepSize = (float(chartDifference) / float(gridSteps))
gridSteps += 1
for g in range(0, gridSteps):
gridStepAmount = int(chartLow + (gridStepSize * g))
gridStepY = chartTop + int((1.0-(float(gridStepAmount-chartLow) / float(chartHigh-chartLow))) * chartHeight)
# dashed line
for gridStepX in range(chartLeft-(gridLegendWidth//2), chartLeft+chartWidth-1, 6):
draw.line(xy=[(gridStepX,gridStepY),(gridStepX+2,gridStepY)],fill=ImageColor.getrgb(gridLineColor),width=1)
# label it
gridlabelpos = "br" if g == 0 else "r"
gridlabelpos = "tr" if g == gridSteps - 1 else gridlabelpos
gridStepText = getShortTextOfNumber(gridStepAmount)
drawLabel(draw, gridStepText, 10, gridlabelpos, (gridLegendWidth*2)//3, gridStepY)
# maximum y allowed is the bottom minus 1
maxy = chartTop + chartHeight - 1
# draw average line
if showAverage:
avgy = top if high == 0 else top + int( (1.0-(float(avg) / float(high))) * height)
draw.line(xy=[(left,avgy),(left+width-1,avgy)],fill=ImageColor.getrgb(averageColor),width=2)
# process each column from right to left
avgy = chartTop if high == 0 else chartTop + int((1.0-(float(avg-chartLow) / float(chartHigh-chartLow))) * chartHeight)
draw.line(xy=[(chartLeft,avgy),(chartLeft+chartWidth-1,avgy)],fill=ImageColor.getrgb(averageColor),width=2)
# process each column
l = len(theList) - 1
xwidth = width // l
xwidth = chartWidth // l
xhalfwidth = (xwidth // 2) - 1 # for calculating bar width
if xhalfwidth < 0: xhalfwidth = 0
index = 0
Expand All @@ -33,22 +81,22 @@ def drawBarChart(draw, left=0, top=0, width=480, height=320, theList=[], fieldNa
item = theList[i]
# determine x coordinate for column
px = float(i) / float(l)
x = left + xhalfwidth + int((width-xwidth) * px) + 1
x = chartLeft + xhalfwidth + int((chartWidth-xwidth) * px) + 1
# get current value
if type(item) is dict and fieldName is not None:
curval = int(item[fieldName])
else:
curval = int(item)
# determine y coordinate for column based on value
py = 1.0 if high <= 0 else float(curval-(low*minfloor)) / float(high-(low*minfloor))
y = top + int((1.0 - py) * height) - 1
y = top if y < top else y
py = 1.0 if high <= 0 else float(curval-chartLow) / float(chartHigh-chartLow)
y = chartTop + int((1.0 - py) * chartHeight) - 1
y = chartTop if y < chartTop else y
y = maxy if y > maxy else y
if curval <= low: # gets right most low value to avoid conflict with avg label:
if curval <= low: # gets right most low value to avoid conflict with avg label
lowx = x
lowy = y
lowFound = True
if curval >= high and not highFound:
if curval >= high and not highFound: # earliest high value gets the label
highx = x
highy = y
highFound = True
Expand All @@ -64,35 +112,35 @@ def drawBarChart(draw, left=0, top=0, width=480, height=320, theList=[], fieldNa
manew = v if len(ma) == 0 else sum(ma) / len(ma)
if index > 1:
if high - low > 0:
maoldy = top+((1.0-(float(maold-(low*minfloor))/float(high-(low*minfloor))))*height)//1
manewy = top+((1.0-(float(manew-(low*minfloor))/float(high-(low*minfloor))))*height)//1
maoldy = chartTop+((1.0-(float(maold-chartLow)/float(chartHigh-chartLow)))*chartHeight)//1
manewy = chartTop+((1.0-(float(manew-chartLow)/float(chartHigh-chartLow)))*chartHeight)//1
else:
maoldy = top + (height//2)
maoldy = chartTop + (chartHeight//2)
manewy = maoldy
if manewy < top + movingAverageWidth: manewy = top + movingAverageWidth
if manewy > top + height - movingAverageWidth: manewy = top + height - movingAverageWidth
if manewy < chartTop + movingAverageWidth: manewy = chartTop + movingAverageWidth
if manewy > chartTop + chartHeight - movingAverageWidth: manewy = chartTop + chartHeight - movingAverageWidth
draw.line(xy=[(maoldx,maoldy),(x,manewy)],fill=ImageColor.getrgb(movingAverageColor),width=movingAverageWidth)
maoldx = x
# draw box around chart
draw.rectangle(xy=[(left,top),(left+width-1,top+height-1)],outline=ImageColor.getrgb(borderColor),width=1)
draw.rectangle(xy=[(chartLeft,chartTop),(chartLeft+chartWidth-1,chartTop+chartHeight)],outline=ImageColor.getrgb(borderColor),width=1)
# draw labels
if showLabels:
if highFound and highx > 0 and highy > 0:
if highx - 100 < left and len(chartLabel) > 0: highy = highy + 20
highpos = "tr" if highx + 150 > left + width else "tl"
if highx - 100 < chartLeft and len(chartLabel) > 0: highy = highy + 20
highpos = "tr" if highx + 150 > chartLeft + chartWidth else "tl"
highx = highx + 3 if highpos == "tl" else highx - 3
drawLabel(draw, f"HIGH:{high}", 10, highpos, highx, highy+2)
if lowFound and lowx > 0 and lowy > 0:
lowpos = "br" if lowx + 150 > left + width else "bl"
lowpos = "br" if lowx + 150 > chartLeft + chartWidth else "bl"
lowx = lowx + 3 if lowpos == "bl" else lowx - 3
drawLabel(draw, f"LOW:{low}", 10, lowpos, lowx, lowy-2)
if showAverage:
avgpos = "tl" if avgy + 20 < top+height-1 else "bl"
avgx = left+2
avgpos = "tl" if avgy + 20 < chartTop+chartHeight-1 else "bl"
avgx = chartLeft+2
avgy = avgy - 2 if avgpos == "bl" else avgy + 2
drawLabel(draw, "AVG:" + str(avg), 10, avgpos, avgx, avgy)
if len(chartLabel) > 0:
drawLabel(draw, chartLabel, 12, "tl", left+2, top+2)
drawLabel(draw, chartLabel, 12, "tl", chartLeft+2, chartTop+2)

def drawDotChart(draw, left=0, top=0, width=480, height=320, list=[], fieldName=None,
valueColor="#2f3fc5", valueRadius=3,
Expand Down

0 comments on commit 5937738

Please sign in to comment.