<?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>DEV Community: Gareth M.</title>
    <description>The latest articles on DEV Community by Gareth M. (@gbafana25).</description>
    <link>https://dev.to/gbafana25</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%2F110665%2Ffe9202a4-5c5c-4c41-8250-73a01239e6e2.png</url>
      <title>DEV Community: Gareth M.</title>
      <link>https://dev.to/gbafana25</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gbafana25"/>
    <language>en</language>
    <item>
      <title>Commosta: share computing resources</title>
      <dc:creator>Gareth M.</dc:creator>
      <pubDate>Thu, 15 Jan 2026 02:18:38 +0000</pubDate>
      <link>https://dev.to/gbafana25/commosta-share-computing-resources-29b8</link>
      <guid>https://dev.to/gbafana25/commosta-share-computing-resources-29b8</guid>
      <description>&lt;p&gt;Learn more &lt;a href="//commosta.io"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>saas</category>
      <category>rust</category>
      <category>marketplace</category>
      <category>angular</category>
    </item>
    <item>
      <title>Displaying air quality data in the terminal</title>
      <dc:creator>Gareth M.</dc:creator>
      <pubDate>Sat, 21 Oct 2023 22:46:37 +0000</pubDate>
      <link>https://dev.to/gbafana25/displaying-air-quality-data-in-the-terminal-4j11</link>
      <guid>https://dev.to/gbafana25/displaying-air-quality-data-in-the-terminal-4j11</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;My local county reports data on the level of several pollutants throughout the area. However, the only way to see this data is via an online dashboard. My project &lt;code&gt;air-monitor&lt;/code&gt; is a Python script that uses the backend APIs of the dashboard to display the same data in the terminal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Obtaining the data
&lt;/h2&gt;

&lt;p&gt;The dashboard makes separate requests for each pollutant type, which are specified in the request parameter &lt;code&gt;where&lt;/code&gt;, along with the desired time span.&lt;br&gt;
The service measures the following pollutant types:&lt;br&gt;
    - sulfur dioxide&lt;br&gt;
    - carbon monoxide&lt;br&gt;
    - Particulate Matter (10 and 2.5)&lt;br&gt;
    - ozone&lt;br&gt;
    - nitrogen oxide&lt;/p&gt;

&lt;p&gt;The data is returned in an array of JSON objects, each of which contains a value as well as the date and time the data was collected&lt;/p&gt;

&lt;p&gt;The are also several sensors placed throughout the county's area, which each generate their own sets of data points. In the web dashboard, these are shown in different colors/lines. In the future, ANSI codes could be used to show multiple data sources. For now, I've opted to use an averaging function that combines pollutants with multiple data sources, which is handled by the &lt;code&gt;chart&lt;/code&gt; library.&lt;/p&gt;
&lt;h2&gt;
  
  
  Graphing the Data
&lt;/h2&gt;

&lt;p&gt;To graph the data, &lt;code&gt;chart.py&lt;/code&gt; uses a numpy matrix that is allocated based on the possible values ranges of a pollutant, since each pollutant is measured with different unit types (ppb, ppm, ug/m^3). Each pollutant type has it's own array size and conversion factors to display. For example, here are the values for CO.&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="s"&gt;"CO"&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ppm"&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="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"{:.2f}"&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="c1"&gt;# ppm
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first value is used as the size for the outer array (the columns), and each of the sub-arrays is allocated according to the number of hours the data covers. The rest of the values are used to round and display the numbers. Then, the values are printed for each interval where a data point exists.&lt;br&gt;
In order to average data from several locations, the data is first arranged into a dictionary with each time slot as a key. Then, any duplicates are averaged together and re-packaged with the same format as the original JSON data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---87JNE3u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1zfexwbhdsipiuiiqwhk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---87JNE3u--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1zfexwbhdsipiuiiqwhk.png" alt="Image description" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;p&gt;The main challenge with this project is getting the data to fit inside the terminal, while also making it legible enough. My goal was for the user to visually be able to match the shape of the graph to the one in the web dashboard, but also fit on smaller displays/terminals with fewer columns. I wanted to make an initial version of this project that just used regular terminal output, without depending on any special ANSI codes. Future updates would include adding colors to the output and adding connecting lines/dots to better show the curve of the graph.&lt;/p&gt;

&lt;p&gt;Project repo: &lt;a href="https://github.com/gbafana25/air-monitor"&gt;https://github.com/gbafana25/air-monitor&lt;/a&gt;&lt;br&gt;
VODs of coding streams: &lt;a href="https://youtube.com/@gbafana25"&gt;https://youtube.com/@gbafana25&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>environment</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building a version control system</title>
      <dc:creator>Gareth M.</dc:creator>
      <pubDate>Sat, 20 May 2023 05:12:26 +0000</pubDate>
      <link>https://dev.to/gbafana25/building-a-version-control-system-2e11</link>
      <guid>https://dev.to/gbafana25/building-a-version-control-system-2e11</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;As developers, we use version control systems everyday. Most are probably with Git or Subversion (SVN). Since it's easy to use these tools without thinking about how they work, I decided to try and make my own version control system.&lt;/p&gt;

&lt;h2&gt;
  
  
  How version control works
&lt;/h2&gt;

&lt;p&gt;A version control system keeps track of the changes (versions) of your code. Whenever you make a commit, the current version of the file(s) are compared to the previous version, and a new "commit" is made. Each control system has various ways of doing this. For example, Git uses a snapshot system, where a completely new copy of every modified file is stored in a commit object [1]. Other systems like SVN (and my project) record the differences between files over time.&lt;/p&gt;

&lt;h2&gt;
  
  
  About zren
&lt;/h2&gt;

&lt;p&gt;When a zren repository is created, a &lt;code&gt;base&lt;/code&gt; directory is made with a copy of every file at that moment. When the first commit is made, it compares the current version of each file with the base version. For every commit after that, the "image" of the current file is first built off of applying each commit to the base file chronologically. Then, the current file is compared to the reconstruction of the previous version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Changefiles
&lt;/h3&gt;

&lt;p&gt;To generate the changefiles, the most recent version of the file is compared with the current version being committed. Every character that is different is stored in a linked list. Places that are the same are given a placeholder, but will not be included in the file itself. When it comes time to write the actual changefile, the linked list is iterated through, and only the different characters and the &lt;em&gt;number&lt;/em&gt; of spaces in between each change is recorded. Hex codes, &lt;code&gt;0x16&lt;/code&gt; for characters and &lt;code&gt;0x15&lt;/code&gt; for space counts are used so the program knows how to read it back and reconstruct the linked list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other features
&lt;/h3&gt;

&lt;p&gt;Since this is just a project I started a few weeks ago, it obviously won't have all the features of a system like Git or SVN. Most importantly, this program currently only keeps track of files locally, since there is no server component for a remote repository. Some of the basic features seen in Git, such as logging and an ignore-file, are present.&lt;/p&gt;

&lt;h2&gt;
  
  
  Branching
&lt;/h2&gt;

&lt;p&gt;New branches are created with the &lt;code&gt;rollback&lt;/code&gt; command, along with its own directory for changefiles and a separate log file. Branch names are autogenerated with the prefix &lt;code&gt;sub-&lt;/code&gt; plus the first 8 characters of the source commit id. Users can switch between branches using the &lt;code&gt;checkout&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Project repo: &lt;a href="https://github.com/gbafana25/zren" rel="noopener noreferrer"&gt;https://github.com/gbafana25/zren&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[1] &lt;a href="https://www.git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F" rel="noopener noreferrer"&gt;https://www.git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F&lt;/a&gt;&lt;/p&gt;

</description>
      <category>c</category>
      <category>linux</category>
      <category>vcs</category>
    </item>
    <item>
      <title>Undocumented APIs in websites</title>
      <dc:creator>Gareth M.</dc:creator>
      <pubDate>Sat, 08 Apr 2023 19:23:52 +0000</pubDate>
      <link>https://dev.to/gbafana25/undocumented-apis-in-websites-42g6</link>
      <guid>https://dev.to/gbafana25/undocumented-apis-in-websites-42g6</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;A website needs to be able to retrieve data, such as information on a set of products or users.  To do this, many websites make requests to APIs, which in turn access data from a database or some other backend service.  Today I'm going to be talking about how I was able to scrape JSON data from an undocumented API, in order to integrate that data into my own application.  &lt;/p&gt;

&lt;h2&gt;
  
  
  The website
&lt;/h2&gt;

&lt;p&gt;Currently I am working on an application that allows users to create a grocery list (&lt;a href="https://github.com/gbafana25/cook-guide"&gt;here&lt;/a&gt;), as well as compare items.  For this I need several pieces of data, such as the name and price of the product(s), as well as nutrition information, ingredients, and images of the product and/or its packaging.  Conveniently, I found all of this information in the API at &lt;code&gt;https://www.bakersplus.com/atlas/v1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrieving the data
&lt;/h2&gt;

&lt;p&gt;Preliminary request to get product ids&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y9etjSL1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/msycwymc6wbdsnyp5hfm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y9etjSL1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/msycwymc6wbdsnyp5hfm.png" alt="First request" width="800" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Secondary request that returns product data&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--koW4YIeT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06uvhwnzvgnrcwzqtewm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--koW4YIeT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/06uvhwnzvgnrcwzqtewm.png" alt="Second request" width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon looking at the web traffic, there are two web requests here that are important.  The first one access the endpoint &lt;code&gt;/search/v1/products-search&lt;/code&gt;, which takes parameters &lt;code&gt;filter.query&lt;/code&gt; and &lt;code&gt;page.size&lt;/code&gt;.  This endpoint returns product id numbers in a JSON object.&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;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;"*/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Host"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s"&gt;"www.bakersplus.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"x-laf-object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_laf_obj&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getProductIds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;#query = "rice"
&lt;/span&gt;    &lt;span class="n"&gt;ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;"/search/v1/products-search?filter.query="&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;"&amp;amp;page.size="&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The second request is made to the &lt;code&gt;/product/v2/products&lt;/code&gt; endpoint.  An array of the product numbers (&lt;code&gt;filter.gtin13s&lt;/code&gt;) is passed as a url parameter.  What we get as the response is all the information we need, although, including some extra info we don't want that is filtered out via a helper function.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getProductInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"filter.gtin13s"&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;ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"filter.gtin13s"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;append&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="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s"&gt;"/product/v2/products"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rate limiting/Request restrictions
&lt;/h2&gt;

&lt;p&gt;APIs, especially when publicly exposed, will limit the amount of consecutive requests.  In my experience with the above example, it is more likely to timeout if the same search is made consecutively.  In addition, this API also required certain HTTP headers to be included, which others did not require.  One of these was called &lt;code&gt;x-laf-object&lt;/code&gt;, which appeared to be some kind of location tracking object.  &lt;/p&gt;

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

&lt;p&gt;Many websites don't make it this easy to scrape their data and effectively bypass their frontend.  However, if the data these services provide is public anyway, and is effectively rate-limited and protected from abuse, then there isn't really a reason to not build a solution that is modular and easier to fix/debug. &lt;/p&gt;

&lt;p&gt;Livestreams/VODs of dev: &lt;a href="https://youtube.com/@gbafana25"&gt;Youtube&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Code: &lt;a href="https://github.com/gbafana25/cook-guide"&gt;Github&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>api</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Tuxsav - backup and save text files</title>
      <dc:creator>Gareth M.</dc:creator>
      <pubDate>Sat, 18 Mar 2023 19:06:09 +0000</pubDate>
      <link>https://dev.to/gbafana25/tuxsav-backup-and-save-text-files-2ec7</link>
      <guid>https://dev.to/gbafana25/tuxsav-backup-and-save-text-files-2ec7</guid>
      <description>&lt;p&gt;TLDR: backs up text files during and after editing w/ Vim swap files.  &lt;a href="https://github.com/gbafana25/tuxsav"&gt;https://github.com/gbafana25/tuxsav&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Hello, this project is still very much in the beginning stages, but the basic idea I had in mind is operational (Link is at the bottom).&lt;/p&gt;

&lt;h2&gt;
  
  
  Design
&lt;/h2&gt;

&lt;p&gt;The repository consists of a Django web application and a C++ client. The Django application exposes endpoints that the client can use to create new documents and update existing ones.&lt;/p&gt;

&lt;p&gt;On a the local machine, the C++ client must be started before opening a file. While the file is still open, it reads the Vim swap file and sends that data to the server. Once the file is closed, it reads the original file and sends its contents one final time. It currently only backups up one file at a time, which is specified in the command line arguments.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Left
&lt;/h2&gt;

&lt;p&gt;I haven't done any styling on the website's frontend, and there are only the bare minimum amount of pages for it to function. I also potentially want the program to backup multiple files at once.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/gbafana25/tuxsav"&gt;https://github.com/gbafana25/tuxsav&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>django</category>
      <category>python</category>
      <category>vim</category>
    </item>
    <item>
      <title>hw-manager: Canvas assignments and todo list application.</title>
      <dc:creator>Gareth M.</dc:creator>
      <pubDate>Fri, 20 Jan 2023 21:55:17 +0000</pubDate>
      <link>https://dev.to/gbafana25/hw-manager-canvas-assignments-and-todo-list-application-3l3i</link>
      <guid>https://dev.to/gbafana25/hw-manager-canvas-assignments-and-todo-list-application-3l3i</guid>
      <description>&lt;h2&gt;
  
  
  About
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://stingray-app-ayu4b.ondigitalocean.app/" rel="noopener noreferrer"&gt;hw-manager&lt;/a&gt; is a Flask application that helps organize class assignments and has a basic todo list feature.  It uses the &lt;a href="https://canvas.instructure.com/doc/api/" rel="noopener noreferrer"&gt;Canvas API&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;The API token and URL for your school's Canvas page can be entered on the &lt;a href="https://stingray-app-ayu4b.ondigitalocean.app/setup" rel="noopener noreferrer"&gt;setup&lt;/a&gt; page.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/gbafana25" rel="noopener noreferrer"&gt;
        gbafana25
      &lt;/a&gt; / &lt;a href="https://github.com/gbafana25/hw-manager" rel="noopener noreferrer"&gt;
        hw-manager
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      View Canvas assignments and create to-do list.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;hw-manager&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;Flask-based frontend to view Canvas assignments.
&lt;a href="https://stingray-app-ayu4b.ondigitalocean.app/" rel="nofollow noopener noreferrer"&gt;Try here&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Stored Cookies&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;token&lt;/code&gt;: Canvas API token&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;url&lt;/code&gt;: Canvas page url&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;todo&lt;/code&gt;: array of todo list items&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;removed-classes&lt;/code&gt;: list of course ids to hide&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;TODO:&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;handle server errors when token not found&lt;/li&gt;
&lt;li&gt;remove to do list not found print statement&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/gbafana25/hw-manager" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
    </item>
    <item>
      <title>typetester - typing practice for the command line</title>
      <dc:creator>Gareth M.</dc:creator>
      <pubDate>Thu, 28 Jul 2022 23:35:00 +0000</pubDate>
      <link>https://dev.to/gbafana25/typetester-typing-practice-for-the-command-line-1hbl</link>
      <guid>https://dev.to/gbafana25/typetester-typing-practice-for-the-command-line-1hbl</guid>
      <description>&lt;p&gt;Typetester lets you to practice typing from the command line.  Text passages are loaded into a text file, and statistics are generated after every run&lt;/p&gt;

&lt;p&gt;Add a passage into &lt;code&gt;a.txt&lt;/code&gt;, compile and run &lt;code&gt;./main&lt;/code&gt;.  Once completed, the results will appear like so:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--StuEBtEc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fepjkf7135lbe8ygkphi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--StuEBtEc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fepjkf7135lbe8ygkphi.jpg" alt="stats" width="800" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Terminal Modes
&lt;/h2&gt;

&lt;p&gt;In order to read input, &lt;code&gt;typetester&lt;/code&gt; chnages terminal settings using &lt;code&gt;termios.h&lt;/code&gt;.  This library contains flags that control various functions of the terminal, such as the input processing, output, etc.  A full list of them can be found &lt;a href="https://man7.org/linux/man-pages/man0/termios.h.0p.html"&gt;here&lt;/a&gt;.  For this program, canonical mode and output echo were disabled.  &lt;em&gt;Canonical mode&lt;/em&gt; is the default mode of most terminals.  Input is available line-by-line, usually after the user hits Enter.  This also allows the user to edit the line, which is useful for typing commands.  &lt;/p&gt;

&lt;p&gt;In non-canonical mode, user input is available as soon as the character is typed.  In the main loop of the program, all we have to do is check if each character equals the current character in the passage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setup_terminal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;termios&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;tcgetattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STDIN_FILENO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// enable canonical mode&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_lflag&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ICANON&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="c1"&gt;// disable echo&lt;/span&gt;
        &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_lflag&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;=&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ECHO&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;tcsetattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STDIN_FILENO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TCSAFLUSH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;options&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;
  
  
  ANSI Cursor Controls
&lt;/h2&gt;

&lt;p&gt;When a session starts, the cursor is placed at the beginning of the passage.  In order to get the correct cursor position, we save it by sending &lt;code&gt;ESC 7&lt;/code&gt; to stdout.  Then after printing out the text passage, we restore the cursor position with &lt;code&gt;ESC 8&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// save current cursor position&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STDOUT_FILENO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"\e7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;read_from_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a.txt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// restore saved cursor position&lt;/span&gt;
&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;STDOUT_FILENO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"\e8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I made this project to experiment with terminal settings, in addition to making something that I normally have to access through the browser.  This project can be found at &lt;a href="https://github.com/gbafana25/typetester"&gt;this github repository&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>c</category>
      <category>programming</category>
    </item>
    <item>
      <title>passtwo - mobile password manager</title>
      <dc:creator>Gareth M.</dc:creator>
      <pubDate>Sat, 09 Jul 2022 02:48:00 +0000</pubDate>
      <link>https://dev.to/gbafana25/passtwo-mobile-password-manager-3c5k</link>
      <guid>https://dev.to/gbafana25/passtwo-mobile-password-manager-3c5k</guid>
      <description>&lt;p&gt;&lt;strong&gt;Update: release apk now available &lt;a href="https://github.com/gbafana25/passtwo/releases/tag/v1.0"&gt;here&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Passtwo is my second attempt at making a mobile password manager app.  It is designed to integrate with &lt;a href="https://github.com/gbafana25/passman"&gt;passman&lt;/a&gt;, a python script that uses GPG to store passwords.  You can see how it works in &lt;a href="https://dev.to/gbafana25/passman-a-password-manager-for-the-command-line-64p"&gt;this&lt;/a&gt; DEV article.  This app relies on BouncyCastle to decrypt files.  &lt;/p&gt;

&lt;h2&gt;
  
  
  OAuth Device Flow
&lt;/h2&gt;

&lt;p&gt;This app uses the beta Device Flow for OAuth apps.  First, when the user presses &lt;strong&gt;Request Device Token&lt;/strong&gt;, the app sends a request for a device token containing the OAuth app's Client ID.  The response contains an 8-character authorization code, and the browser automatically opens to type it in (The code is displayed on the app's settings page).  The user will be prompted to login before entering the code.  Finally, if the user has agreed to allow the application, the &lt;strong&gt;Get Token&lt;/strong&gt; button must be pressed in order to finish the authentication process.  &lt;/p&gt;

&lt;h2&gt;
  
  
  User Info
&lt;/h2&gt;

&lt;p&gt;After authenticating, the user simply inputs their github username, the private repo holding their passwords, and their gpg passphrase.  The only thing required in the repo are the folders containing the encrypted passwords, and a gpg private key.  The app currently doesn't support adding new accounts (directly from the app), however I hope to add that in the future since it would be improvement over the previous version.  Each time the user tries to access the &lt;strong&gt;Passwords&lt;/strong&gt; page, they will be prompted to enter their fingerprint or pin before proceeding&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;A release apk can be found &lt;a href="https://github.com/gbafana25/passtwo/releases/tag/v1.0"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>android</category>
      <category>java</category>
      <category>security</category>
    </item>
    <item>
      <title>Passman - a password manager for the command line</title>
      <dc:creator>Gareth M.</dc:creator>
      <pubDate>Mon, 19 Jul 2021 18:03:42 +0000</pubDate>
      <link>https://dev.to/gbafana25/passman-a-password-manager-for-the-command-line-64p</link>
      <guid>https://dev.to/gbafana25/passman-a-password-manager-for-the-command-line-64p</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Passman is a python script that encrypts your passwords with &lt;code&gt;gpg&lt;/code&gt;.  The inspiration for this project came from &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9Yu2o3Wt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://passwordstore.org" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9Yu2o3Wt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://passwordstore.org" alt="pass" width="" height=""&gt;&lt;/a&gt;, a shell script that does the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependencies
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;gpg&lt;/code&gt; and &lt;code&gt;xclip&lt;/code&gt; should be installed.  No external python libraries were used.&lt;/p&gt;

&lt;h2&gt;
  
  
  Structure
&lt;/h2&gt;

&lt;p&gt;Passman stores credentials in the following file structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Website name
       |
       -- username_1.enc
       |
       -- username_2.enc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passwords are copied to the clipboard with &lt;code&gt;xclip&lt;/code&gt;, and are cleared from the clipboard after one paste.  Configuration is done via a json file, which must be named &lt;code&gt;config.json&lt;/code&gt;, and be in the same directory as &lt;code&gt;passman.py&lt;/code&gt;.  Since there are no external dependencies, &lt;code&gt;passman.py&lt;/code&gt; can be moved around and placed wherever, as long as the &lt;code&gt;store_path&lt;/code&gt; variable in &lt;code&gt;config.json&lt;/code&gt; is set correctly.&lt;/p&gt;

</description>
      <category>python</category>
      <category>linux</category>
      <category>gpg</category>
      <category>security</category>
    </item>
    <item>
      <title>Anyone here use Solus Linux</title>
      <dc:creator>Gareth M.</dc:creator>
      <pubDate>Mon, 15 Jul 2019 01:25:23 +0000</pubDate>
      <link>https://dev.to/gbafana25/anyone-here-use-solus-linux-3k35</link>
      <guid>https://dev.to/gbafana25/anyone-here-use-solus-linux-3k35</guid>
      <description>&lt;p&gt;Just wanted to see if anyone used Solus Linux for anything, and what your opinions are on it.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>linux</category>
      <category>solus</category>
    </item>
    <item>
      <title>THE SCRIPT KIDDIE HOLE OF DOOM (aka the ESP8266 honeypot)</title>
      <dc:creator>Gareth M.</dc:creator>
      <pubDate>Sun, 28 Oct 2018 19:41:54 +0000</pubDate>
      <link>https://dev.to/gbafana25/the-script-kiddie-hole-of-doom-aka-the-esp8266-honeypot-492a</link>
      <guid>https://dev.to/gbafana25/the-script-kiddie-hole-of-doom-aka-the-esp8266-honeypot-492a</guid>
      <description>&lt;p&gt;This project is designed to make a simple honeypot that runs a Telnet server.  The server is then port forwarded so it can be accessed by anyone.  I wanted to see if people would still bother scanning or hacking Telnet devices since most scanning tools are automated now.  Please feel free to suggest any additions or changes, v1 is now out (Honeyboi 1).  Project link &lt;a href="https://github.com/gbafana25/esp8266_honeypot"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>micropython</category>
      <category>dweet</category>
      <category>esp8266</category>
      <category>honeypot</category>
    </item>
  </channel>
</rss>
