mirror of
https://github.com/almet/notmyidea.git
synced 2025-04-28 19:42:37 +02:00
177 lines
No EOL
22 KiB
HTML
177 lines
No EOL
22 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<title>
|
|
Using Datasette for tracking my professional activity - 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 Datasette for tracking my professional activity</h1>
|
|
<time datetime="2023-11-11T00:00:00+01:00">11 novembre 2023</time>
|
|
|
|
|
|
</header>
|
|
<article>
|
|
<p>I’ve been following Simon Willison since quite some time, but I’ve actually never played with his main project <a href="https://datasette.io">Datasette</a> before.</p>
|
|
<p>As I’m going back into development, I’m trying to track where my time goes, to be able to find patterns, and just remember how much time I’ve worked on such and such project. A discussion with <a href="https://thom4.net/">Thomas</a> made me realize it would be nice to track all this in a spreadsheet of some sort, which I was doing until today.</p>
|
|
<p>Spreadsheets are nice, but they don’t play well with rich content, and doing graphs with them is kind of tricky. So I went ahead and setup everything in Datasette.</p>
|
|
<p>First of all, I’ve imported my <code>.csv</code> file into a sqlite database: </p>
|
|
<div class="highlight"><pre><span></span><code>sqlite3<span class="w"> </span>-csv<span class="w"> </span>-header<span class="w"> </span>db.sqlite<span class="w"> </span><span class="s2">".import journal.csv journal"</span>
|
|
</code></pre></div>
|
|
|
|
<p>Then, I used <a href="https://sqlite-utils.datasette.io/en/stable/">sqlite-utils</a> to do some tidying and changed the columns names:</p>
|
|
<div class="highlight"><pre><span></span><code><span class="c1"># Rename a column</span>
|
|
sqlite-utils<span class="w"> </span>transform<span class="w"> </span>journal<span class="w"> </span>--rename<span class="w"> </span><span class="s2">"quoi ?"</span><span class="w"> </span>content
|
|
|
|
<span class="c1"># Make everything look similar</span>
|
|
sqlite-utils<span class="w"> </span>update<span class="w"> </span>db.sqlite<span class="w"> </span>journal<span class="w"> </span>project<span class="w"> </span><span class="s1">'value.replace("Umap", "uMap")'</span>
|
|
</code></pre></div>
|
|
|
|
<p>Here is my database schema:</p>
|
|
<div class="highlight"><pre><span></span><code>sqlite-utils<span class="w"> </span>schema<span class="w"> </span>db.sqlite
|
|
CREATE<span class="w"> </span>TABLE<span class="w"> </span><span class="s2">"journal"</span><span class="w"> </span><span class="o">(</span>
|
|
<span class="w"> </span><span class="o">[</span>date<span class="o">]</span><span class="w"> </span>TEXT,
|
|
<span class="w"> </span><span class="o">[</span>project<span class="o">]</span><span class="w"> </span>TEXT,
|
|
<span class="w"> </span><span class="o">[</span>duration<span class="o">]</span><span class="w"> </span>TEXT,
|
|
<span class="w"> </span><span class="o">[</span>where<span class="o">]</span><span class="w"> </span>TEXT,
|
|
<span class="w"> </span><span class="o">[</span>content<span class="o">]</span><span class="w"> </span>TEXT,
|
|
<span class="w"> </span><span class="o">[</span>paid_work<span class="o">]</span><span class="w"> </span>INTEGER
|
|
<span class="o">)</span><span class="p">;</span>
|
|
</code></pre></div>
|
|
|
|
<p>And then installed datasette, with a few plugins, and ran it:</p>
|
|
<div class="highlight"><pre><span></span><code>pipx<span class="w"> </span>install<span class="w"> </span>datasette
|
|
datasette<span class="w"> </span>install<span class="w"> </span>datasette-render-markdown<span class="w"> </span>datasette-write-ui<span class="w"> </span>datasette-dashboards<span class="w"> </span>datasette-dateutil
|
|
</code></pre></div>
|
|
|
|
<p>I then came up with a few <span class="caps">SQL</span> queries which are useful:</p>
|
|
<p>How much I’ve worked per project:</p>
|
|
<div class="highlight"><pre><span></span><code><span class="n">sqlite</span><span class="o">-</span><span class="n">utils</span><span class="w"> </span><span class="n">db</span><span class="p">.</span><span class="n">sqlite</span><span class="w"> </span><span class="ss">"SELECT project, SUM(CAST(duration AS REAL)) as total_duration FROM journal GROUP BY project;"</span>
|
|
<span class="p">[</span><span class="err">{</span><span class="ss">"project"</span><span class="p">:</span><span class="w"> </span><span class="ss">"Argos"</span><span class="p">,</span><span class="w"> </span><span class="ss">"total_duration"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"project"</span><span class="p">:</span><span class="w"> </span><span class="ss">"IDLV"</span><span class="p">,</span><span class="w"> </span><span class="ss">"total_duration"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"project"</span><span class="p">:</span><span class="w"> </span><span class="ss">"Notmyidea"</span><span class="p">,</span><span class="w"> </span><span class="ss">"total_duration"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"project"</span><span class="p">:</span><span class="w"> </span><span class="ss">"Sam"</span><span class="p">,</span><span class="w"> </span><span class="ss">"total_duration"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"project"</span><span class="p">:</span><span class="w"> </span><span class="ss">"uMap"</span><span class="p">,</span><span class="w"> </span><span class="ss">"total_duration"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">]</span>
|
|
</code></pre></div>
|
|
|
|
<p>How much I’ve worked per week, in total (I’ve redacted the results for privacy):</p>
|
|
<div class="highlight"><pre><span></span><code><span class="n">sqlite</span><span class="o">-</span><span class="n">utils</span><span class="w"> </span><span class="n">db</span><span class="p">.</span><span class="n">sqlite</span><span class="w"> </span><span class="ss">"SELECT strftime('%Y-W%W', date) AS week, SUM(CAST(duration AS REAL)) AS hours FROM journal GROUP BY week ORDER BY week;"</span>
|
|
|
|
<span class="p">[</span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W21"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W22"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W23"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W25"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W29"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W37"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W39"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W40"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W41"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W42"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W44"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">,</span>
|
|
<span class="w"> </span><span class="err">{</span><span class="ss">"week"</span><span class="p">:</span><span class="w"> </span><span class="ss">"2023-W45"</span><span class="p">,</span><span class="w"> </span><span class="ss">"hours"</span><span class="p">:</span><span class="w"> </span><span class="n">XX</span><span class="err">}</span><span class="p">]</span>
|
|
</code></pre></div>
|
|
|
|
<p>I then created a quick dashboard using <a href="https://github.com/rclement/datasette-dashboards">datasette-dashboard</a>, which looks like this:</p>
|
|
<p><img alt="Capture d'écran du dashboard, heures par semaine" src="/images/datasette/hours-per-week.png">
|
|
<img alt="Capture d'écran du dashboard, heures par projet" src="/images/datasette/hours-per-project.png"></p>
|
|
<p>Using this configuration:</p>
|
|
<div class="highlight"><pre><span></span><code><span class="nt">plugins</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">datasette-render-markdown</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">columns</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="s">"content"</span>
|
|
<span class="w"> </span><span class="nt">datasette-dashboards</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">my-dashboard</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Notmyidea</span>
|
|
<span class="w"> </span><span class="nt">filters</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">project</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Projet</span>
|
|
<span class="w"> </span><span class="nt">type</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">select</span>
|
|
<span class="w"> </span><span class="nt">db</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">db</span>
|
|
<span class="w"> </span><span class="nt">query</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">SELECT DISTINCT project FROM journal WHERE project IS NOT NULL ORDER BY project ASC</span>
|
|
<span class="w"> </span><span class="nt">layout</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="nv">hours-per-project</span><span class="p p-Indicator">]</span>
|
|
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="nv">entries</span><span class="p p-Indicator">]</span>
|
|
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="nv">hours-per-week</span><span class="p p-Indicator">]</span>
|
|
<span class="w"> </span><span class="nt">charts</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">hours-per-project</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Nombre d'heures par projet</span>
|
|
<span class="w"> </span><span class="nt">query</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">SELECT project, SUM(CAST(duration AS REAL)) as total FROM journal GROUP BY project;</span>
|
|
<span class="w"> </span><span class="nt">db</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">db</span>
|
|
<span class="w"> </span><span class="nt">library</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">vega-lite</span>
|
|
<span class="w"> </span><span class="nt">display</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">mark</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">{</span><span class="nt"> type</span><span class="p">:</span><span class="w"> </span><span class="nv">arc</span><span class="p p-Indicator">,</span><span class="nt"> tooltip</span><span class="p">:</span><span class="w"> </span><span class="nv">true</span><span class="w"> </span><span class="p p-Indicator">}</span>
|
|
<span class="w"> </span><span class="nt">encoding</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">color</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">{</span><span class="nt"> field</span><span class="p">:</span><span class="w"> </span><span class="nv">project</span><span class="p p-Indicator">,</span><span class="nt"> type</span><span class="p">:</span><span class="w"> </span><span class="nv">nominal</span><span class="w"> </span><span class="p p-Indicator">}</span>
|
|
<span class="w"> </span><span class="nt">theta</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">{</span><span class="nt"> field</span><span class="p">:</span><span class="w"> </span><span class="nv">total</span><span class="p p-Indicator">,</span><span class="nt"> type</span><span class="p">:</span><span class="w"> </span><span class="nv">quantitative</span><span class="w"> </span><span class="p p-Indicator">}</span>
|
|
<span class="w"> </span><span class="nt">hours-per-week</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Heures par semaine</span>
|
|
<span class="w"> </span><span class="nt">query</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">SELECT strftime('%Y-W%W', date) AS week, SUM(CAST(duration AS REAL)) AS hours FROM journal GROUP BY week ORDER BY week;</span>
|
|
<span class="w"> </span><span class="nt">db</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">db</span>
|
|
<span class="w"> </span><span class="nt">library</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">vega-lite</span>
|
|
<span class="w"> </span><span class="nt">display</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">mark</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">{</span><span class="nt"> type</span><span class="p">:</span><span class="w"> </span><span class="nv">bar</span><span class="p p-Indicator">,</span><span class="nt"> tooltip</span><span class="p">:</span><span class="w"> </span><span class="nv">true</span><span class="w"> </span><span class="p p-Indicator">}</span>
|
|
<span class="w"> </span><span class="nt">encoding</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">x</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">{</span><span class="nt"> field</span><span class="p">:</span><span class="w"> </span><span class="nv">week</span><span class="p p-Indicator">,</span><span class="nt"> type</span><span class="p">:</span><span class="w"> </span><span class="nv">ordinal</span><span class="p p-Indicator">}</span>
|
|
<span class="w"> </span><span class="nt">y</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">{</span><span class="nt"> field</span><span class="p">:</span><span class="w"> </span><span class="nv">hours</span><span class="p p-Indicator">,</span><span class="nt"> type</span><span class="p">:</span><span class="w"> </span><span class="nv">quantitative</span><span class="w"> </span><span class="p p-Indicator">}</span>
|
|
|
|
<span class="w"> </span><span class="nt">entries</span><span class="p">:</span>
|
|
<span class="w"> </span><span class="nt">title</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">Journal</span>
|
|
<span class="w"> </span><span class="nt">db</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">db</span>
|
|
<span class="w"> </span><span class="nt">query</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">SELECT * FROM journal WHERE TRUE [[ AND project = :project ]] ORDER BY date DESC</span>
|
|
<span class="w"> </span><span class="nt">library</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">table</span>
|
|
<span class="w"> </span><span class="nt">display</span><span class="p">:</span>
|
|
</code></pre></div>
|
|
|
|
<p>And ran datasette with:</p>
|
|
<div class="highlight"><pre><span></span><code>datasette<span class="w"> </span>db.sqlite<span class="w"> </span>--root<span class="w"> </span>--metadata<span class="w"> </span>metadata.yaml
|
|
</code></pre></div>
|
|
<p>
|
|
<a href="https://blog.notmyidea.org/tag/datasette.html">#Datasette</a>, <a href="https://blog.notmyidea.org/tag/graphs.html">#Graphs</a>, <a href="https://blog.notmyidea.org/tag/sql.html">#SQL</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> |