<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Tom Nijhof</title>
    <description>The latest articles on Forem by Tom Nijhof (@wagenrace).</description>
    <link>https://forem.com/wagenrace</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1163998%2Fb401c05e-028a-4e91-ad4b-000edabc8c97.jpeg</url>
      <title>Forem: Tom Nijhof</title>
      <link>https://forem.com/wagenrace</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wagenrace"/>
    <language>en</language>
    <item>
      <title>A Solo Game Developer who uses only Free &amp; Open Source Tools</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Sun, 04 Jan 2026 19:21:26 +0000</pubDate>
      <link>https://forem.com/wagenrace/a-solo-game-developer-who-uses-only-free-open-source-tools-1d6g</link>
      <guid>https://forem.com/wagenrace/a-solo-game-developer-who-uses-only-free-open-source-tools-1d6g</guid>
      <description>&lt;p&gt;I am amazed by the current state of open-source tooling. It came a long way from “just a hobby project made in the attic”. Recently, I spoke with Marcel Visser from &lt;a href="https://businesscard.dot-asterisk.nl/" rel="noopener noreferrer"&gt;Dot Asterisk&lt;/a&gt;. He creates not only the code but also the 3D assets and music, all by himself, using only open-source tools.&lt;br&gt;&lt;br&gt;
His latest game, Juggle Star DX, was also built with only open source tools as a solo developer! With even an Android release!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F49a6347ql7uus6d2obeo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F49a6347ql7uus6d2obeo.png" alt="Screen shot of the opening screen of Juggle Star DX" width="800" height="449"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot of the opening screen of Juggle Star DX&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why open source?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Did it feel like a constraint to work with only open source?&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Not really, only at specific times. Unreal has had much more time to invest in graphical maturity, for instance, but I don’t miss it in Godot. Blender has been amazing as of late, so has Godot.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Was budget or principal a big factor in going open source?&lt;/em&gt;&lt;br&gt;&lt;br&gt;
The budget to buy a lot of commercial products is there. Not interested, though. These are my default tools for small projects and studying. They also work well enough when going for something bigger. They suit my workflow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Game Engine: Godot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Godot was first released in 2014 by two Argentine developers, Juan Linietsky and Ariel Manzur. Over the years, the team has grown all over the world to make and maintain this open source game engine.&lt;br&gt;&lt;br&gt;
If we look at the trend of GDScript (a script specially made for Godot) on GitHub, we can see the growing popularity of Godot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyfro6yzce2hflqaljjd2.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyfro6yzce2hflqaljjd2.webp" alt="Graph showing the increasing number of pushes done to GitHub with GDScript" width="800" height="602"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;em&gt;Graph showing the increasing number of pushes done to GitHub with GDScript.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What was your experience with Godot?&lt;/em&gt;&lt;br&gt;&lt;br&gt;
At first, my experience was not great, this was in the 3.x days. But with 4.x, especially since 4.3, things have gotten really good and stable. Besides, the occasional crash worked great.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3D assets: Blender&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Blender is 3D computer graphics software. Fully free of charge and open source. The Dutch software developer Ton Roosendaal is the main developer who launched it in 1994 as part of the now-defunct NeoGeo company. The project went through a few phases till it ended up in the non-profit Blender foundation where in 2002 it became open source.&lt;/p&gt;

&lt;p&gt;Blender stays developing with about 3 million a year, mainly thanks to sponsors, like AMD, Nvidia, Intel, Apple, Epic Games, and Meta, but also a significant part from individual contributors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frb1k9rgiq9nxgdrijmee.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frb1k9rgiq9nxgdrijmee.webp" alt="The Blender logo" width="800" height="244"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;The Blender logo&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How is Blender?&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Blender has been an absolute blast. Since 2.8, the workflow has been intuitive enough to work with, and since 4.x, everything’s been really streamlined. It’s a great tool. I only wish there were better support for AMD cards on Linux, because I don’t want to deal with the wonky proprietary drivers. I’m stuck with CPU rendering in Cycles.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Did you hide your first donut in the game?&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Haha, no, not Blender-ready for sure, but I did re-use older stuff I made earlier!&lt;br&gt;&lt;br&gt;
&lt;em&gt;For some context&lt;/em&gt;: A very popular Blender tutorial is by Blender Guru; his first tutorial is always to make a donut.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audio&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What did you use as audio software?&lt;/em&gt;&lt;br&gt;&lt;br&gt;
I did use FL Studio for one song; this was the only time I used the closed-source software in this product. After that, I used MuseScore and Audacity for the heavy lifting and some light touches with the audio programming in Godot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audio: MuseScore&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MuseScore Studio is the open source software of MuseScore created in 2008 by Werner Schweer, Nicolas Froment, and Thomas Bonte (from Germany, France, and Belgium).&lt;/p&gt;

&lt;p&gt;Besides the open source project, the MuseScore company is a social sharing website. Here, sheet music, lessons, an AI music assistant, and other music-related products are shared and sold.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvl92t85jsftoywl0cqkb.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvl92t85jsftoywl0cqkb.webp" alt="MuseScore Studio sheet music sample" width="800" height="450"&gt;&lt;/a&gt; &lt;br&gt;
&lt;em&gt;A sample of MuseScore Studio showing how sheet music looks within the application. The music is “Green Eggs” by Marcel Visser.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What made you choose MuseScore?&lt;/em&gt;&lt;br&gt;&lt;br&gt;
MuseScore is not an application I would recommend to serious musicians, nor does it have the best and most sustainable business model, but does the job well enough. For me especially important to get sketches out quickly because my workflow is with sheet music. It makes it a lot easier for me to think in intervals and parse my harmonies semantically than doing things in a DAW (digital audio workstation), where things are obscured a bit by enharmonicism.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How did the experience differ between FL Studio, and MuseScore with Audacity?&lt;/em&gt;&lt;br&gt;&lt;br&gt;
FL Studio is also a bit looked down upon in the DAW ecosphere, but it works well enough for me to get out some electronic production that sounds good to me. I also appreciate the very consumer-friendly commercial strategy. Because of semantic erosion, actual part writing is harder, but usually I’m past that phase when switching to there (barring some embellishments or things that start to sound too dissonant because of the instrument chosen).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Video editing: Kdenlive&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Kdenlive is a video editing software part of KDE. It was launched in 2003 by Jason Wood, but was stopped for 2 years till Jean-Baptiste Mardelle picked it up in 2005, who is still part of the current development.&lt;br&gt;&lt;br&gt;
It is part of the German non-profit KDE e.V.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Video editing: Kdenlive — What made it so unpleasant? Would a closed-source alternative be better?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using Kdenlive, especially in the past, was quite crash-prone. The UX is, unfortunately, a bit hammy with having to configure everything over and over. It has a palette of video effects, but something that I would consider simple, like shaking, isn’t present, and you have to work your way around that. A lot of things are missing, like having a default export folder, quick presets, user-friendly encoding settings (very ffmpeg-wrapped), etc. I’ve had much smoother experiences with Sony Vegas in the early 10’s. It didn’t crash, had much better frame-by-frame and speed manipulation, and effects stacked more naturally. I do prefer using a FOSS variant, though, even given the discomfort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lastly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s talk about the bigger picture outside the individual tools used.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What would you do differently?&lt;/em&gt;&lt;br&gt;&lt;br&gt;
Many things. I’d get a better UI scaling system from the outset. I’d want to set up some asset pipelines before going, with more nodes that actually fit more towards Godot’s philosophy of working. I’d change my release windows to better suit player sentiment, and one of the more important things is that I will most likely kill the next game that I’m targeting for bigger audiences faster if it doesn’t seem good enough. I’ve committed to finishing this one, really just to get through the motions of things, but I’d like to be more wise about that with developing upcoming ones. This one was set up as an educational experience from start to finish, and it has been wildly successful in that regard so far.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Talking with Marcel and playing his latest game gives a clear idea of how mature open source has become. It feels like a miracle that today you can make a professional game using nothing but free and open source software tools.&lt;br&gt;&lt;br&gt;
It is not to a state where every triple-A game can be made with it, and there is a good business reason to still pay for closed software tools, but it is not the only solution anymore.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>gamedev</category>
      <category>godot</category>
      <category>blender</category>
    </item>
    <item>
      <title>Modern License Metadata for Python Packages: An Introduction to PEP 639 and SPDX</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Mon, 10 Nov 2025 14:59:08 +0000</pubDate>
      <link>https://forem.com/wagenrace/modern-license-metadata-for-python-packages-an-introduction-to-pep-639-and-spdx-2d11</link>
      <guid>https://forem.com/wagenrace/modern-license-metadata-for-python-packages-an-introduction-to-pep-639-and-spdx-2d11</guid>
      <description>&lt;p&gt;As a developer working extensively on &lt;a href="https://pypi.org/project/license-scanner/" rel="noopener noreferrer"&gt;license_scanner&lt;/a&gt;, I've encountered numerous Python packages with outdated license metadata. Understanding PEP 639 is crucial, as it introduces significant changes to how licenses are managed in Python packages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Changes in PEP 639&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Python now adheres to the SPDX (Software Package Data Exchange) standard, moving away from using classifiers. The recommended approach is to use only two fields in your &lt;code&gt;pyproject.toml&lt;/code&gt; file: &lt;code&gt;license&lt;/code&gt; and &lt;code&gt;license_files&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;license&lt;/code&gt;&lt;/strong&gt;: This field should be a string.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;license_files&lt;/code&gt;&lt;/strong&gt;: This field can be a string or a list of strings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choosing the Right License&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are numerous licenses available, many with similar names. For example, if a package is published under the "BSD License," it could refer to any of over 30 different BSD licenses, each with varying requirements. To avoid confusion, use a license name from the SPDX list, which you can find here: &lt;a href="https://spdx.org/licenses/" rel="noopener noreferrer"&gt;SPDX Licenses&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here is an example of how the &lt;code&gt;black&lt;/code&gt; package (version 25.9.0) uses this new standard: &lt;a href="https://github.com/psf/black/blob/25.9.0/pyproject.toml#L36" rel="noopener noreferrer"&gt;black's pyproject.toml&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Including License Files&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Including the full text of the license in your package can be beneficial. You can do this by adding the paths to the licenses in the &lt;code&gt;license_files&lt;/code&gt; field. Multiple files can be included if necessary. For example, see how &lt;code&gt;numpy&lt;/code&gt; handles this: &lt;a href="https://github.com/numpy/numpy/blob/main/pyproject.toml#L53" rel="noopener noreferrer"&gt;numpy's pyproject.toml&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using Multiple Licenses&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you need to use multiple licenses for your package, use SPDX names along with brackets, the OR operator, and the AND operator. The order of operations for SPDX is: brackets first, then the AND operator, and finally the OR operator.&lt;/p&gt;

&lt;p&gt;For instance, if you have &lt;code&gt;(Apache-2.0 OR BSD-3-Clause) AND PSF-2.0&lt;/code&gt;, it means you must always accept PSF-2.0, but you can combine it with either Apache-2.0 or BSD-3-Clause. Here is an example from the &lt;code&gt;cryptography&lt;/code&gt; package (version 40.0): &lt;a href="https://github.com/pyca/cryptography/blob/40.0.x/setup.cfg#L7" rel="noopener noreferrer"&gt;cryptography's setup.cfg&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handling Exceptions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The WITH operator can be used to add exceptions to a license. For example, PyInstaller version 6.16.0 includes the following: "GPLv2-or-later with a special exception which allows to use PyInstaller to build and distribute non-free programs (including commercial ones)". This indicates that the GPLv2 does not apply to products made with PyInstaller. This exception is resolved at the highest level in the SPDX operator order. See the example here: &lt;a href="https://github.com/pyinstaller/pyinstaller/blob/v6.16.0/pyproject.toml#L7" rel="noopener noreferrer"&gt;PyInstaller's pyproject.toml&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;By following these guidelines, you can ensure that your Python packages have clear and up-to-date license metadata, making it easier for users to understand and comply with your licensing terms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Adopting modern license metadata practices for Python packages, as outlined in PEP 639 and the SPDX standard, is essential for maintaining clarity and compliance. By using the &lt;code&gt;license&lt;/code&gt; and &lt;code&gt;license_files&lt;/code&gt; fields in your &lt;code&gt;pyproject.toml&lt;/code&gt; file, you can provide precise and understandable licensing information. Choosing the correct license from the SPDX list helps avoid confusion, while including full license texts and handling multiple licenses and exceptions ensures comprehensive coverage. These practices not only streamline the licensing process but also enhance transparency and trust among users and contributors.&lt;/p&gt;

</description>
      <category>python</category>
      <category>packaging</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Datalists or 50 lines of extra JavaScript and HTML?</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Wed, 29 May 2024 20:46:13 +0000</pubDate>
      <link>https://forem.com/wagenrace/datalists-or-50-lines-of-extra-javascript-and-html-51j1</link>
      <guid>https://forem.com/wagenrace/datalists-or-50-lines-of-extra-javascript-and-html-51j1</guid>
      <description>&lt;p&gt;If you’re looking to incorporate an autocomplete feature into your text input fields, there are two options available: using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist" rel="noopener noreferrer"&gt;datalist&lt;/a&gt; or writing some JavaScript. I experimented with both on my website to compare their pros and cons. The main difference are in my &lt;a href="https://github.com/rate-my-drink/rate-my-drink.github.io/pull/42/files" rel="noopener noreferrer"&gt;pull request 42&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h1&gt;
  
  
  This blog is part of &lt;a href="https://caffeinecritics.com/" rel="noopener noreferrer"&gt;caffeinecritics.com&lt;/a&gt; the code can be found on&lt;a href="https://github.com/rate-my-drink/rate-my-drink.github.io" rel="noopener noreferrer"&gt; GitHub&lt;/a&gt;.
&lt;/h1&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Datalists
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AU2s5ChTsAN0gty0t" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AU2s5ChTsAN0gty0t" alt="Datalist on [caffeinecritics](https://caffeinecritics.com/)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With just a few lines of code, you can easily add autocomplete functionality to your input fields. The code is surprisingly simple and took me much more code to accomplish without it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h1&gt;
  
  
  &lt;em&gt;Code is not the product, it is the liability of the product.&lt;/em&gt;
&lt;/h1&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
      &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border-grey-light block w-full rounded border p-3"&lt;/span&gt;
      &lt;span class="na"&gt;list=&lt;/span&gt;&lt;span class="s"&gt;"all-current-producers"&lt;/span&gt;
      &lt;span class="na"&gt;v-model=&lt;/span&gt;&lt;span class="s"&gt;"producerName"&lt;/span&gt;
      &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;input=&lt;/span&gt;&lt;span class="s"&gt;"updateProducer()"&lt;/span&gt;
      &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;datalist&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"all-current-producers"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;option&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"producer in producers"&lt;/span&gt; &lt;span class="na"&gt;:value=&lt;/span&gt;&lt;span class="s"&gt;"producer.name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/option&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/datalist&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to recent statistics, Datalist is supported by a whopping &lt;a href="https://caniuse.com/?search=datalist" rel="noopener noreferrer"&gt;97.5%&lt;/a&gt; of websites. However, there’s one notable exception — Firefox for Android, which does not support Datalist. However, it only has a market share of 0.3%. This minor setback can be particularly frustrating given that I use this browser personally. Additionally, some browsers offer integration with Datalist, such as Chrome for Android, which allows users to easily access and utilize the feature through the keyboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2160%2F0%2Ard0gPZ-y17-VPE-A" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2160%2F0%2Ard0gPZ-y17-VPE-A" alt="Datalist on [caffeinecritics](https://caffeinecritics.com/) use Chrome for Android"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, there is a drawback to using datalists. By default, the browser styles them, which means you have limited control over their appearance. While you can overwrite this by adding your own CSS, doing so requires extra effort and additional JavaScript code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Own JavaScript
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AcJAxKBfnZno2cYFG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2AcJAxKBfnZno2cYFG" alt="Own JavaScript on [caffeinecritics](https://caffeinecritics.com/)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our own JavaScript will improve across various platforms, including Firefox on Android. However, it’s worth noting that some browsers may lose their unique integration features. On the other hand, you’ll regain a significant amount of styling freedom. I must admit, I prefer the new look much more, but it requires additional effort to ensure seamless support for mobile browsers as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Datalists have some advantages and disadvantages. On the plus side, they offer less code and better integration with web browsers, making it easier to use them. However, there are also some drawbacks to consider. For instance, styling Datalists can be more challenging, and they’re not supported by Firefox on Android devices. Personally, I opted for Datalists because I prefer to keep my code simple.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>html</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Animation in SVG</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Sun, 21 Apr 2024 20:49:20 +0000</pubDate>
      <link>https://forem.com/wagenrace/animation-in-svg-12pb</link>
      <guid>https://forem.com/wagenrace/animation-in-svg-12pb</guid>
      <description>&lt;p&gt;SVG (Scalable Vector graphics) is a versatile tool that allows us to create graphics with scalable vector shapes, such as lines and circles. The best part? These graphics can be scaled down to infinitesimal sizes without any loss of resolution! But wait, there’s more — we can even add animation to our SVG without needing to use CSS. This means that our graphics will come to life and capture the viewer’s attention in a whole new way. Whether creating a simple logo or a complex info-graphic, SVG is a potent tool that can help you bring your visual ideas to life.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbblwfoz8u5wo0immerci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbblwfoz8u5wo0immerci.png" alt="A button drawn with 3 circles in SVG" width="412" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A button drawn with 3 circles in SVG&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-fit w-fit"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"102.635mm"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"102.635mm"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 103 103"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"1.1"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"svg1"&lt;/span&gt;
       &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;xmlns:svg=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"fill:#4d4d4d;stroke:#000000;stroke-width:3.01567"&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"51.50901"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"51.479477"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"49.80949"&lt;/span&gt;
           &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"Casing"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"fill:#9b0101;fill-opacity:1;stroke:#6d0101;stroke-width:2.63;stroke-opacity:1"&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"51.50901"&lt;/span&gt;
           &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"51.479477"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"35.757164"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"back-button"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"fill:#ff0000;stroke:#6d0101;stroke-width:2.63;stroke-opacity:1"&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"61.425705"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"50.430336"&lt;/span&gt;
           &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"35.757164"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"front-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;/circle&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SVG code for the button&lt;/p&gt;

&lt;h2&gt;
  
  
  Animation
&lt;/h2&gt;

&lt;p&gt;Did you know that SVG has an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Element/animate"&gt;animation element&lt;/a&gt;? With this, we can make things move or change over time on our websites or applications. Take a look at the example below, where I’m changing the center x position (cx) of a light red circle from 61 to 50 and back to 61. The animation will keep repeating forever!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-fit w-fit"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"102.635mm"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"102.635mm"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 103 103"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"1.1"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"svg1"&lt;/span&gt;
       &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;xmlns:svg=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;


       &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"fill:#4d4d4d;stroke:#000000;stroke-width:3.01567"&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"51.50901"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"51.479477"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"49.80949"&lt;/span&gt;
           &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"Casing"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"fill:#9b0101;fill-opacity:1;stroke:#6d0101;stroke-width:2.63;stroke-opacity:1"&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"51.50901"&lt;/span&gt;
           &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"51.479477"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"35.757164"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"back-button"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"fill:#ff0000;stroke:#6d0101;stroke-width:2.63;stroke-opacity:1"&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"61.425705"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"50.430336"&lt;/span&gt;
           &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"35.757164"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"front-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;lt;animate&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"animateRef"&lt;/span&gt; &lt;span class="na"&gt;attributeName=&lt;/span&gt;&lt;span class="s"&gt;"cx"&lt;/span&gt; &lt;span class="na"&gt;values=&lt;/span&gt;&lt;span class="s"&gt;"61.425705;50.430336;61.425705"&lt;/span&gt;
               &lt;span class="na"&gt;dur=&lt;/span&gt;&lt;span class="s"&gt;"1s"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;/circle&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Trigger on click
&lt;/h2&gt;

&lt;p&gt;To finish off let’s only trigger the animation if we click. The example below is done with VUE, to see a vanilla JavaScript example see this &lt;a href="https://stackoverflow.com/questions/48316611/start-a-svg-animation-onclick-event/48337470#48337470"&gt;stackoverflow example&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When we change an animation element’s repeatCount to 1 and begin to indefinite, it stops completely. Then, we add a function that starts the animation again by using a click event. Inside this function, we get the animation element either by reference (VUE.js) or by ID (vanilla JS), and call the beginElement method to start the animation once.&lt;/p&gt;

&lt;p&gt;Example: &lt;a href="https://escapethisgamevue.github.io/"&gt;https://escapethisgamevue.github.io/&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;setup&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;


      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;animateRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;clickButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nx"&gt;animateRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;beginElement&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;template&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-fit w-fit"&lt;/span&gt; &lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="na"&gt;click=&lt;/span&gt;&lt;span class="s"&gt;"clickButton()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h-fit w-fit"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"102.635mm"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"102.635mm"&lt;/span&gt; &lt;span class="na"&gt;viewBox=&lt;/span&gt;&lt;span class="s"&gt;"0 0 103 103"&lt;/span&gt; &lt;span class="na"&gt;version=&lt;/span&gt;&lt;span class="s"&gt;"1.1"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"svg1"&lt;/span&gt;
               &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt; &lt;span class="na"&gt;xmlns:svg=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2000/svg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
               &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"fill:#4d4d4d;stroke:#000000;stroke-width:3.01567"&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"51.50901"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"51.479477"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"49.80949"&lt;/span&gt;
                   &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"Casing"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
               &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"fill:#9b0101;fill-opacity:1;stroke:#6d0101;stroke-width:2.63;stroke-opacity:1"&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"51.50901"&lt;/span&gt;
                   &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"51.479477"&lt;/span&gt; &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"35.757164"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"back-button"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
               &lt;span class="nt"&gt;&amp;lt;circle&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"fill:#ff0000;stroke:#6d0101;stroke-width:2.63;stroke-opacity:1"&lt;/span&gt; &lt;span class="na"&gt;cx=&lt;/span&gt;&lt;span class="s"&gt;"61.425705"&lt;/span&gt; &lt;span class="na"&gt;cy=&lt;/span&gt;&lt;span class="s"&gt;"50.430336"&lt;/span&gt;
                   &lt;span class="na"&gt;r=&lt;/span&gt;&lt;span class="s"&gt;"35.757164"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"front-button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                   &lt;span class="nt"&gt;&amp;lt;animate&lt;/span&gt; &lt;span class="na"&gt;ref=&lt;/span&gt;&lt;span class="s"&gt;"animateRef"&lt;/span&gt; &lt;span class="na"&gt;attributeName=&lt;/span&gt;&lt;span class="s"&gt;"cx"&lt;/span&gt; &lt;span class="na"&gt;begin=&lt;/span&gt;&lt;span class="s"&gt;"indefinite"&lt;/span&gt; &lt;span class="na"&gt;values=&lt;/span&gt;&lt;span class="s"&gt;"61.425705;50.430336;61.425705"&lt;/span&gt;
                       &lt;span class="na"&gt;dur=&lt;/span&gt;&lt;span class="s"&gt;"1s"&lt;/span&gt; &lt;span class="na"&gt;repeatCount=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
               &lt;span class="nt"&gt;&amp;lt;/circle&amp;gt;&lt;/span&gt;
           &lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Animations in SVG format can be quite impressive and easy to use without requiring additional coding like CSS or JavaScript. This means that creating scalable vector animations is often possible without needing to rely on these tools.&lt;/p&gt;

</description>
      <category>svg</category>
      <category>vue</category>
      <category>webdev</category>
      <category>html</category>
    </item>
    <item>
      <title>Boy Scout refactoring works but can take 189 years (still faster than waiting for motivation)</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Mon, 25 Mar 2024 21:34:33 +0000</pubDate>
      <link>https://forem.com/wagenrace/boy-scout-refactoring-works-but-can-take-189-years-still-faster-than-waiting-for-motivation-2c09</link>
      <guid>https://forem.com/wagenrace/boy-scout-refactoring-works-but-can-take-189-years-still-faster-than-waiting-for-motivation-2c09</guid>
      <description>&lt;p&gt;The Boy Scout principle, “Leave a place better than you found it,” is an admirable philosophy that can be applied to many aspects of life, including coding. Refactoring a large code base with this principle in mind can be a challenging task, but it’s certainly possible. Can you replace one language with another? Well, I wondered about this and I found a use case where this happened — the Dutch Law.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Boy Scout refactoring?
&lt;/h2&gt;

&lt;p&gt;The Boy Scout principle is to leave a place better than you found it. This can be cleaning up, adding something, or updating something. In code, we use this as an excuse to refactor every piece of code we have to work on.&lt;/p&gt;

&lt;p&gt;A great upside is that you never plan time to refactor. You also never spend time on just refactoring. And in general, it costs less time, because if you already worked on the code the refactoring will go faster.&lt;/p&gt;

&lt;p&gt;However, not all code gets updates at a similar rate. Some code will be updated every sprint, other code will never be updated after it is written. So what if a standard changes before you update the last code? Well, now you have 3 standards, good luck!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dutch Law
&lt;/h2&gt;

&lt;p&gt;Let’s go over a bit of history, in 1813 the Netherlands broke free from France (again). This is the point where the current Dutch law can be traced back to. This is not the beginning of the Netherlands, but the beginning of the current Dutch law (let’s ignore the rest of the messy history).&lt;/p&gt;

&lt;p&gt;The Dutch made an important decision, “we keep all the France laws we have from when we were puppet state and rewrite them in Dutch when we update them”. So basically Boy Scout Refactoring.&lt;br&gt;
And so they did, every time a law needed an update it would be rewritten in Dutch. It took till the 31st of October 2002 when the last law was rewritten! 189 years! (&lt;a href="https://nl.wikipedia.org/wiki/Chronologie_van_de_Nederlandse_wetgeving"&gt;link&lt;/a&gt;)&lt;br&gt;
For those wondering, the last law is *Loi concernant les Mines, les Minières et les Carrières; *Something about mining.&lt;/p&gt;

&lt;p&gt;What happened in these years? Well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Belgium broke away from the Netherlands.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Luxembourg and the Netherlands stopped sharing a head of state because a Dutch succession law allowed for a woman and Luxembourg did not.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;World War One happened. This left Keizer Willem in the Netherlands until the German monarchy will be restored (his grave is still here) (&lt;a href="https://nl.wikipedia.org/wiki/Wilhelm_II_van_Duitsland#Wilhelms_laatste_wens"&gt;link&lt;/a&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;World War two, when the Nazis introduced more laws into the Netherlands. (&lt;a href="https://nl.wikipedia.org/wiki/Gehandhaafde_wetten_van_de_Duitse_bezetter"&gt;link&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Indonesia became independent followed by Suriname&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The European Union was founded (now we have laws in France, Dutch and 22 other languages). (&lt;a href="https://european-union.europa.eu/principles-countries-history/languages_en"&gt;source&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I was born (maybe a slightly smaller event compared to the rest)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;When it comes to refactoring, the Boy Scout approach is not a substitute for more comprehensive techniques. While the Boy Scout principle can be a useful tool in certain situations, it’s important to recognize that it’s not a one-size-fits-all solution. In order to effectively refactor code, it’s important to use a variety of techniques and approaches, rather than relying solely on the Boy Scout method. By incorporating different strategies into your refactoring toolkit, you can ensure that your code is always well-structured, maintainable, and efficient.&lt;/p&gt;

</description>
      <category>humor</category>
      <category>refactoring</category>
      <category>productivity</category>
      <category>coding</category>
    </item>
    <item>
      <title>The weird quirk with rounding in Python (and that is good)</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Wed, 20 Mar 2024 19:43:01 +0000</pubDate>
      <link>https://forem.com/wagenrace/the-weird-quirk-with-rounding-in-python-and-that-is-good-1fki</link>
      <guid>https://forem.com/wagenrace/the-weird-quirk-with-rounding-in-python-and-that-is-good-1fki</guid>
      <description>&lt;p&gt;When working with rounding in Python, you may have come across an unexpected quirk. Unlike most people who tend to round numbers ending in .5 up, Python operates differently. While it rounds 0.5 to 0, it rounds 1.5 to 2, following the international standard (&lt;a href="https://en.wikipedia.org/wiki/IEEE_754" rel="noopener noreferrer"&gt;IEEE 754&lt;/a&gt;). This may be seen as a feature or a bug, depending on how you view it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ABFmQo2jQ6haUTnD4" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F2000%2F0%2ABFmQo2jQ6haUTnD4" alt="The Python logo surrounded by different examples of rounding"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  It is a bug, for me!
&lt;/h2&gt;

&lt;p&gt;If you encounter a bug when working with numbers ending in .5 I recommend utilizing the build library &lt;a href="https://docs.python.org/3/library/decimal.html" rel="noopener noreferrer"&gt;decimal&lt;/a&gt;. Within this library, there is a function that rounds all numbers ending with .5 up by using the ROUND_HALF_UP mode. This mode can be found in the &lt;a href="https://docs.python.org/3/library/decimal.html#rounding-modes" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;, along with other options for rounding. By incorporating this function into our code, we can ensure that any numbers ending in .5 are rounded correctly and avoid potential bugs.&lt;/p&gt;

&lt;p&gt;I do NOT recommend using this unless it specifically causes a bug!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;decimal&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Decimal&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROUND_HALF_UP&lt;/span&gt;


    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;round_nat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
        Rounds all halves up
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;quantize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ROUND_HALF_UP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; rounds to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;round_nat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  It is a feature!
&lt;/h2&gt;

&lt;p&gt;Rounding behavior in Python conforms to &lt;a href="https://en.wikipedia.org/wiki/Rounding#Rounding_half_to_even" rel="noopener noreferrer"&gt;IEEE 754 rounding to nearest, ties to even&lt;/a&gt;. This is also known as &lt;strong&gt;bankers rounding&lt;/strong&gt; or &lt;strong&gt;Dutch rounding&lt;/strong&gt;. Guido van Rossum is not a banker but he is Dutch. &lt;br&gt;
This ensures a fully random number is just as likely to round up as down. For example, let’s consider a range of numbers from 0 to 10 in increments of 0.1. If we calculate the average of these numbers, it comes out to 4.95. However, if we use the round_nat function (created in the previous chapter), we get an average of 5, due to the presence of 10 numbers ending in .5, which are all rounded up. This results in a slight bias towards a larger number. On the other hand, using native Python rounding eliminates this bias.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="n"&gt;all_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;average of all numbers:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_num&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;all_rounded_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_num&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;average of all rounded numbers:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_rounded_num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_rounded_num&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;all_nat_rounded_num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;round_nat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;all_num&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;average of all natural rounded numbers:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_nat_rounded_num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;all_nat_rounded_num&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    average of all numbers: 4.95
    average of all rounded numbers: 4.95
    average of all natural rounded numbers: 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another way to understand it is by looking at the numbers between 0 and 1 with 1 decimal. We have 9 numbers in between, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9. To round them we need to remove 0.1 from 0.1, and add 0.1 to 0.9. Meaning their biases balance out.&lt;br&gt;
The same for 0.2 and 0.8, 0.3 and 0.7, and 0.4 and 0.6; All numbers except 0.5. We do not have a counterbalance for 0.5 between 0 and 1.&lt;/p&gt;

&lt;p&gt;To balance rounding 0.5 we balance it with 1.5. We remove 0.5 from 0.5 and add 0.5 to 1.5. Meaning that rounding is just as likely to increase as decrease a random number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Rounding in Python may seem unusual at first glance, but it’s actually quite useful. Understanding how it works is essential as it can cause a program to behave differently than expected. While it may not have a significant impact in most cases, it’s important to be aware of this behavior to avoid any potential issues.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to not launch an empty app</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Tue, 19 Mar 2024 20:46:55 +0000</pubDate>
      <link>https://forem.com/wagenrace/how-to-not-launch-an-empty-app-1d9m</link>
      <guid>https://forem.com/wagenrace/how-to-not-launch-an-empty-app-1d9m</guid>
      <description>&lt;p&gt;Many applications are currently displaying content in various forms. Reddit, Facebook, Masterdom, Yelp, and CaffeineCritics are just a few examples of apps that offer content in one way or another. While the technology behind these apps may not be particularly groundbreaking, it is the content they provide that truly gives them value. However, a question remains: how can app developers fill their new apps with content without violating copyright laws?&lt;/p&gt;

&lt;h2&gt;
  
  
  Just make it
&lt;/h2&gt;

&lt;p&gt;If you need content, it's worth investing the time to create it. A great example is Artturi Jalli, who developed an app for finding Finnish cottages. He dedicates a significant amount of time to writing blog posts to promote his web app. Please note that this article tells us he spent 6 hours coding and more than 1000 hours of blogging.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://medium.com/@artturi-jalli/i-built-an-app-in-6-hours-that-makes-1-500-mo-85139edee87d"&gt;https://medium.com/@artturi-jalli/i-built-an-app-in-6-hours-that-makes-1-500-mo-85139edee87d&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzc37f37dxrqwz07hfck.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzc37f37dxrqwz07hfck.jpg" alt="woman writting" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate it
&lt;/h2&gt;

&lt;p&gt;Using artificial intelligence to create content may seem like a dream come true, but it's not as simple as it sounds. While AI can certainly assist in the creation process, you are still ultimately responsible for ensuring the quality of the content. Moreover, users are becoming increasingly aware of AI-generated content, so it's important to approach this method with a mindset of "make it yourself with the help of AI" rather than relying solely on machine-generated output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjos3hm5py6qlnn6d129q.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjos3hm5py6qlnn6d129q.jpg" alt="Robot writting" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scrape
&lt;/h2&gt;

&lt;p&gt;Data scraping can be a useful tool for collecting information from various websites. In my case, I utilized this technique to gather data on coffee and tea offerings from numerous webshops. By scraping data from these sites, I was able to accumulate a sizable collection of drinks without requiring users to manually input reviews. This approach not only saves time but also makes the app feel more dynamic and alive! However, it's crucial to be mindful of copyright considerations when engaging in data scraping.&lt;br&gt;
At Mastodon, you also see this a lot in the form of twitter-bots (or x-bots I guess). They copy tweets and toot them on Mastodon. However, this might* be copyright infringement, so it may not be the best if you are a business.&lt;br&gt;
*might be because there are thousands of governing bodies that make laws. Ask a lawyer, not a blog post if you have questions about it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxo6j1pizvj5b8yd2lupr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxo6j1pizvj5b8yd2lupr.jpg" alt="Woman shopping groceries" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait for users
&lt;/h2&gt;

&lt;p&gt;In the world of app development, it's (sometimes) possible to launch an app without any users and wait for them to fill it. This approach is often used by online games that become more enjoyable with more players. However, by incorporating NPCs (non-player characters) and storylines for solo play, developers can create an engaging experience without the need for others. &lt;br&gt;
Apple has taken this approach with their new VR headset (that they do not want to call VR headset), which comes with a few basic apps and has ported many iPhone apps to work in the VR environment. While the initial value may come from Apple itself, the true potential lies in the creativity of other developers who will bring innovative apps to the platform. However, if not enough people will make Apple VR apps the project will be killed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frv0x9xc6ckdndyffg2or.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frv0x9xc6ckdndyffg2or.jpg" alt="Woman waiting" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, filling mobile apps with engaging content while respecting copyright laws requires a careful balance of strategies. While shortcuts like AI-generated content or data scraping may seem appealing, prioritizing original creation, as seen in examples like Artturi Jalli's Finnish cottages app, can lead to greater user satisfaction.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>design</category>
      <category>scrapping</category>
      <category>launching</category>
    </item>
    <item>
      <title>Why SHOULD Python be your first language?</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Fri, 16 Feb 2024 21:08:52 +0000</pubDate>
      <link>https://forem.com/wagenrace/why-should-python-be-your-first-language-j89</link>
      <guid>https://forem.com/wagenrace/why-should-python-be-your-first-language-j89</guid>
      <description>&lt;p&gt;While reading an article by Krishnaa on why Python shouldn't be one's first programming language, I found myself disagreeing with many of the points presented. In fact, most of the arguments seemed to suggest that Python is actually a great choice for beginners. It was an interesting read, but I couldn't help writing this response.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/krishnaa192" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Yda_aXfP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://media.dev.to/cdn-cgi/image/width%3D150%2Cheight%3D150%2Cfit%3Dcover%2Cgravity%3Dauto%2Cformat%3Dauto/https%253A%252F%252Fdev-to-uploads.s3.amazonaws.com%252Fuploads%252Fuser%252Fprofile_image%252F696519%252F038a0d46-4b05-4b52-bfc4-1378aaa2ae30.jpeg" alt="krishnaa192"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/krishnaa192/why-should-python-not-be-your-first-language-51f" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Why should Python not be your first language?&lt;/h2&gt;
      &lt;h3&gt;krishnaa192 ・ Feb 14&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#datastructures&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#java&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#coding&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  What makes a good first language?
&lt;/h2&gt;

&lt;p&gt;In my view, a good first language should be welcoming and make it easy for beginners to learn programming without encountering numerous obstacles or feeling discouraged. A language that requires new programmers to master numerous "real" programming concepts from the start can be off-putting and may lead to frustration. Conversely, a language like Python is ideal for beginners due to its simplicity and ease of use, allowing them to learn without feeling overwhelmed. By making programming more accessible and enjoyable, we can encourage more people to take an interest in this field and foster a positive learning environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Type Declarations
&lt;/h2&gt;

&lt;p&gt;In Python, we use a dynamically typed system, which means that you don't have to worry about assigning the wrong data type to a variable. For instance, you can simply add a double to an integer or divide it by a float without any issues. This is beneficial for beginners as they can postpone learning about different types of numbers until they feel more comfortable with programming. For example, let's say we have a variable &lt;code&gt;x&lt;/code&gt; that is initially set to &lt;code&gt;4.5&lt;/code&gt;. We can then assign it the value of &lt;code&gt;10&lt;/code&gt;, which might seem strange at first. However, Python will automatically convert the value to a float without any errors. This process feels natural and intuitive, allowing beginners to focus on learning programming concepts without getting bogged down by technical details. Once you're ready to introduce types, you can add type hints to your code without having to learn an entirely new language. This makes the learning process more incremental and manageable. By gradually introducing new concepts, you can build a strong foundation in programming that will serve you well in your future endeavors.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limited data structure
&lt;/h2&gt;

&lt;p&gt;In a recent blog post, we explore Vanilla Python regarding data structures. Unlike many other programming languages, Vanilla Python only provides lists, with no arrays to be found. At first, this might seem like a limitation, but it actually works in the language's favor. By not having arrays, beginners are spared the confusion of distinguishing between lists and arrays, as there is only one type of data structure available. This streamlined approach allows learners to focus on other aspects of programming without getting bogged down in the details of multiple data structures. Once they have a solid foundation in Vanilla Python, they can then explore additional packages like NumPy, which provide array functionality. This gradual learning process makes it easier for beginners to absorb new concepts and build upon their existing knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Less Exposure to Low-Level Concepts
&lt;/h2&gt;

&lt;p&gt;“Python abstracts many low-level details, making it easy for beginners to start coding without understanding some fundamental concepts such as memory management or pointers.” - Krishnaa &lt;/p&gt;

&lt;p&gt;I could not have said it better myself. Memory optimization is not for beginners, it is what you learn to do from beginner to mid-level. &lt;/p&gt;

&lt;h2&gt;
  
  
  Good Built-in Functions
&lt;/h2&gt;

&lt;p&gt;In Python, there are useful built-in functions that can help beginners get started with ease. For instance, the sort function is a great tool for organizing data in a logical manner. However, forcing a beginner to learn a specific sorting algorithm may hinder their enjoyment of the language. If they show interest, it's fine to explain the concept, but it's not essential knowledge for basic use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Downsides of Python as a first language
&lt;/h2&gt;

&lt;p&gt;I have talked a lot about why Python is great for beginners, but let's also address some of the shortcomings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hard to install
&lt;/h3&gt;

&lt;p&gt;Getting Python set up and running can be a bit of a challenge. Many tutorials focus on creating the ideal work environment, but we're here to show you a faster way. If you're learning Python, check out Mu. It's easy to install and get started, just like Processing for Java.&lt;/p&gt;

&lt;h3&gt;
  
  
  Boring visuals
&lt;/h3&gt;

&lt;p&gt;In our pursuit of creating engaging and interactive programs, visual feedback plays a crucial role in keeping users motivated. While programming can become challenging at times, it's essential to provide visually appealing results to keep things interesting. For Python developers, utilizing libraries like PyGame can help create more visually stimulating content, even if the language itself may not be ideal for game development. To gain further inspiration, checking out the Mu tutorials can offer valuable insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The simplification of Python is what makes it a great first language! Do not try to punish beginners because they do not know everything, help them by letting them skip the more advanced topics.&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginners</category>
      <category>programming</category>
      <category>learning</category>
    </item>
    <item>
      <title>Caffeine Critics: January update</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Wed, 14 Feb 2024 14:40:56 +0000</pubDate>
      <link>https://forem.com/wagenrace/caffeine-critics-january-update-10pb</link>
      <guid>https://forem.com/wagenrace/caffeine-critics-january-update-10pb</guid>
      <description>&lt;p&gt;Several small changes were made to the Caffeine Critics project within the past month. To gather all these updates in one place, I have decided to share them here. So, here are the updates for Caffeine Critics within January.&lt;/p&gt;

&lt;h2&gt;
  
  
  UX: Using stars for rating
&lt;/h2&gt;

&lt;p&gt;With &lt;a href="https://www.npmjs.com/package/vue-star-rating"&gt;vue-star-rating&lt;/a&gt;, you can easily add a rating system to your project. The rating can now be rounded off to half stars for more precision. Clicking directly on the stars is simple, but there are also plus and minus buttons located next to them for added convenience when using a mobile device or the user has trouble with the mouse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foo7a7k95oi95oh5bmpgf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foo7a7k95oi95oh5bmpgf.png" alt="Screenshot of star reviews" width="800" height="112"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the database, the rating will be stored as a value between 0 and 240. This allows for some flexibility in adjusting the rating system. For instance, 240 can be divided by the numbers 2, 3, 4, 5, 6, 8, 10, 12, 15, 16, 30, 40, 48, 60, 80, and 120, which means that I can swap the current 10-step rating system for a 48-step by simply altering the front-end without affecting the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  UX: Clear error message
&lt;/h2&gt;

&lt;p&gt;When logging in, registering, or creating a new drink, you could encounter an error. In the past, this error was silent and went unnoticed, but with the integration of vue-toast-notification, users are now provided with clear messages. &lt;br&gt;
I think this lack of clear messages resulted in my first user disappearing, as their account was created but never verified, making the app seem non-functional for them. &lt;br&gt;
However, we can learn from this experience and strive to provide a better experience for future users.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwi5e94nsuq3nmr45gnyt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwi5e94nsuq3nmr45gnyt.png" alt="Screenshot of an error message" width="800" height="167"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  New feature: Filter on producer
&lt;/h2&gt;

&lt;p&gt;With the addition of a new filter, searching for drinks on our platform has become even more convenient. Now, you can specifically search for drinks produced by "Albert Heijn", making it easier to find the one you want to review&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsqcijzsto3jbf3f3gbx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffsqcijzsto3jbf3f3gbx.png" alt="Screen shot of the filter by producer" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bugfix: Github page + VUE.js = 404 for some files
&lt;/h2&gt;

&lt;p&gt;Github Pages rely on Jekyll, which in turn uses &lt;code&gt;_&lt;/code&gt; as a default ignore pattern for files. However, this caused an issue when vue-star-rating was introduced, because it created a file named &lt;code&gt;_plugin-vue_export-helper-*.js&lt;/code&gt; during the build process. Fortunately, the solution was straightforward - creating an empty file called .nojekyll in the public folder resolved the problem. The amount of time spent searching for this simple fix will not be mentioned…&lt;/p&gt;

&lt;h2&gt;
  
  
  UX: Populate database
&lt;/h2&gt;

&lt;p&gt;An app launching with nothing in it will never succeed. Users can't do all the work, so I try to gather drinks from different producers to make it more user-friendly. Instead of covering multiple markets partially, my goal is to collect all teas and coffees sold in the Netherlands' big supermarket chains and add producers I come across. At the end of January, there were 555 total drinks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The producers added in January&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pickwick&lt;/li&gt;
&lt;li&gt;Clipper&lt;/li&gt;
&lt;li&gt;Albert Heijn&lt;/li&gt;
&lt;li&gt;Zonnatura&lt;/li&gt;
&lt;li&gt;Lipton&lt;/li&gt;
&lt;li&gt;Pukka&lt;/li&gt;
&lt;li&gt;Bellarom (Lidl brand)&lt;/li&gt;
&lt;li&gt;Bean Brothers&lt;/li&gt;
&lt;li&gt;Uno a basta&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The app is maturing rapidly, but unfortunately, no one is sticking around to use it. Our next goal is to scrape all tea and coffee products in Dutch supermarkets and make sure drinks and producers are fully CURD (create, update, read, and delete) for maximum efficiency.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ux</category>
      <category>opendevelopment</category>
      <category>development</category>
    </item>
    <item>
      <title>Sharing code between projects</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Sun, 11 Feb 2024 12:02:18 +0000</pubDate>
      <link>https://forem.com/wagenrace/sharing-code-between-projects-2219</link>
      <guid>https://forem.com/wagenrace/sharing-code-between-projects-2219</guid>
      <description>&lt;p&gt;As developers, we often strive to follow the principle of "Don't Repeat Yourself" (DRY) in our code. While this practice can lead to more maintainable and efficient software, it's not always a straightforward task. In my experience, I've found that several techniques can help us avoid repetition and write better code. I'd like to share some of these methods with you today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Just repeat yourself
&lt;/h2&gt;

&lt;p&gt;When it comes to implementing a new feature in your project, sometimes the simplest approach is the best. In these cases, simply copying and pasting the code from one project to another can be the most efficient method. This approach works particularly well when you expect the two pieces of code to differ significantly from each other. By repeating yourself in this way, you can save time and effort compared to trying to rewrite the code from scratch.&lt;/p&gt;

&lt;p&gt;Pros:&lt;br&gt;
Very easy to implement&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the use cases start to differ from each other it is easy to update the functions
Cons:&lt;/li&gt;
&lt;li&gt;You will have to maintain the same code in multiple places&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a package
&lt;/h2&gt;

&lt;p&gt;Creating a package and sharing it in a distribution is one of the most common ways to share code. If you are working on open source I would say this comes with barely any downsides. However, if you need to work on closed-sourced software you first need to set up your own distribution in order to make this work.&lt;br&gt;
Most developers have already worked with packages so working with your code as a package will be easy to adopt.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Almost every developer will be able to work with it
The versioning of code is easy
Cons:&lt;/li&gt;
&lt;li&gt;Setting up an internal distribution for closed-sourced software&lt;/li&gt;
&lt;li&gt;Additional boilerplate code to turn your work into a package.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Git submodules
&lt;/h2&gt;

&lt;p&gt;If you're using Git for your source control, you can utilize Git submodules to add another repository as a folder within your current repository. This allows you to access the full repository of your project. To add a submodule, use &lt;code&gt;git submodule add [URL2GITREPO]&lt;/code&gt;. &lt;br&gt;
When cloning a repo for the first time with a submodule, you'll need to run &lt;code&gt;git submodule update --init --recursive&lt;/code&gt; to populate the submodule, otherwise, it will simply be an empty folder. The submodule will keep track of what commit and branch it is on. &lt;br&gt;
However, in my experience, this solution can be less stable and requires more debugging. Additionally, you may need to explain it to colleagues since it's not a common practice.&lt;/p&gt;

&lt;p&gt;Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lots of control of which version
No additional infrastructure is needed besides git
Cons:&lt;/li&gt;
&lt;li&gt;It does not always work well, and to debug it you need some good git knowledge&lt;/li&gt;
&lt;li&gt;Who receives does need read access to your code to make it work&lt;/li&gt;
&lt;li&gt;Only works for code, not for compiled binaries&lt;/li&gt;
&lt;li&gt;All tests and developers' code will also be in the repo&lt;/li&gt;
&lt;li&gt;You need to pull the latest version of the submodule to make sure you are in sync&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sccw1soxch30g9q266z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0sccw1soxch30g9q266z.png" alt="An example of ai-writer-assistant.github.io added as a submodule to a project"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An example of ai-writer-assistant.github.io added as a submodule to a project&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;There are multiple solutions for sharing code, and none of them are the best solution every time. Make sure you understand your situation and realize what will give you the least work in the future. &lt;/p&gt;

</description>
      <category>developer</category>
      <category>git</category>
      <category>packaging</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Locally hosted AI writing assistant</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Sat, 10 Feb 2024 14:10:53 +0000</pubDate>
      <link>https://forem.com/wagenrace/locally-hosted-ai-writing-assistant-1bfa</link>
      <guid>https://forem.com/wagenrace/locally-hosted-ai-writing-assistant-1bfa</guid>
      <description>&lt;p&gt;Most artificial intelligence (AI) tools are either closed-source or require a server to function. However, with the help of Ollama, it is possible to create simple yet powerful local AI tools without any of these constraints. In this blog post, I will demonstrate how I built my own local AI tool using Ollama's user-friendly interface and flexible architecture. By doing so, I aim to showcase the ease with which one can leverage AI capabilities without relying on proprietary software or remote servers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wn0ik9q7gw17nook4t1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9wn0ik9q7gw17nook4t1.jpg" alt="An AI image of a bird robot writing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An AI image of a bird robot writing&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I recently developed an AI-powered writing assistant using Ollama. To build this innovative tool, I leveraged Vue.js and the Ollama JavaScript package, both of which proved to be indispensable in the development process. By harnessing the power of these advanced technologies, I was able to create a user-friendly interface that streamlines the writing process and yields high-quality content with ease.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  The solution only works on Linux, Windows Subsystem Linux (WSL), and maybe Mac
&lt;/h3&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lr4przwntkgm4xzj8gi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9lr4przwntkgm4xzj8gi.png" alt="A screenshot of AI writer showing how the first draft of this blog was made"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A screenshot of AI writer showing how the first draft of this blog was made.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Integrate into JavaScript
&lt;/h2&gt;

&lt;p&gt;Ollama will send the message to Llama2 model. The response can be returned with streaming, allowing for a seamless and interactive experience. By displaying the model's response in real-time, users will no longer have to look at loading bars, further enhancing their overall experience. This feature not only simplifies the programming process but also provides a more dynamic and engaging environment for all involved.&lt;br&gt;
It will use a model named blog that I create in the next chapter.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“”&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendToModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// Empty the output&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;


&lt;span class="c1"&gt;// Send the message to Ollama&lt;/span&gt;
&lt;span class="c1"&gt;// It will use the model named ‘blog’&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
   &lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
 &lt;span class="p"&gt;})&lt;/span&gt;


&lt;span class="c1"&gt;// At every part of the stream back to the response in real time&lt;/span&gt;
 &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Fine-tuning the model
&lt;/h2&gt;

&lt;p&gt;Ollama's default model is Llama2, which is suitable for casual conversations. However, I wanted to modify it to generate rewritten versions of my initial ideas. To achieve this, I created a custom model file and made two key adjustments. Firstly, I lowered the temperature to 0.5, which results in less creative output that still conveys my intended message. Secondly, I modified the system message to avoid generating answers and instead focus on rewriting the input text. Below is the updated model file for your reference.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

FROM llama2

# set the temperature to 1 [higher is more creative, lower is more coherent]
PARAMETER temperature 0.5

# set the system message
SYSTEM """
Rewrite the following into a paragraph of a technical blog with simple and formal English.
"""


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To utilize the newly introduced model file, I executed the following command. This resulted in the generation of a freshly minted model named "blog." With this novel model at my disposal, I am now able to incorporate it into my JavaScript applications for seamless usage.&lt;br&gt;
&lt;code&gt;ollama create blog -f ./Modelfile&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In conclusion, Ollama provides an accessible and user-friendly means of utilizing Large Language Models (LLMs) in local projects. The platform allows for seamless customization to meet individual needs and preferences, making it a valuable tool for those looking to leverage the power of LLMs in their projects. By providing an easy-to-use interface and a wide range of customization options, Ollama simplifies the process of incorporating LLMs into local projects, enabling users to unlock their full potential.&lt;/p&gt;

</description>
      <category>ollama</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Medical Subject Headings (MeSH) Into Neo4j</title>
      <dc:creator>Tom Nijhof</dc:creator>
      <pubDate>Wed, 07 Feb 2024 07:49:09 +0000</pubDate>
      <link>https://forem.com/wagenrace/medical-subject-headings-mesh-into-neo4j-3gg2</link>
      <guid>https://forem.com/wagenrace/medical-subject-headings-mesh-into-neo4j-3gg2</guid>
      <description>&lt;p&gt;To make a knowledge graph, it is useful to have a vocabulary in place, which is called an &lt;a href="https://en.wikipedia.org/wiki/Ontology_(information_science)"&gt;ontology&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nlm.nih.gov/mesh/meshhome.html"&gt;The Medical Subject Headings&lt;/a&gt; is one such ontology, which includes many of the medical terms that are currently being used.&lt;br&gt;
It can be &lt;a href="https://www.nlm.nih.gov/databases/download/mesh.html"&gt;downloaded &lt;/a&gt;as an RDF file (N-triples), making it easy to import to &lt;a href="https://neo4j.com/"&gt;Neo4j &lt;/a&gt;with &lt;a href="https://neo4j.com/labs/neosemantics/4.3/"&gt;neosemantics&lt;/a&gt; (n10s).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe80m2hz5u45c3iw7r7uy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe80m2hz5u45c3iw7r7uy.png" alt="Installing n10s in neo4j desktop" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Installing n10s in neo4j desktop&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next three commands will import the 2021 MeSH graph directly into Neo4j. It will take a moment before all 2 million nodes and 4 million relations are loaded in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;    &lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;n10s_unique_uri&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;r:&lt;/span&gt;&lt;span class="n"&gt;Resource&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;ASSERT&lt;/span&gt; &lt;span class="n"&gt;r.uri&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;&lt;span class="ss"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;n10s.graphconfig.init&lt;/span&gt;&lt;span class="ss"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;CALL&lt;/span&gt; &lt;span class="n"&gt;n10s.rdf.import.fetch&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"https://nlmpubs.nlm.nih.gov/projects/mesh/rdf/2021/mesh2021.nt"&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"N-Triples"&lt;/span&gt;&lt;span class="ss"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Exploring the Data
&lt;/h2&gt;

&lt;p&gt;Before I start, I will set the caption to rdfs_&lt;em&gt;label for resources, so the nodes have a name. For ns0_Term, I will use ns0&lt;/em&gt;_prefLabel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pkde1hnhinkg56x8x0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pkde1hnhinkg56x8x0w.png" alt="Naming nodes within Neo4j desktop" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Naming nodes within Neo4j desktop&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's start with the sexiest thing to do — reading the &lt;a href="https://hhs.github.io/meshrdf/"&gt;documentation &lt;/a&gt;of RDF data structure of medical terms used to sort medical papers.&lt;br&gt;
Did I say “sexy”? I meant nerdiest.&lt;/p&gt;

&lt;p&gt;I will not go over the full structure; instead, I will select just two elements I think are interesting to start with. Feel free to disagree.&lt;/p&gt;

&lt;p&gt;The code snippets in this blog are cypher query you can use in Neo4j. It is not needed but might be useful if you want to know how I got the results, or they can serve as an example that my cypher is not optimized, up to standard, etc.&lt;/p&gt;
&lt;h3&gt;
  
  
  Terms, Descriptors, and Concepts
&lt;/h3&gt;

&lt;p&gt;Descriptors, concepts, and terms are very closely related. Descriptors are the broadest — within descriptors, you have concepts (at least one that is the preferred one). Concepts have terms — these terms hold synonyms for the concepts. Each concept has one preferred term, while the descriptor also has one preferred term out of all (see picture below).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;    &lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;q:&lt;/span&gt;&lt;span class="n"&gt;ns0__Term&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="ss"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;n:&lt;/span&gt;&lt;span class="n"&gt;ns0__TopicalDescriptor&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;p:&lt;/span&gt;&lt;span class="n"&gt;ns0__Concept&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;z:&lt;/span&gt;&lt;span class="n"&gt;ns0__Term&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n.rdfs__label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Calcimycin"&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;  
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8acseqyw1mq2r6oloeox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8acseqyw1mq2r6oloeox.png" alt="Relation between descriptor (pink), concepts (green), and terms (blue)" width="800" height="522"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Relation between descriptor (pink), concepts (green), and terms (blue)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Terms are very useful for labeling text. Concepts can define a part that is smaller than the whole descriptor. The descriptor holds the connection to the rest of the graph (tree, other descriptors, SCR, Qualifiers, etc.). I will mainly focus on the descriptors for graph algorithms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tree Structure
&lt;/h3&gt;

&lt;p&gt;All TopicalDescriptor have a link to a tree-number (ns0_&lt;em&gt;treeNumber) and to another TopicalDescriptor (ns0&lt;/em&gt;_broaderDescriptor).&lt;/p&gt;

&lt;p&gt;These two hold very similar information but have one use case where they differ: multiple tree locations.&lt;/p&gt;

&lt;p&gt;A descriptor can be in more than one tree at the same time (like the descriptor “&lt;a href="https://meshb.nlm.nih.gov/record/ui?ui=D005123"&gt;eye&lt;/a&gt;”). Eye has tree number &lt;a href="https://meshb.nlm.nih.gov/record/ui?ui=D005123"&gt;**A01.456.505.420&lt;/a&gt; **as a subcategory of face, and &lt;a href="https://meshb.nlm.nih.gov/record/ui?ui=D005123"&gt;**A09.371&lt;/a&gt; **as a subcategory of Sense Organ. This can give us problems because these two tree numbers do NOT have the same subcategories!&lt;/p&gt;

&lt;p&gt;Eyebrows are part of the eye as part of the face but are NOT part of the eye as part of a sense organ.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8eu83sp8gnggmuajbco.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm8eu83sp8gnggmuajbco.png" alt="Tree overview of Eye in [online MeSH Browser](https://meshb.nlm.nih.gov/record/ui?ui=D005123)" width="559" height="655"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tree overview of Eye in &lt;a href="https://meshb.nlm.nih.gov/record/ui?ui=D005123"&gt;online MeSH Browser&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we use ns0__broaderDescriptor to go back from Eyebrows to the broadest description, we come upon a mistake. The broader description of Eyebrows is Eye, which has two broader descriptions (namely, sense organs and face). As Eyebrows is not a sense organ, this shouldn’t be correct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;    &lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;n:&lt;/span&gt;&lt;span class="n"&gt;ns0__TopicalDescriptor&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:ns0__broaderDescriptor&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;p:&lt;/span&gt;&lt;span class="n"&gt;ns0__TopicalDescriptor&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;n.rdfs__label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Eyebrows"&lt;/span&gt; 
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa76ccb3nb5zqpmhy7al.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa76ccb3nb5zqpmhy7al.png" alt="Sense Organs is found as broader description of Eyebrows" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sense Organs is found as broader description of Eyebrows&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The other way is to go via the tree numbers. This will mean Eyebrows is only connected to one of the two tree numbers of Eye and does NOT have Sense organs as a broader description.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;    &lt;span class="k"&gt;MATCH&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;n:&lt;/span&gt;&lt;span class="n"&gt;ns0__TopicalDescriptor&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:ns0__treeNumber&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;t:&lt;/span&gt;&lt;span class="n"&gt;ns0__TreeNumber&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:ns0__parentTreeNumber&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;p:&lt;/span&gt;&lt;span class="n"&gt;ns0__TreeNumber&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:ns0__treeNumber&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;d:&lt;/span&gt;&lt;span class="n"&gt;ns0__TopicalDescriptor&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;n.rdfs__label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Eyebrows"&lt;/span&gt; 
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F780x0gy0w85yhsltiw98.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F780x0gy0w85yhsltiw98.png" alt="Going via the tree number gives only “Body Regions” and “Integumentary System” as the broadest descriptor" width="800" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Going via the tree number gives only “Body Regions” and “Integumentary System” as the broadest descriptor&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For this reason, I will use ns0_&lt;em&gt;treeNumber to find hierarchical relationships rather than ns0&lt;/em&gt;_broaderDescriptor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In conclusion, using the Medical Subject Headings (MeSH) ontology to create a knowledge graph is highly beneficial. By importing MeSH as an RDF file into Neo4j with neosemantics (n10s), we can easily explore the extensive collection of medical terms and their relationships.&lt;/p&gt;

&lt;p&gt;Descriptors, concepts, and terms are essential components of MeSH. Descriptors encompass broad categories, concepts provide specific definitions within descriptors, and terms offer synonyms for concepts. Understanding the hierarchical structure is crucial for effective graph analysis, with tree numbers being a more reliable way to establish relationships than broader descriptors.&lt;/p&gt;

&lt;p&gt;In summary, MeSH is a valuable resource for constructing medical knowledge graphs. Leveraging its rich information and employing appropriate graph analysis techniques, researchers can gain meaningful insights from medical literature and data.&lt;/p&gt;

</description>
      <category>neo4j</category>
      <category>medical</category>
      <category>mesh</category>
      <category>graphdb</category>
    </item>
  </channel>
</rss>
