mirror of
https://github.com/almet/notmyidea.git
synced 2025-04-28 11:32:39 +02:00
274 lines
No EOL
31 KiB
HTML
274 lines
No EOL
31 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<title>
|
|
Using pelican to track my worked and volunteer hours - Alexis Métaireau </title>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<link rel="stylesheet"
|
|
href="https://blog.notmyidea.org/theme/css/main.css?v2"
|
|
type="text/css" />
|
|
<link href="https://blog.notmyidea.org/feeds/all.atom.xml"
|
|
type="application/atom+xml"
|
|
rel="alternate"
|
|
title="Alexis Métaireau ATOM Feed" />
|
|
</head>
|
|
<body>
|
|
<div id="content">
|
|
<section id="links">
|
|
<ul>
|
|
<li>
|
|
<a class="main" href="/">Alexis Métaireau</a>
|
|
</li>
|
|
<li>
|
|
<a class=""
|
|
href="https://blog.notmyidea.org/journal/index.html">Journal</a>
|
|
</li>
|
|
<li>
|
|
<a class="selected"
|
|
href="https://blog.notmyidea.org/code/">Code, etc.</a>
|
|
</li>
|
|
<li>
|
|
<a class=""
|
|
href="https://blog.notmyidea.org/weeknotes/">Notes hebdo</a>
|
|
</li>
|
|
<li>
|
|
<a class=""
|
|
href="https://blog.notmyidea.org/lectures/">Lectures</a>
|
|
</li>
|
|
<li>
|
|
<a class=""
|
|
href="https://blog.notmyidea.org/projets.html">Projets</a>
|
|
</li>
|
|
</ul>
|
|
</section>
|
|
<header>
|
|
<h1 class="post-title">Using pelican to track my worked and volunteer hours</h1>
|
|
<p>
|
|
<em>Graphs, progress-bars and python-markdown extensions</em>
|
|
</p>
|
|
<time datetime="2023-11-23T00:00:00+01:00">23 novembre 2023</time>
|
|
</header>
|
|
<article>
|
|
<p>I was tracking my hours in Datasette (<a href="https://blog.notmyidea.org/using-datasette-for-tracking-my-professional-activity.html">article</a> and <a href="https://blog.notmyidea.org/deploying-and-customizing-datasette.html">follow-up</a>), but I wasn’t really happy with the editing process.</p>
|
|
<p>I’ve seen <a href="https://larlet.fr/david">David</a> notes, which made me want to do something similar.</p>
|
|
<p>I’m consigning everything in markdown files and as such, was already keeping track of everything this way already. Tracking my hours should be simple otherwise I might just oversee it. So I hacked something together with <a href="https://github.com/getpelican/pelican">pelican</a> (the software I wrote for this blog).</p>
|
|
<p><img alt="A graph showing the worked hours and volunteer hours" src="/images/pelican/worklog.png"></p>
|
|
<p>It’s doing the following:</p>
|
|
<ol>
|
|
<li>Defines a specific format for my worklog entries</li>
|
|
<li>Parses them (using a regexp), does some computation and ;</li>
|
|
<li>Uses a specific template to display a graph and progress bar.</li>
|
|
</ol>
|
|
<h2 id="reading-information-from-the-titles">Reading information from the titles</h2>
|
|
<p>I actually took the format I’ve been already using in my log, and enhanced it a bit.
|
|
Basically, the files look likes this (I’m writing in french):</p>
|
|
<div class="highlight"><pre><span></span><code>---
|
|
title: My project
|
|
<span class="gu">total_days: 25</span>
|
|
<span class="gu">---</span>
|
|
|
|
<span class="gu">## Mardi 23 Novembre 2023 (9h, 5/5)</span>
|
|
|
|
What I did this day.
|
|
I can include [<span class="nt">links</span>](<span class="na">https://domain.tld</span>) and whatever I want.
|
|
It won't be processed.
|
|
|
|
<span class="gu">## Lundi 22 Novembre 2023 (8h rémunérées, 2h bénévoles, 4/5)</span>
|
|
|
|
Something else.
|
|
</code></pre></div>
|
|
|
|
<p>Basically, the second titles (h2) are parsed, and should have the following structure:
|
|
<code>{day_of_week} {day} {month} {year} ({worked_hours}(, optional {volunteer_hours}), {fun_rank})</code></p>
|
|
<p>The goal here is to retrieve all of this, so I asked ChatGPT for a regexp and iterated on the result which got me:</p>
|
|
<div class="highlight"><pre><span></span><code><span class="n">pattern</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span>
|
|
<span class="w"> </span><span class="sa">r</span><span class="sd">"""</span>
|
|
<span class="sd"> (\w+)\s+ # Day name</span>
|
|
<span class="sd"> (\d{1,2})\s+ # Day number</span>
|
|
<span class="sd"> ([\wéû]+)\s+ # Month name</span>
|
|
<span class="sd"> (\d{4})\s+ # Year</span>
|
|
<span class="sd"> \(</span>
|
|
<span class="sd"> (\d{1,2})h # Hours (mandatory)</span>
|
|
<span class="sd"> (?:\s+facturées)? # Optionally 'facturées', if not present, assume hours are 'facturées'</span>
|
|
<span class="sd"> (?:,\s*(\d{1,2})h\s*bénévoles)? # Optionally 'volunteer hours 'bénévoles'</span>
|
|
<span class="sd"> ,? # An optional comma</span>
|
|
<span class="sd"> \s* # Optional whitespace</span>
|
|
<span class="sd"> (?:fun\s+)? # Optionally 'fun' (text) followed by whitespace</span>
|
|
<span class="sd"> (\d)/5 # Happiness rating (mandatory, always present)</span>
|
|
<span class="sd"> \) # Closing parenthesis</span>
|
|
<span class="sd"> """</span><span class="p">,</span>
|
|
<span class="n">re</span><span class="o">.</span><span class="n">VERBOSE</span> <span class="o">|</span> <span class="n">re</span><span class="o">.</span><span class="n">UNICODE</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
</code></pre></div>
|
|
|
|
<h2 id="the-markdown-preprocessor">The markdown preprocessor</h2>
|
|
<p>I’m already using a custom pelican plugin, which makes it possible to have pelican behave exactly the way I want. For instance, it’s getting the date from the filesystem.</p>
|
|
<p>I just had to add some features to it. The way I’m doing this is by <a href="https://docs.getpelican.com/en/3.6.2/plugins.html#how-to-create-a-new-reader">using a custom Markdown reader</a>, on which I add extensions and custom processors.</p>
|
|
<p>In my case, I added a preprocessor which will only run when we are handling the worklog. It makes it possible to change what’s being read, before the markdown lib actually transforms it to <span class="caps">HTML</span>.</p>
|
|
<p>Here is the code for it:</p>
|
|
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">WorklogPreprocessor</span><span class="p">(</span><span class="n">Preprocessor</span><span class="p">):</span>
|
|
<span class="n">pattern</span> <span class="o">=</span> <span class="s2">"the regexp we've seen earlier"</span>
|
|
|
|
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">lines</span><span class="p">):</span>
|
|
<span class="n">new_lines</span> <span class="o">=</span> <span class="p">[]</span>
|
|
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">:</span>
|
|
<span class="k">if</span> <span class="n">line</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">"##"</span><span class="p">):</span>
|
|
<span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">pattern</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
|
|
<span class="k">if</span> <span class="ow">not</span> <span class="n">match</span><span class="p">:</span>
|
|
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">"Unable to parse worklog title"</span><span class="p">,</span> <span class="n">line</span><span class="p">)</span>
|
|
<span class="p">(</span>
|
|
<span class="n">day_of_week</span><span class="p">,</span>
|
|
<span class="n">day</span><span class="p">,</span>
|
|
<span class="n">month</span><span class="p">,</span>
|
|
<span class="n">year</span><span class="p">,</span>
|
|
<span class="n">payed_hours</span><span class="p">,</span>
|
|
<span class="n">volunteer_hours</span><span class="p">,</span>
|
|
<span class="n">happiness</span><span class="p">,</span>
|
|
<span class="p">)</span> <span class="o">=</span> <span class="n">match</span><span class="o">.</span><span class="n">groups</span><span class="p">()</span>
|
|
|
|
<span class="n">volunteer_hours</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">volunteer_hours</span><span class="p">)</span> <span class="k">if</span> <span class="n">volunteer_hours</span> <span class="k">else</span> <span class="mi">0</span>
|
|
<span class="n">payed_hours</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">payed_hours</span><span class="p">)</span>
|
|
<span class="n">happiness</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">happiness</span><span class="p">)</span>
|
|
|
|
<span class="n">date</span> <span class="o">=</span> <span class="n">datetime</span><span class="o">.</span><span class="n">strptime</span><span class="p">(</span><span class="sa">f</span><span class="s2">"</span><span class="si">{</span><span class="n">day</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">month</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">year</span><span class="si">}</span><span class="s2">"</span><span class="p">,</span> <span class="s2">"</span><span class="si">%d</span><span class="s2"> %B %Y"</span><span class="p">)</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">[</span><span class="n">date</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s2">"%Y-%m-</span><span class="si">%d</span><span class="s2">"</span><span class="p">)]</span> <span class="o">=</span> <span class="p">{</span>
|
|
<span class="s2">"payed_hours"</span><span class="p">:</span> <span class="n">payed_hours</span><span class="p">,</span>
|
|
<span class="s2">"volunteer_hours"</span><span class="p">:</span> <span class="n">volunteer_hours</span><span class="p">,</span>
|
|
<span class="s2">"happyness"</span><span class="p">:</span> <span class="n">happiness</span><span class="p">,</span>
|
|
<span class="p">}</span>
|
|
|
|
<span class="c1"># Replace the line with just the date</span>
|
|
<span class="n">new_lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="sa">f</span><span class="s2">"## 🗓️ </span><span class="si">{</span><span class="n">day_of_week</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">day</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">month</span><span class="si">}</span><span class="s2"> </span><span class="si">{</span><span class="n">year</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
|
|
<span class="k">else</span><span class="p">:</span>
|
|
<span class="n">new_lines</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
|
|
<span class="k">return</span> <span class="n">new_lines</span>
|
|
</code></pre></div>
|
|
|
|
<p>It does the following when it encounters a h2 line:</p>
|
|
<ul>
|
|
<li>try to parse it</li>
|
|
<li>store the data locally</li>
|
|
<li>replace the line with a simpler version</li>
|
|
<li>If if doesn’t work, error out.</li>
|
|
</ul>
|
|
<p>I’ve also added some computations on top of it, which makes it possible to display a percentage of completion for the project, if “payed_hours” was present in the metadata, and makes it use a specific template (see later).</p>
|
|
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">compute_data</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">metadata</span><span class="p">):</span>
|
|
<span class="n">done_hours</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">([</span><span class="n">item</span><span class="p">[</span><span class="s2">"payed_hours"</span><span class="p">]</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="o">.</span><span class="n">values</span><span class="p">()])</span>
|
|
|
|
<span class="n">data</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span>
|
|
<span class="n">data</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">,</span>
|
|
<span class="n">done_hours</span><span class="o">=</span><span class="n">done_hours</span><span class="p">,</span>
|
|
<span class="n">template</span><span class="o">=</span><span class="s2">"worklog"</span><span class="p">,</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="s2">"total_days"</span> <span class="ow">in</span> <span class="n">metadata</span><span class="p">:</span>
|
|
<span class="n">total_hours</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">metadata</span><span class="p">[</span><span class="s2">"total_days"</span><span class="p">])</span> <span class="o">*</span> <span class="mi">7</span>
|
|
<span class="n">data</span><span class="o">.</span><span class="n">update</span><span class="p">(</span>
|
|
<span class="nb">dict</span><span class="p">(</span>
|
|
<span class="n">total_hours</span><span class="o">=</span><span class="n">total_hours</span><span class="p">,</span>
|
|
<span class="n">percentage</span><span class="o">=</span><span class="nb">round</span><span class="p">(</span><span class="n">done_hours</span> <span class="o">/</span> <span class="n">total_hours</span> <span class="o">*</span> <span class="mi">100</span><span class="p">),</span>
|
|
<span class="p">)</span>
|
|
<span class="p">)</span>
|
|
|
|
<span class="k">return</span> <span class="n">data</span>
|
|
</code></pre></div>
|
|
|
|
<h2 id="plugging-this-with-pelican">Plugging this with pelican</h2>
|
|
<p>Here’s the code for extending a custom reader, basically adding a pre-processor and adding back its data in the document metadata:</p>
|
|
<div class="highlight"><pre><span></span><code><span class="n">is_worklog</span> <span class="o">=</span> <span class="n">Path</span><span class="p">(</span><span class="n">source_path</span><span class="p">)</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">match</span><span class="p">(</span><span class="s2">"pages/worklog"</span><span class="p">)</span>
|
|
|
|
<span class="k">if</span> <span class="n">is_worklog</span><span class="p">:</span>
|
|
<span class="n">worklog</span> <span class="o">=</span> <span class="n">WorklogPreprocessor</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">_md</span><span class="p">)</span>
|
|
<span class="bp">self</span><span class="o">.</span><span class="n">_md</span><span class="o">.</span><span class="n">preprocessors</span><span class="o">.</span><span class="n">register</span><span class="p">(</span><span class="n">worklog</span><span class="p">,</span> <span class="s2">"worklog"</span><span class="p">,</span> <span class="mi">20</span><span class="p">)</span>
|
|
|
|
<span class="c1"># process the markdown, and then</span>
|
|
|
|
<span class="k">if</span> <span class="n">is_worklog</span><span class="p">:</span>
|
|
<span class="n">metadata</span><span class="p">[</span><span class="s2">"worklog"</span><span class="p">]</span> <span class="o">=</span> <span class="n">worklog</span><span class="o">.</span><span class="n">compute_data</span><span class="p">(</span><span class="n">metadata</span><span class="p">)</span>
|
|
</code></pre></div>
|
|
|
|
<h2 id="adding-a-graph">Adding a graph</h2>
|
|
<p>Okay, everything is parsed, but it’s not yet displayed on the pages. I’m using <a href="https://vega.github.io/vega-lite/docs/">vega-lite</a> to display a graph.</p>
|
|
<p>Here is my template for this (stored in <code>template/worklog.html</code>), it’s doing a stacked bar chart with my data.</p>
|
|
<div class="highlight"><pre><span></span><code><span class="kd">const</span><span class="w"> </span><span class="nx">spec</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span>
|
|
<span class="w"> </span><span class="s2">"$schema"</span><span class="o">:</span><span class="w"> </span><span class="s2">"https://vega.github.io/schema/vega-lite/v5.json"</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="s2">"width"</span><span class="o">:</span><span class="w"> </span><span class="mf">500</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="s2">"height"</span><span class="o">:</span><span class="w"> </span><span class="mf">200</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="s2">"data"</span><span class="o">:</span><span class="w"> </span>
|
|
<span class="w"> </span><span class="p">{</span>
|
|
<span class="w"> </span><span class="s2">"name"</span><span class="o">:</span><span class="w"> </span><span class="s2">"table"</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="s2">"values"</span><span class="o">:</span><span class="w"> </span><span class="p">[</span>
|
|
<span class="w"> </span><span class="p">{</span><span class="o">%</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="nx">date</span><span class="p">,</span><span class="w"> </span><span class="nx">item</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="nx">page</span><span class="p">.</span><span class="nx">metadata</span><span class="p">.</span><span class="nx">worklog</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">items</span><span class="p">()</span><span class="w"> </span><span class="o">%</span><span class="p">}</span>
|
|
<span class="w"> </span><span class="p">{</span><span class="s2">"date"</span><span class="o">:</span><span class="w"> </span><span class="s2">"{{ date }}"</span><span class="p">,</span><span class="w"> </span><span class="s2">"series"</span><span class="o">:</span><span class="w"> </span><span class="s2">"Rémunéré"</span><span class="p">,</span><span class="w"> </span><span class="s2">"count"</span><span class="o">:</span><span class="w"> </span><span class="p">{{</span><span class="w"> </span><span class="nx">item</span><span class="p">[</span><span class="s1">'payed_hours'</span><span class="p">]</span><span class="w"> </span><span class="p">}}},</span>
|
|
<span class="w"> </span><span class="p">{</span><span class="s2">"date"</span><span class="o">:</span><span class="w"> </span><span class="s2">"{{ date }}"</span><span class="p">,</span><span class="w"> </span><span class="s2">"series"</span><span class="o">:</span><span class="w"> </span><span class="s2">"Bénévole"</span><span class="p">,</span><span class="w"> </span><span class="s2">"count"</span><span class="o">:</span><span class="w"> </span><span class="p">{{</span><span class="w"> </span><span class="nx">item</span><span class="p">[</span><span class="s1">'volunteer_hours'</span><span class="p">]</span><span class="w"> </span><span class="p">}}},</span>
|
|
<span class="w"> </span><span class="p">{</span><span class="o">%</span><span class="w"> </span><span class="nx">endfor</span><span class="w"> </span><span class="o">%</span><span class="p">}</span>
|
|
<span class="w"> </span><span class="p">]</span>
|
|
<span class="w"> </span><span class="p">}</span>
|
|
<span class="w"> </span><span class="p">,</span>
|
|
<span class="w"> </span><span class="s2">"mark"</span><span class="o">:</span><span class="w"> </span><span class="s2">"bar"</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="s2">"encoding"</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
|
<span class="w"> </span><span class="s2">"x"</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
|
<span class="w"> </span><span class="s2">"timeUnit"</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="s2">"unit"</span><span class="o">:</span><span class="w"> </span><span class="s2">"dayofyear"</span><span class="p">,</span><span class="w"> </span><span class="s2">"step"</span><span class="o">:</span><span class="w"> </span><span class="mf">1</span><span class="p">},</span>
|
|
<span class="w"> </span><span class="s2">"field"</span><span class="o">:</span><span class="w"> </span><span class="s2">"date"</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="s2">"axis"</span><span class="o">:</span><span class="w"> </span><span class="p">{</span><span class="s2">"format"</span><span class="o">:</span><span class="w"> </span><span class="s2">"%d/%m"</span><span class="p">},</span>
|
|
<span class="w"> </span><span class="s2">"title"</span><span class="o">:</span><span class="w"> </span><span class="s2">"Date"</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="s2">"step"</span><span class="o">:</span><span class="w"> </span><span class="mf">1</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="p">},</span>
|
|
<span class="w"> </span><span class="s2">"y"</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
|
<span class="w"> </span><span class="s2">"aggregate"</span><span class="o">:</span><span class="w"> </span><span class="s2">"sum"</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="s2">"field"</span><span class="o">:</span><span class="w"> </span><span class="s2">"count"</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="s2">"title"</span><span class="o">:</span><span class="w"> </span><span class="s2">"Heures"</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="p">},</span>
|
|
<span class="w"> </span><span class="s2">"color"</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
|
<span class="w"> </span><span class="s2">"field"</span><span class="o">:</span><span class="w"> </span><span class="s2">"series"</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="s2">"scale"</span><span class="o">:</span><span class="w"> </span><span class="p">{</span>
|
|
<span class="w"> </span><span class="s2">"domain"</span><span class="o">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"Bénévole"</span><span class="p">,</span><span class="w"> </span><span class="s2">"Rémunéré"</span><span class="p">],</span>
|
|
<span class="w"> </span><span class="s2">"range"</span><span class="o">:</span><span class="w"> </span><span class="p">[</span><span class="s2">"#e7ba52"</span><span class="p">,</span><span class="w"> </span><span class="s2">"#1f77b4"</span><span class="p">]</span>
|
|
<span class="w"> </span><span class="p">},</span>
|
|
<span class="w"> </span><span class="s2">"title"</span><span class="o">:</span><span class="w"> </span><span class="s2">"Type d'heures"</span>
|
|
<span class="w"> </span><span class="p">}</span>
|
|
<span class="w"> </span><span class="p">}</span>
|
|
<span class="w"> </span><span class="p">};</span>
|
|
|
|
<span class="w"> </span><span class="nx">vegaEmbed</span><span class="p">(</span><span class="s2">"#vis"</span><span class="p">,</span><span class="w"> </span><span class="nx">spec</span><span class="p">)</span>
|
|
<span class="w"> </span><span class="c1">// result.view provides access to the Vega View API</span>
|
|
<span class="w"> </span><span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">result</span><span class="w"> </span><span class="p">=></span><span class="w"> </span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">result</span><span class="p">))</span>
|
|
<span class="w"> </span><span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">console</span><span class="p">.</span><span class="nx">warn</span><span class="p">);</span>
|
|
</code></pre></div>
|
|
|
|
<p>I’ve also added a small progress bar, made with unicode, which looks like this.</p>
|
|
<div class="highlight"><pre><span></span><code>▓▓░░░░░░░░ 29% (51h / 175 prévues)
|
|
</code></pre></div>
|
|
|
|
<p>Here is the code for it:</p>
|
|
<div class="highlight"><pre><span></span><code><span class="cp">{%</span> <span class="k">if</span> <span class="s2">"total_days"</span> <span class="k">in</span> <span class="nv">page.metadata.keys</span><span class="o">()</span> <span class="cp">%}</span>
|
|
<span class="w"> </span><span class="cp">{%</span> <span class="k">set</span> <span class="nv">percentage</span> <span class="o">=</span> <span class="nv">page.metadata.worklog</span><span class="o">[</span><span class="s1">'percentage'</span><span class="o">]</span> <span class="cp">%}</span>
|
|
<span class="w"> </span><span class="cp">{%</span> <span class="k">set</span> <span class="nv">total_blocks</span> <span class="o">=</span> <span class="m">10</span> <span class="cp">%}</span>
|
|
<span class="w"> </span><span class="cp">{%</span> <span class="k">set</span> <span class="nv">percentage_value</span> <span class="o">=</span> <span class="o">(</span><span class="nv">percentage</span> <span class="o">/</span> <span class="m">100.0</span><span class="o">)</span> <span class="cp">%}</span>
|
|
<span class="w"> </span><span class="cp">{%</span> <span class="k">set</span> <span class="nv">full_blocks</span> <span class="o">=</span> <span class="o">((</span><span class="nv">percentage_value</span> <span class="o">*</span> <span class="nv">total_blocks</span><span class="o">)</span> <span class="o">|</span> <span class="nf">round</span><span class="o">(</span><span class="m">0</span><span class="o">,</span> <span class="s1">'floor'</span><span class="o">)</span> <span class="o">)</span> <span class="o">|</span> <span class="nf">int</span> <span class="cp">%}</span>
|
|
<span class="w"> </span><span class="cp">{%</span> <span class="k">set</span> <span class="nv">empty_blocks</span> <span class="o">=</span> <span class="nv">total_blocks</span> <span class="o">-</span> <span class="nv">full_blocks</span> <span class="cp">%}</span>
|
|
<span class="w"> </span><span class="nt"><div</span><span class="w"> </span><span class="na">class=</span><span class="s">"progressbar"</span><span class="nt">></span>
|
|
<span class="w"> </span><span class="c">{# Display full blocks #}</span>
|
|
<span class="w"> </span><span class="cp">{%</span> <span class="k">for</span> <span class="nv">i</span> <span class="k">in</span> <span class="nv">range</span><span class="o">(</span><span class="nv">full_blocks</span><span class="o">)</span> <span class="cp">%}</span>▓<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
|
|
<span class="w"> </span><span class="c">{# Display empty blocks #}</span>
|
|
<span class="w"> </span><span class="cp">{%</span> <span class="k">for</span> <span class="nv">i</span> <span class="k">in</span> <span class="nv">range</span><span class="o">(</span><span class="nv">empty_blocks</span><span class="o">)</span> <span class="cp">%}</span>░<span class="cp">{%</span> <span class="k">endfor</span> <span class="cp">%}</span>
|
|
<span class="w"> </span><span class="cp">{{</span> <span class="nv">percentage</span> <span class="cp">}}</span>%<span class="w"> </span>(<span class="cp">{{</span> <span class="nv">page.metadata.worklog</span><span class="o">[</span><span class="s1">'done_hours'</span><span class="o">]</span> <span class="cp">}}</span>h<span class="w"> </span>/<span class="w"> </span><span class="cp">{{</span> <span class="nv">page.metadata.worklog</span><span class="o">[</span><span class="s1">'total_hours'</span><span class="o">]</span> <span class="cp">}}</span><span class="w"> </span>prévues)
|
|
<span class="w"> </span><span class="nt"></div></span>
|
|
</code></pre></div>
|
|
<p>
|
|
<a href="https://blog.notmyidea.org/tag/pelican.html">#Pelican</a>
|
|
, <a href="https://blog.notmyidea.org/tag/work.html">#Work</a>
|
|
, <a href="https://blog.notmyidea.org/tag/vega.html">#Vega</a>
|
|
, <a href="https://blog.notmyidea.org/tag/markdown.html">#Markdown</a>
|
|
- Posté dans la catégorie <a href="https://blog.notmyidea.org/code/">code</a>
|
|
</p>
|
|
</article>
|
|
<footer>
|
|
<a id="feed" href="/feeds/all.atom.xml">
|
|
<img alt="RSS Logo" src="/theme/rss.svg" />
|
|
</a>
|
|
</footer>
|
|
</div>
|
|
</body>
|
|
</html> |