= load_sample_events()
evts = [first(evts, risinstance(o)) for o in described_evts] exs
Extensions To rich
Animated Stats
This section outlines how we can display statistics and visualizations such as sparklines and status bars that are animated as events are received.
EProg
EProg (hdg='Quota', width=10)
Progress bar with a heading hdg
.
When you instantiate Eprog
the starting progress is set to 0%:
= EProg()
p print(p) console.
Quota ━━━━━━━ 0%
You can update the progress bar with the update
method:
10)
p.update(print(p) console.
Quota ╸━━━━━━ 10%
Espark
- A sparkline combined with an EventTimer
fastcore’s EventTimer
calculates frequency metrics aggregated by slices of time specified by the argument span
. The EventTimer
can produce a sparkline that shows the last n time slices, where n is specified by the parameter store
:
ESpark
ESpark (nm:str, color:str, ghevts=None, store=5, span=0.2, mn=0, mx=None, stacked=True, show_freq=False)
An EventTimer
that displays a sparkline with a heading nm
.
from time import sleep
def _randwait(): yield from (sleep(random.random()/200) for _ in range(100))
= EventTimer(store=5, span=0.03)
c for o in _randwait(): c.add(1)
By default nm
will be stacked on top of the sparkline. We simulate adding events to ESpark
and render the result:
= ESpark(nm='💌Issue', color='blue', store=5)
e
def _r(): return random.randint(1,30)
def _sim(e, steps=8, sleep=.2):
for i in range(steps):
e.add(_r())
time.sleep(sleep)
_sim(e)print(e) console.
💌Issue 27 ▃▃▁▅▇
If you would prefer nm
and the sparkline to be on one line instead, you can set stacked
to false
:
= ESpark(color='blue', nm='💌Issue', stacked=False)
e
_sim(e)print(e) console.
💌Issue 59 ▇▇▂▅▂
You can optionally specify a list of GhEvent
types that will allow you to update sparklines by streaming in events. described_evts
has a complete list of options:
described_evts
(ghapi.event.PushEvent,
ghapi.event.CreateEvent,
ghapi.event.IssueCommentEvent,
ghapi.event.WatchEvent,
ghapi.event.PullRequestEvent,
ghapi.event.PullRequestReviewEvent,
ghapi.event.PullRequestReviewCommentEvent,
ghapi.event.DeleteEvent,
ghapi.event.ForkEvent,
ghapi.event.IssuesEvent,
ghapi.event.ReleaseEvent,
ghapi.event.MemberEvent,
ghapi.event.CommitCommentEvent,
ghapi.event.GollumEvent,
ghapi.event.PublicEvent)
If ghevts
is specified, only events that match the list of the GhEvent
types will increment the event counter.
In the below example, the IssueCommentEvent
and IssuesEvent
are listed, therefore any other event types will not update the event counter:
= evts.filter(risinstance((PullRequestEvent, PullRequestReviewCommentEvent, PullRequestReviewEvent)))
_pr_evts = evts.filter(risinstance((WatchEvent)))
_watch_evts
= ESpark('Issues', 'blue', [IssueCommentEvent, IssuesEvent], span=5)
_s
_s.add_events(_pr_evts)
_s.add_events(_watch_evts)0) test_eq(_s.events,
However, events that match those types will update the event counter accordingly:
= evts.filter(risinstance((IssueCommentEvent, IssuesEvent)))
_issue_evts
_s.add_events(_issue_evts)len(_issue_evts)) test_eq(_s.events,
If ghevts
is not specified, all events are counted:
= ESpark('Issues', 'blue', span=5)
_s
_s.add_events(evts)len(evts)) test_eq(_s.events,
You can also just add one event at a time instead of a list of events:
= ESpark('Issues', 'blue', span=5)
_s 0])
_s.add_events(evts[1) test_eq(_s.events,
Update A Group of Sparklines with SpkMap
SpkMap
SpkMap (spks:List[__main__.ESpark])
A Group of ESpark
instances.
You can define a SpkMap
instance with a list of ESpark
:
= ESpark('Issues', 'green', [IssueCommentEvent, IssuesEvent], span=60)
s1 = ESpark('PR', 'red', [PullRequestEvent, PullRequestReviewCommentEvent, PullRequestReviewEvent], span=60)
s2 = ESpark('Follow', 'blue', [WatchEvent, StarEvent, IssueCommentEvent, IssuesEvent], span=60)
s3 = ESpark('Other', 'red', span=60)
s4
= SpkMap([s1,s2,s3,s4]) sm
We haven’t added any events to SpkMap
so the event count will be zero for all sparklines:
sm.evcounts
{'Issues': 0, 'PR': 0, 'Follow': 0, 'Other': 0}
In the above example, Issue events update both the Issues
and Follow
sparklines, as well as the Other
sparkline which doesn’t have any GhEvent
type filters so it counts all events:
sm.add_events(_issue_evts)'Issues'], len(_issue_evts))
test_eq(sm.evcounts['Follow'], len(_issue_evts))
test_eq(sm.evcounts['Other'], len(_issue_evts))
test_eq(sm.evcounts[
sm.evcounts
{'Issues': 80, 'PR': 0, 'Follow': 80, 'Other': 80}
You can also just add one event at a time:
0])
sm.add_events(_pr_evts['PR'], 1)
test_eq(sm.evcounts['Other'], len(_issue_evts)+1) test_eq(sm.evcounts[
It may be desirable to make certain attributes of the sparklines the same so the group can look consistent. For example, by default sparklines are set to stacked=True
, which means the labels are on top:
print(sm) console.
Issues PR Follow Other 4339 52 4016 3923
We can update stack=False
for the entire group with the update_params
method:
=False)
sm.update_params(stackedprint(sm) console.
Issues 0.0 PR 0.0 Follow 0.0 Other 0.0
=True, span=.1, store=8)
sm.update_params(stackeddef _sim(s):
with Live(s) as live:
for i in range(200):
0,500)])
s.add_events(evts[:random.randint(0,10)/100)
time.sleep(random.randint( _sim(sm)
print(sm.spks[0]) console.
Issues 128 ▂▁▅▅▁▇▁▁
Stats - Sparklines, Progress bars and Counts Combined
We may want to combine sparklines (with ESpark
), spinners, and progress bars (with EProg
) to display organized information concerning an event stream. Stats
helps you create, group, display and update these elements together.
Stats
Stats (spks:List[__main__.ESpark], store=None, span=None, stacked=None, show_freq=None, max_width=75, spin:str='earth', spn_lbl='/min')
Renders a group of ESpark
along with a spinner and progress bar that are dynamically sized.
Instantiate Stats
with a list of Espark
instances. The parameters: store
, span
, and stacked
allow you to set or override properties of underlying sparklines for consistency.
= ESpark('Issues', 'green', [IssueCommentEvent, IssuesEvent])
s1 = ESpark('PR', 'red', [PullRequestEvent, PullRequestReviewCommentEvent, PullRequestReviewEvent])
s2 = ESpark('Follow', 'blue', [WatchEvent, StarEvent])
s3 = ESpark('Other', 'red')
s4
= Stats([s1,s2,s3,s4], store=5, span=.1, stacked=True)
s print(s) console.
🌍 Issues PR Follow Other Quota /min 0.0 0.0 0.0 0.0 ━━━━━━━ 0%
You can add events to update counters and sparklines just like SpkMap
:
s.add_events(evts)print(s) console.
🌍 Issues PR Follow Other Quota /min 2614 3254 1061 30513 ━━━━━━━ 0%
You can update the progress bar with the update_prog
method:
50)
s.update_prog(print(s) console.
🌍 Issues PR Follow Other Quota /min 1847 2316 760 21981 ━━━╸━━━ 50%
Here is what this looks like when animated using Live
:
def _sim_spark(s):
with Live(s) as live:
for i in range(101):
s.update_prog(i)0,500)])
s.add_events(evts[:random.randint(0,10)/100)
time.sleep(random.randint(
=1, show_freq=True)
s.update_params(span _sim_spark(s)
Event Panel
Display GitHub events in a FixedPanel
, which is a frame of fixed height that displays streaming data.
= FixedPanel(15, box=box.HORIZONTALS, title='ghtop')
p for e in evts[:163]: p.append(e)
p
────────────────────────────────────────── ghtop ────────────────────────────────────────── ⭐ diddledan pushed 1 commits to "master" in diddlesnaps/openttd ⭐ veigarbot pushed 1 commits to "main" in veigarbot/veigarbot.github.io ⭐ FromDarkHell pushed 1 commits to "main" in FromDarkHell/Simulstream 🏭 pg45 created branch "add-headers" in pg45/markdown-portfolio 💬 ljwagerfield created comment on issue #743 in lukeautry/tsoa: "@rudfoss did you find… ⭐ KwameTaylor pushed 1 commits to "main" in SpotiScryers/SpotiScry 👀 okeeffdp started watching alshedivat/al-folio 📬 JlchavezG opened PR #43 on JlchavezG/Psbg: "Se agregan notificaciones" 👀 Lit3r4lly started watching 0xgalz/Virtuailor 🏭 Nabil1907 created repository in Nabil1907/Node-js-Projects ⭐ NotWhoYoureThinkingOf pushed 1 commits to "main" in NotWhoYoureThinkingOf/fb-clone 🏭 thoriqkemal created repository in thoriqkemal/tig: "tig tig" ⭐ WrathfulSpatula pushed 1 commits to "master" in WrathfulSpatula/OpenRelativity ⭐ alinapopaqb pushed 1 commits to "master" in alinkamalvinka/cdond-c3-projectstarter ⭐ LombiqBot pushed 0 commits to "MatteoPiovanelli…Lombiq/Orchard ───────────────────────────────────────────────────────────────────────────────────────────
Using grid
with FixedPanel
We can use grid
to arrange multiple FixedPanel
instances in rows and columns. Below is an example of how two FixedPanel
instances can be arranged in a row:
= FixedPanel(15, box=box.HORIZONTALS, title='ghtop')
p for e in exs: p.append(e)
grid([[p,p]])
─────────────────── ghtop ─────────────────── ────────────────── ghtop ─────────────────── ⭐ BeckhamL pushed 1 commi…BeckhamL/leetc… ⭐ BeckhamL pushed 1 commi…BeckhamL/leet… 🏭 admmonito…created branch…admmon…"This … 🏭 admmonito…created branc…admmon…"This … 💬 Holzhaus created commen…mixxxdj…"I reo… 💬 Holzhaus created commen…mixxxd…"I reo… 👀 mikalacki…started watchi…microg/GmsCore 👀 mikalacki…started watch…microg/GmsCore 📪 Didier-D-…closed PR #3 o…Didier…"Bump … 📪 Didier-D-…closed PR #3 …Didier…"Bump … 💌 kadirselc…created PR rev…turkdevops/no… 💌 kadirselc…created PR re…turkdevops/no… 🗨 mobinmob created review …void-li…"Οκ 👍" 🗨 mobinmob created review…void-li…"Οκ 👍" ✂ heehee3 deleted branch …heehee3/Proyect… ✂ heehee3 deleted branch …heehee3/Proyec… 🍽 Tubbz-alt forked fortran-lan…"Fortran w… 🍽 Tubbz-alt forked fortran-la…"Fortran w… 🐛 oulasvirt…opened issue #…oulasv…"Title" 🐛 oulasvirt…opened issue …oulasv…"Title" 🚀 github-ac…published rele…Anuken/Mindus… 🚀 github-ac…published rel…Anuken/Mindus… 💃 laurofilh…added member b…laurofilho96/… 💃 laurofilh…added member …laurofilho96/… 🎉 vercel[bo…created commit…LCinde…"Succe… 🎉 vercel[bo…created commi…LCinde…"Succe… 📚 adrianggc edited wiki pa…adrianggc/Die… 📚 adrianggc edited wiki p…adrianggc/Die… ───────────────────────────────────────────── ────────────────────────────────────────────
Here is another example of a four FixedPanel
instances arranged in two rows and two columns:
= IssueCommentEvent,IssuesEvent,PullRequestEvent,PullRequestReviewEvent
types = {o:FixedPanel(15, box=box.HORIZONTALS, title=camel2words(remove_suffix(o.__name__,'Event'))) for o in types} ps
for k,v in ps.items(): v.extend(evts.filter(risinstance(k)))
= ps.values()
isc,iss,prs,prrs =110) grid([[isc,iss],[prs,prrs]], width
─────────────────── Issue Comment ─────────────────── ────────────────────── Issues ─────────────────────── 💬 sanskritbsc…created comment o…sanskrit…"fixed … 🐛 github-lear…opened issue #1 o…pg45/mar…"Gettin… 💬 github-lear…created comment o…pfxsys/g…"## Ste… 🐛 NillerMedDi…opened issue #3 o…gigabit1…"[Reque… 💬 moezzineb created comment on…flutter/…"Voila :… 🐛 lucasfarias…opened issue #2 o…lucasfar…"Issue … 💬 JustSlone created comment on…microsof…"So read… 🐛 jabolopes opened issue #44 o…lafriks/…"Possibl… 💬 codecov[bot…created comment o…open-mml…"# [Cod… 🐛 aisaioop opened issue #1598…aisaioop/…"北京通州 💬 mightybart created comment o…floriank…"Current… 🐛 jonathannag…opened issue #23 …navikt/m…"Access… 💬 Joulinar created comment on…MichaIng/…"Hi, … 🐛 aisaioop opened issue #1599…aisaioop/…"上海黄浦 💬 9mm created comment on is…rubycdp/fe…"@route h… 🐛 helmrich opened issue #27 o…vinceliui…"Spotify… 💬 dependabot[…created comment o…herzliya…"Looks … 🐛 github-acti…opened issue #204…Sakzsee/…"Error … 💬 codepope created comment on…superfly/…"That's … 🐛 aisaioop opened issue #1600…aisaioop/…"北京门头 💬 poelzi created comment on …mixxxdj/m…"@Be-ing … 🎁 glutamate closed issue #466 …saltcorn…"Forgott… 💬 ethindp created comment on …rust-osde…"I disag… 🐛 autocode-ap…opened issue #793…imamandr…"https:… 💬 michaelforn…created comment o…oasislin…"Thanks… 🎁 sanskritbsc…closed issue #3 o…sanskrit…"someth… 💬 stale[bot] created comment o…ironhack…"This pu… 🐛 aisaioop opened issue #1601…aisaioop/…"上海宝山 💬 awolf78 created comment on …ImpulseRC…"If you … 🐛 slingamn opened issue #1455…oragono/o…"split m… ───────────────────────────────────────────────────── ───────────────────────────────────────────────────── ─────────────────── Pull Request ──────────────────── ──────────────── Pull Request Review ──────────────── 📪 alexerlands…closed PR #5 on alexerlan…"Bump in… 📬 constancefe…opened PR #85 on lucienwa…"week 5 … 📬 jyshangguan opened PR #1 on jyshanggu…"add ima… 💌 kadirselcuk created PR review…turkdevops/node 📪 Ryukishi closed PR #2 on adibhanna/…"Feature/p… 💌 mobinmob created PR review …void-linux/void-pa… 📪 ahocevar closed PR #11811 o…openlayer…"Better … 💌 swatso2020 created PR review…swatso2020/Projec… 📬 snyk-bot opened PR #84 on stelthdroi…"[Snyk] S… 💌 github-lear…created PR review…theaioat…"## Ste… 📪 mennovanemm…closed PR #27 on Softimis…"Ship Mo… 💌 ASchwad created PR review i…reaviz/reaflow 📪 Chramox closed PR #4 on Chramox/tyt…"Update" 💌 francinaPon…created PR review…UB-ES-2020-A/Gru… 📪 gabriel-hah…closed PR #128 on gabriel-…"Bump @… 💌 aaronPeruga created PR review…UB-ES-2020-A/Gru… 📬 distantnati…opened PR #1103 o…getkirby…"Remove… 💌 ahocevar created PR review …openlayer…"Thanks,… 📬 pull[bot] opened PR #534 on antosubas…"[pull] … 💌 nedbat created PR review i…nedbat/scriv 📪 yakirgot closed PR #1 on yakirgot/s…"chore(dep… 💌 nedbat created PR review i…nedbat/scriv 📪 mergify[bot…closed PR #4 on spbu-codi…"Исправл… 💌 mennovanemm…created PR review…Softimistic/Proj… 🔁 stringcode8…reopened PR #2051…MetaMask…"[1984]… 💌 i-stam created PR review i…Synthetixio/synthet… 📪 dependabot[…closed PR #3 on herzliya-…"Bump in… 💌 gmolinga created PR review …UB-ES-2020-A/Grup-… ───────────────────────────────────────────────────── ─────────────────────────────────────────────────────