<?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: Leonardo Cumplido</title>
    <description>The latest articles on DEV Community by Leonardo Cumplido (@leocumpli21).</description>
    <link>https://dev.to/leocumpli21</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%2F644769%2F1e2b859a-01ee-4d0c-ba10-8da9a0e79a0f.jpg</url>
      <title>DEV Community: Leonardo Cumplido</title>
      <link>https://dev.to/leocumpli21</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/leocumpli21"/>
    <language>en</language>
    <item>
      <title>My experience in Summer of Bitcoin 2022</title>
      <dc:creator>Leonardo Cumplido</dc:creator>
      <pubDate>Thu, 25 Aug 2022 04:29:00 +0000</pubDate>
      <link>https://dev.to/leocumpli21/my-experience-in-summer-of-bitcoin-2022-4c4i</link>
      <guid>https://dev.to/leocumpli21/my-experience-in-summer-of-bitcoin-2022-4c4i</guid>
      <description>&lt;p&gt;&lt;strong&gt;Hi! I’m Leonardo Cumplido&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since May 23rd, 2022, till August 15th I worked as a data science assistant in &lt;strong&gt;LN Capital&lt;/strong&gt;, a startup that built &lt;u&gt;Torq&lt;/u&gt;, a capital management software for routing nodes. This opportunity was possible thanks to &lt;a href="https://www.summerofbitcoin.org/"&gt;&lt;strong&gt;Summer of Bitcoin&lt;/strong&gt;&lt;/a&gt; (SoB) internship program. SoB is &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a global, online summer internship program focused on introducing university students to bitcoin open-source development and design.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;SoB has come to an end, so I'm wrapping up my experience in the program in this post. I hope you like it, and that it encourages you in some way to apply maybe next year 😃&lt;/p&gt;

&lt;h2&gt;
  
  
  Admission process
&lt;/h2&gt;

&lt;p&gt;When I got to know SoB, thanks to Adi, the SoB founder, I didn’t really know much about Bitcoin, and nothing about the Lightning Network (LN). I decided it was a good idea to explore the different organizations and projects I could apply to. &lt;br&gt;
At the very beginning, I was kind of lost. I didn’t really know where to start, because almost every project required some skills I didn’t have. Then, I found the right project for me: &lt;em&gt;Routing node research and automation&lt;/em&gt;, from LN Capital 🚀&lt;/p&gt;

&lt;h3&gt;
  
  
  The reason I liked this project
&lt;/h3&gt;

&lt;p&gt;Currently, I’m studying a Bachelor of Science in data science in &lt;strong&gt;Tecnológico de Monterrey&lt;/strong&gt; 🐏. I’d love to become a data scientist because I like how you can find solutions to business problems, and how you can predict future behaviors, based on data, by manipulating that data with smart algorithms that come from a pure mathematical background 😁&lt;/p&gt;

&lt;p&gt;The project from LN Capital fitted my interests and current skills at that moment. The requirements where to know Python 🐍, have experience with Jupyter and Pandas, and have basic analytical skills and some experience cleaning and wrangling data into useful features 💻. During my winter break I took a data science course in Coursera platform, and also read some books about Pandas library. So, I had just acquired those skills. It fitted like a glove.&lt;/p&gt;

&lt;h3&gt;
  
  
  Competency test
&lt;/h3&gt;

&lt;p&gt;Before writing my project proposal, a competency test was asked to be done 😨. It involved some basic data manipulation to calculate some things regarding the LN. Although the coding part didn’t bring trouble to me, learning about the LN did. As I mentioned already, I knew nothing about it then, so I began to learn what it is, what it is used for, why it’s a game changer in the Bitcoin ecosystem, and more technical stuff around how it works. &lt;/p&gt;

&lt;h3&gt;
  
  
  Project proposal
&lt;/h3&gt;

&lt;p&gt;After completing the competency test and getting good feedback, I began my project proposal. It is titled &lt;em&gt;Clustering and outlier detection for tagging, automation and alerts&lt;/em&gt;, and you can find it here: &lt;a href="https://docs.google.com/document/d/1rAoizpIDd-Jxct5f0Y5wV-H5q7iXYrZCmu-wk_NlKgI/edit?usp=sharing"&gt;Project proposal&lt;/a&gt;. The main objective was to produce interesting articles about the procedures, findings, and insights obtained from the data analysis. &lt;/p&gt;

&lt;p&gt;Henrik Skogstrøm, the CEO of LN Capital and &lt;strong&gt;the mentor&lt;/strong&gt; of the project, liked my proposal, and after some days, I was told by him that I was accepted as a SoB intern at LN Capital ⚡ ⚡ ⚡&lt;/p&gt;

&lt;h2&gt;
  
  
  First steps
&lt;/h2&gt;

&lt;p&gt;The internship began on May 23rd. At that time, I was still attending classes at my university, so the first month of the program was a little chaotic for me. During that month I kept learning about the LN, specifically about routing nodes, their channels, and the transactions behavior in the network. I was handed 5 databases containing data from a real routing node, as well as data representing all the transactions that node has participated in. I began by exploring the data to create new features involving the channels behavior of the node. These features were useful for a more concise analysis, and the writing of an article.&lt;/p&gt;

&lt;p&gt;One thing I loved about the SoB program is that they didn’t just connect us with the organizations and then left us alone, but they also offered weekly seminars about Bitcoin. So, with LN Capital I got to know the LN, and with the weekly seminars I learned about Bitcoin in a more deeply way than I could imagine learning by myself. &lt;/p&gt;

&lt;p&gt;When my courses were over, I had plenty of time to concentrate on my work and really get the most out of the internship.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clustering task and first article written
&lt;/h2&gt;

&lt;p&gt;This was for me probably the most interesting work I did during SoB. It consisted of searching for a suitable clustering algorithm to group the channels of our routing node based on their features similarities. I liked this task for several reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I was able to explore and apply different ML models to a dataset.&lt;/li&gt;
&lt;li&gt;After selecting the appropriate ML model, I managed to visualize the clusters in a way that helped me understand why the model was giving these outputs.
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--upDGdogV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mfdozfkeadpm6fzgl4u8.png" alt="Visualizing clusters" width="880" height="342"&gt; &lt;/li&gt;
&lt;li&gt;Previous work was retaken to help gain insights from the clusters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QIxSmwbN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v4ezqdj2bkdiu2k62xbk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QIxSmwbN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v4ezqdj2bkdiu2k62xbk.png" alt="Chart of the behaviour of 3 clusters" width="720" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I wrote a first article, titled &lt;em&gt;Increasing our lightning node activity&lt;/em&gt;, explaining the whole process, the whys of the analysis, the outcomes, and the conclusions. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UwBb3aXq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wqvks95znqetjfbccsbv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UwBb3aXq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wqvks95znqetjfbccsbv.png" alt="Article" width="539" height="679"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I published the article in my LinkedIn profile, where I reached 1,764 impressions, and in &lt;a href="https://twitter.com/LeonardoCM219"&gt;my Twitter account&lt;/a&gt;, where I reached 5,737 impressions. If you want to read the article, you can find it here: &lt;a href="https://drive.google.com/file/d/1G954CT7QNUrNHyatrPKDEUzB72HqmUWD/view?usp=sharing"&gt;Increasing our lightning node activity&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rest of my work
&lt;/h2&gt;

&lt;p&gt;After writing the mentioned article, the middle evaluation period began. I uploaded all my work, in form of Jupyter notebooks, to a repository in &lt;a href="https://github.com/LeoCumpli21"&gt;my GitHub account&lt;/a&gt;. I passed the evaluation, and this gave me confidence to keep on working with the same quality as the last task. &lt;/p&gt;

&lt;p&gt;From then onwards, I worked on creating interactive visualizations of payment routes. First, I used Altair library for building a cool way of visualizing one or more payment attempts, whether they’ve been successful or not. This is useful because you can see which paths are taken more often, which are being the most successful ones, which channels charge the most fees, which nodes might not be reliable based on their forwarding behavior, etc. I wrote an article on this here: &lt;a href="https://dev.to/leocumpli21/visualizing-lightning-network-payments-1pfi"&gt;Visualizing Lightning Network payments&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then, Henrik asked me to search for a way of visualizing the potential paths when rebalancing a channel. We find this interesting because it would be great to have a tool that helps node owners visualize potential paths to rebalance their channels. With it, they could select which paths to probe by filtering and sorting the data of the different possible routes. You can find the article I wrote about this here: &lt;a href="https://dev.to/leocumpli21/visualizing-potential-paths-for-rebalancing-channels-in-the-lightning-network-1d8l"&gt;Visualizing Lightning Network payments&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overall experience
&lt;/h2&gt;

&lt;p&gt;I loved Summer of Bitcoin. I had the opportunity to begin a journey working with an emerging technology like is Bitcoin. I really like what LN Capital is building with Torq, and I really enjoyed my role with them during this internship. I learned a lot from these months, and I think I couldn’t have a better first experience working in a real project as someone analyzing data and extracting important features from it, to gain insights that help a user make better decisions with its routing node. &lt;/p&gt;

&lt;p&gt;I’m incredibly grateful with Henrik, my mentor. He had been supportive the whole duration of the internship. What I’ve done wouldn’t have been possible without his mentoring. &lt;/p&gt;

&lt;p&gt;Now that I got to know these technologies, I'm eager to learn more about them, and to continue this journey. Thanks SoB and LN Capital for giving me the opportunity to learn and grow as a developer, and as a person.&lt;/p&gt;

&lt;h2&gt;
  
  
  How can you contribute to LN Capital
&lt;/h2&gt;

&lt;p&gt;You can start by joining their Telegram group at ln.capital/telegram, and asking anything there. It's a very active group and Henrik is always solving doubts regarding Torq. &lt;br&gt;
You could also DM them in &lt;a href="http://twitter.com/ln_capital"&gt;their Twitter account&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Those could be the first steps in reaching LN Capital. If you're a node owner, then you could try Torq, and point all the things you consider are the best features, the weakest features that can be improved, and the things that are not still there that could be very useful for managing your node's data. All kind of input is welcomed and encouraged. Remember that Torq is a product in development, so having opinions on the product is very good for it to be improved.&lt;/p&gt;

&lt;p&gt;On the other hand, if you are a developer and want to contribute to an open-source project, you could visit Torq's repository on &lt;a href="https://github.com/lncapital/torq"&gt;GitHub&lt;/a&gt; and start exploring the way it's built. &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Z-dvNDbj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/otl8lokfktifedqd5276.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Z-dvNDbj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/otl8lokfktifedqd5276.png" alt="Torq repo" width="274" height="68"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The main programming languages used for Torq are TypeScript and Go. Check it out!&lt;/p&gt;

</description>
      <category>internship</category>
      <category>summerofbitcoin</category>
      <category>lightningnetwork</category>
    </item>
    <item>
      <title>Visualizing potential paths for rebalancing channels in the Lightning Network</title>
      <dc:creator>Leonardo Cumplido</dc:creator>
      <pubDate>Mon, 15 Aug 2022 16:14:30 +0000</pubDate>
      <link>https://dev.to/leocumpli21/visualizing-potential-paths-for-rebalancing-channels-in-the-lightning-network-1d8l</link>
      <guid>https://dev.to/leocumpli21/visualizing-potential-paths-for-rebalancing-channels-in-the-lightning-network-1d8l</guid>
      <description>&lt;p&gt;Once again, working with &lt;a href="https://twitter.com/LN_Capital"&gt;LN Capital&lt;/a&gt; under &lt;a href="https://twitter.com/summerofbitcoin"&gt;Summer of Bitcoin&lt;/a&gt; 2022 program, we were able to produce interesting visualizations for selecting potential paths for a payment in the Lightning Network (LN).&lt;/p&gt;

&lt;p&gt;Recall that the LN is a payment channel network (PCN) built on top of Bitcoin. If you have a LN node, then you can send, receive, and forward payments through the network. Here, we are going to focus on sending payments. &lt;/p&gt;

&lt;p&gt;When you send a payment to another node, data gets back to you when the payment rather succeeds or fails. You are able to see the route of the payment, the fees in each channel, and in which channel, if any, the payment failed. &lt;/p&gt;

&lt;p&gt;You can also send a payment to yourself for rebalancing purposes. Rebalancing is needed when the inbound and outbound capacities of a channel become unbalanced. One way to rebalance your channels is creating a circular route, i.e. sending a payment from your node back to your node.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rqbKkawc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qpzdvhr4joamburn2c9s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rqbKkawc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qpzdvhr4joamburn2c9s.png" alt="rebalancing example" width="880" height="527"&gt;&lt;/a&gt;Figure 1: Circular route rebalancing. Image taken from Matering the Lightning Network book, p. 139. &lt;/p&gt;

&lt;p&gt;It would be great to have a tool that helps node owners visualize potential paths to rebalance their channels. With it, they could select which paths to probe by filtering and sorting the data of the different possible routes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visualization tool
&lt;/h2&gt;

&lt;p&gt;This time, we decided the best way to easily visualize this was with interactive tables, where you can filter, sort, and select data to display the full route you're interested in.  &lt;/p&gt;

&lt;p&gt;The tool consists of 4 tables. The first one is for selecting a node you want to include in the rebalancing (central node), and the channel you want to send the payment from. The second table is for selecting a route that starts from the channel you selected before, and ends in the central node. Third one is for selecting the channel where you want to receive the payment. Finally, the fourth table is for selecting a route from the central node to your node, ending in the channel you selected right before.&lt;/p&gt;

&lt;p&gt;For this task, the 3 main libraries used were &lt;code&gt;networkx&lt;/code&gt;, &lt;code&gt;pandas&lt;/code&gt;, and &lt;code&gt;dash&lt;/code&gt;. &lt;code&gt;network&lt;/code&gt; was used for computing the simple paths from one node to the rest in the network. &lt;code&gt;pandas&lt;/code&gt; was used for creating the suitable dataframes, that later were converted into interactive tables using &lt;code&gt;dash&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  First table
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xYL4d2pS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8f93xxrvgabb8n6j5zu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xYL4d2pS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8f93xxrvgabb8n6j5zu6.png" alt="Table 1" width="880" height="166"&gt;&lt;/a&gt;Figure 2: Table 1 for selecting central node and outgoing channel. &lt;/p&gt;

&lt;p&gt;This table has 3 columns: &lt;br&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;source&lt;/em&gt;: to choose your node&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;central_node&lt;/em&gt;: to choose a certain node you want to include in the rebalancing path&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Channel_out&lt;/em&gt;: to choose the channel you want to send the payment from.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Second table
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QDFxwRfv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fe2tctsatnbdqzd1seul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QDFxwRfv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fe2tctsatnbdqzd1seul.png" alt="Table 2" width="880" height="368"&gt;&lt;/a&gt;Figure 3: Table 2 for selecting a path to the central node. &lt;/p&gt;

&lt;p&gt;This table is more interesting. All columns support filtering and sorting. It's an easy way to see which routes are cheaper, which take fewer hops, and which have a higher overall capacity.&lt;/p&gt;

&lt;p&gt;Here is part of the code to build this table: &lt;br&gt;&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="o"&gt;@&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'secondtable-container'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'children'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'first-table'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'selected_row_ids'&lt;/span&gt;&lt;span class="p"&gt;),&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;make_second_table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;selected_rows&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;selected_rows&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;source_central&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;channel_out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;' -&amp;gt; '&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;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;selected_rows&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;source_central&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;channel_out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;masks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grouped_routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;peers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_central&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;grouped_routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;channel_out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;channel_out&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;second_dff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grouped_routes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;masks&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="n"&gt;dash_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DataTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'second-table'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'name'&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="s"&gt;'id'&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="s"&gt;'deletable'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&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;second_dff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;columns&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;'id'&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;'channel_out'&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;second_dff&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'records'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;editable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# cells are not editable
&lt;/span&gt;            &lt;span class="n"&gt;filter_action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'native'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# filter by entering text in the input box
&lt;/span&gt;            &lt;span class="n"&gt;sort_action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'native'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;sort_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'multi'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;row_selectable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'single'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  
            &lt;span class="n"&gt;page_action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'native'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;page_current&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;page_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Div&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'thirdtable-container'&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;
  
  
  Third table
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hW7ilwKH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1o6nn4nnbzqq5jnaqjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hW7ilwKH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1o6nn4nnbzqq5jnaqjq.png" alt="Table 3" width="880" height="165"&gt;&lt;/a&gt;Figure 4: Table 3 for selecting the channel where you want to receive the payment. &lt;/p&gt;

&lt;p&gt;The third table is similar to the first one. It's only purpose is showing the channels you have, in order to select the one where you want to receive the payment, hence completing the circular path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fourth table
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eHUxczcd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bbjtdmoh62nhnbijuyda.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eHUxczcd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bbjtdmoh62nhnbijuyda.png" alt="Table 4" width="880" height="391"&gt;&lt;/a&gt;Figure 5: Table 4 for selecting the route back to your node. &lt;/p&gt;

&lt;p&gt;The final table is this one, similar to the second one but for selecting an appropriate route, based on your criteria, to complete the rebalancing path.&lt;/p&gt;

&lt;p&gt;After selecting a row, a new container displays the whole information of the circular path, like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Dx190hiH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/no0m5iww0fa03wb4taa2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Dx190hiH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/no0m5iww0fa03wb4taa2.png" alt="Final Container" width="880" height="91"&gt;&lt;/a&gt;Figure 6: The aggregated data of the whole path. &lt;/p&gt;

&lt;p&gt;Here is a full demonstration of the tables functionality:&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XvqtccmT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpylu37tqpitbxct0eg9.gif" alt="Image description" width="880" height="495"&gt;
&lt;/h2&gt;

&lt;p&gt;The value of having a tool like this is being tested. Feel free to leave in the comments whether this would help you in some way with your lightning node.&lt;/p&gt;

</description>
      <category>lightningnetwork</category>
      <category>datavisualizations</category>
    </item>
    <item>
      <title>Visualizing Lightning Network payments</title>
      <dc:creator>Leonardo Cumplido</dc:creator>
      <pubDate>Mon, 25 Jul 2022 13:38:00 +0000</pubDate>
      <link>https://dev.to/leocumpli21/visualizing-lightning-network-payments-1pfi</link>
      <guid>https://dev.to/leocumpli21/visualizing-lightning-network-payments-1pfi</guid>
      <description>&lt;p&gt;As Andreas M. Antonopoulos, Olaoluwa Osuntokun, and René Pickhardt explain in &lt;em&gt;Matering the Lightning Network&lt;/em&gt; (2021) book:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Lightning Network (LN) is a second layer peer-to-peer network that allows us to make Bitcoin [near-instantaneous] payments "off-chain".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you have a LN node, then you can send, receive and route payments through the network. When you send a payment to a node you don't have a channel with, then a path is found to route the payment. Multiple paths are tried in a payment attempt until one succeeds or all fail. All payment attempts come with data related to the different routes the payment tried in order to get to its destination.&lt;/p&gt;

&lt;p&gt;Being able to analyse this data might lead to valuable insights around what channels/nodes perform well and are reliable, to whom it might be okay to create a channel with, which directions present more activity, the effects of the chosen fee limit in a payment attempt, etc.&lt;/p&gt;

&lt;p&gt;Working alongside &lt;a href="https://twitter.com/LN_Capital" rel="noopener noreferrer"&gt;LN Capital&lt;/a&gt;, under Summer of Bitcoin 2022 internship, mentored by Henrik Skogstrøm, we were able to produce valuable visualizations that represent payment attempts in the LN. All data used for this task is real.&lt;/p&gt;




&lt;h2&gt;
  
  
  Visualizing payments with networkx
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Undirected graph
&lt;/h3&gt;

&lt;p&gt;One tool that's often used for graph analysis and visualization with python is &lt;code&gt;networkx&lt;/code&gt;. Networkx provides built-in algorithms for setting the position of the nodes for when the network is displayed. Let's look at two payment attempts drawn using &lt;code&gt;networkx&lt;/code&gt;, and go through the elements of both charts.&lt;/p&gt;

&lt;p&gt;The code for displaying the following figures goes something like this:&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;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# G is our graph. It already has the edges and nodes attributes.
&lt;/span&gt;&lt;span class="n"&gt;widths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;weight&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;span class="n"&gt;colors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;tab:red&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;black&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;edges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;fail_weight&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spring_layout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# positions for all nodes - seed for reproducibi
&lt;/span&gt;
&lt;span class="c1"&gt;# nodes
&lt;/span&gt;&lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw_networkx_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# edges
&lt;/span&gt;&lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw_networkx_edges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;widths&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;edge_color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  

&lt;span class="c1"&gt;# labels
&lt;/span&gt;&lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw_networkx_labels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;G&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font_family&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sans-serif&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gca&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;ax&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;margins&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.08&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;axis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;off&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tight_layout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source node is hidden, and the destination node is &lt;em&gt;WalletOfSatoshi.com&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tr&gt;
&lt;td&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%2Fuz6lkdvd3nlfb4l9lb33.png" alt="Networkx representation of LN failed payment"&gt;Figure 1: Failed payment attempt  &lt;/td&gt;
&lt;td&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%2F181zu0eze395knw68i74.png" alt="Networkx representation of LN successful payment"&gt;Figure 2: Successful payment attempt  &lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Some things you may notice at first glance: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The edges, henceforth we'll call them channels, are coloured differently: they can be black, green or red.&lt;/li&gt;
&lt;li&gt;The channels thickness varies. &lt;/li&gt;
&lt;li&gt;The position of the nodes from both charts has nothing to do with each other. &lt;/li&gt;
&lt;li&gt;In general, both look messy and it's difficult to interpret what they supposedly show.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The color of the channels isn't random. If a payment fails in that given hop, that channel is coloured in red. If a payment succeeds, the successful route is coloured in green. Otherwhise, the channel must be black.&lt;/p&gt;

&lt;p&gt;The channels width also represents data from the payment. The more a channel is used in a payment attempt, the thicker that channel will be.&lt;/p&gt;

&lt;p&gt;We want to achieve the following: combine different payment attempts aggregating their data, and visually seeing this information without struggle. So far, if we do this adapting the code used to display the above figures, it's impossible to search for insights.&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%2F67t7ibnzaq9ofgf8kach.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%2F67t7ibnzaq9ofgf8kach.jpg" alt="Many payment attempts"&gt;&lt;/a&gt;Figure 3: Many payment attempts aggregated in one visualization &lt;/p&gt;

&lt;p&gt;This tells us nothing. There're a lot of improvements to make the visualization readable and valuable, starting with the nodes position.&lt;/p&gt;

&lt;h3&gt;
  
  
  Directed graph
&lt;/h3&gt;

&lt;p&gt;Let's improve the payments visual representation by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding the source node (our node).&lt;/li&gt;
&lt;li&gt;Colouring the source and destination nodes in another color than the rest of the nodes.&lt;/li&gt;
&lt;li&gt;Placing the nodes better to enhance visualization of the routes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As for the last point, let's compare two graph structures when the nodes are well positioned.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;&lt;tr&gt;
&lt;td&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%2F7imqkdk2i2f9q26tpj8f.png" alt="Undirected structure"&gt;Figure 4: Undirected graph structure  &lt;/td&gt;
&lt;td&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%2Fksyf6oc3gwnyluxum7jq.png" alt="Directed graph structure"&gt;Figure 5: Directed graph structure with curved edges  &lt;/td&gt;
&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Figure 4 shows an undirected graph. I've seen that for other payment attempts, some edges overlaped, making it difficult to follow the routes in those payments. That's why a directed graph with curved edges is better for the visualization.&lt;/p&gt;

&lt;p&gt;Improved visualization:&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%2Fjsysflw6oou4esmk7dwd.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%2Fjsysflw6oou4esmk7dwd.png" alt="Directed graph"&gt;&lt;/a&gt;Figure 6: More readable payment attempt visualization &lt;/p&gt;

&lt;p&gt;Now, let's add the channel features Figures 1 and 2 show, plus these ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a channel is red, meaning the payment failed there, and there're still channels in the route to get to the destination, those channels should be coloured in blue. This highlights what channels were never attempted to route the payment.&lt;/li&gt;
&lt;li&gt;Place channel average fees below each channel. Showing this information might lead to useful insights regarding channels behaviour.&lt;/li&gt;
&lt;li&gt;Set node size based on the number of times a node has been in a route.&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%2Fln2prosh0sr5nf1v04w2.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%2Fln2prosh0sr5nf1v04w2.png" alt="Directed graph with more features"&gt;&lt;/a&gt;Figure 7: Visual representation of a payment attempt &lt;/p&gt;

&lt;p&gt;This looks way better. Now you can clearly see who sent the payment, who the destination was, which routes the payment tried, where in those routes the payment failed, which channels were never tried because of a previous failure, which nodes and channels participated the most in this payment attempt, and the fee amount in msats each channel was going to charge if the payment was to be succesful.&lt;/p&gt;

&lt;p&gt;The code for displaying Figures 6 and 7 is:&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;show_graph_of_routes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node_positions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodes_colors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;widths&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                         &lt;span class="n"&gt;edges_colors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;black&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nodes_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;700&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;draw_fee_labels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                         &lt;span class="n"&gt;fee_labels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;figure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;figsize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c1"&gt;# drawing nodes
&lt;/span&gt;    &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw_networkx_nodes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;node_positions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nodes_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
                           &lt;span class="n"&gt;node_color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nodes_colors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# drawing edges
&lt;/span&gt;    &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw_networkx_edges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;node_positions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="n"&gt;connectionstyle&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arc3,rad=-0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# curve the edges
&lt;/span&gt;        &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;widths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;edge_color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;edges_colors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# labels
&lt;/span&gt;    &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw_networkx_labels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;node_positions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nodes_labels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;font_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font_family&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sans-serif&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;font_color&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;purple&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verticalalignment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;top&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;draw_fee_labels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;draw_networkx_edge_labels&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;node_positions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;edge_labels&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fee_labels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                 &lt;span class="n"&gt;font_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;label_pos&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.35&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;plt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Graph Representation of the routes attempted trying to pay an invoice&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interactive visualization with Altair
&lt;/h2&gt;

&lt;p&gt;We already have a nice way to display one payment attempt with &lt;code&gt;networkx&lt;/code&gt;. What if we could visualize more payment attempts aggregated without loosing readability? Moreover, what if we could decide what payments, channels, or nodes to filter out based on their properties? In order to do this, we need to create an interactive visualization.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://altair-viz.github.io/index.html" rel="noopener noreferrer"&gt;Altair&lt;/a&gt; is a data visualization tool for making interactive charts with Python. An advantaje to other visualization libraries is that you can produce visualizations with a minimal amount of code. The hard work is done previously cleaning the data and transforming it into a format that &lt;code&gt;altair&lt;/code&gt; can understand (done with &lt;code&gt;pandas&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Some main ideas to make an interactive visualization for the payment attempts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Menu to select the payments to visualize.&lt;/li&gt;
&lt;li&gt;Zoom in and out.&lt;/li&gt;
&lt;li&gt;Panning.&lt;/li&gt;
&lt;li&gt;Drag the pointer on top of a node to see the node alias, number of times it's been part of a route, and number of failures.&lt;/li&gt;
&lt;li&gt;Drag the pointer on top of a channel to see the average fee of the channel, number of times it's been part of a route, number of failures, and the number of times it's been part of a successful route.&lt;/li&gt;
&lt;li&gt;Filtering by dragging:

&lt;ul&gt;
&lt;li&gt;Brush to select channels based on their payment activity.&lt;/li&gt;
&lt;li&gt;Brush to select nodes based on their payment activity.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This is how the visualization looks:&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%2Fw974exwjtzd8ycpajdrd.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%2Fw974exwjtzd8ycpajdrd.png" alt="Interactive visualization"&gt;&lt;/a&gt;Figure 8: How the interactive visualization looks like &lt;/p&gt;

&lt;h3&gt;
  
  
  Characteristics of the interactive visualization
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Zooming and panning&lt;/strong&gt;&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%2Fjvh1uc3cawnsc1qdcdp1.gif" 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%2Fjvh1uc3cawnsc1qdcdp1.gif" alt="Zoom"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Information displayed when placing the cursor on top of a node or a channel&lt;/strong&gt;&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%2Fcnux1dbysnq0npfpoeis.gif" 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%2Fcnux1dbysnq0npfpoeis.gif" alt="Graph data"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Filter by dragging&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can interact with the two charts at the top, by holding and dragging the cursor to filter out channels or nodes based on how many times they've been part of a route.&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%2Fb1amjheuz63zqusrbpjj.gif" 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%2Fb1amjheuz63zqusrbpjj.gif" alt="Dragging filter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Selecting multiple payments&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The chart at the left is to select which payment/s you want to visualize. When selecting multiple payments, the channels and nodes information updates, based on the aggregated data.&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%2Fbuca1314k04axvox669n.gif" 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%2Fbuca1314k04axvox669n.gif" alt="Filtering by id"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;There are more ideas we want to explore to look for things visually. Transforming data into visual representations of it is always the best way to understand it. &lt;/p&gt;

&lt;p&gt;Any ideas are welcome :)&lt;/p&gt;

</description>
      <category>lightningnetwork</category>
      <category>datascience</category>
      <category>python</category>
      <category>todayilearned</category>
    </item>
    <item>
      <title>Ninth-week check in for PSF, GSoC 2021</title>
      <dc:creator>Leonardo Cumplido</dc:creator>
      <pubDate>Wed, 18 Aug 2021 20:59:00 +0000</pubDate>
      <link>https://dev.to/leocumpli21/ninth-week-check-in-for-psf-gsoc-2021-329a</link>
      <guid>https://dev.to/leocumpli21/ninth-week-check-in-for-psf-gsoc-2021-329a</guid>
      <description>&lt;p&gt;Hi! This is my ninth post of my GSoC 2021 series 😃 &lt;/p&gt;

&lt;p&gt;This week I did 3 things.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I fixed some details of my third milestone PRs. Also, I added the final exercise that was missing in the second challenge. This was pretty straightforward.&lt;/li&gt;
&lt;li&gt;I reviewed the PRs of the other students. &lt;/li&gt;
&lt;li&gt;I had to research about &lt;code&gt;pytest-vcr&lt;/code&gt;, a &lt;cite&gt;plugin for managing VCR.py cassettes&lt;/cite&gt;, and to implement it in 2 tests of the project. See its docs &lt;a href="https://pytest-vcr.readthedocs.io/en/latest/"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To implement &lt;code&gt;pytest-vcr&lt;/code&gt;correctly, I needed to undestand what a decorator is, and what it does, because to use &lt;code&gt;pytest-vcr&lt;/code&gt; tools I had to add decorators to the tests functions. In a nutshell, this was necessary for a couple of tests that make API calls because sometimes this APIs are not active. What &lt;code&gt;pytest-vcr&lt;/code&gt; does is that the fisrt time a test is run, the request to a web page is made, and a &lt;code&gt;.yaml&lt;/code&gt; file is generated with the information of that request. Next time the same test is run, there will not be a web request. Instead, the data will be returned from the &lt;code&gt;.yaml&lt;/code&gt; file. This not only speeds up the tests, but makes them less error prone.&lt;/p&gt;

&lt;p&gt;This week, I can't say I got stuck somewhere, because I didn't. However, I took more time than expected in the &lt;code&gt;pytest-vcr&lt;/code&gt; thing.&lt;/p&gt;

</description>
      <category>gsoc</category>
      <category>python</category>
    </item>
    <item>
      <title>Seventh-week check in for PSF, GSoC 2021</title>
      <dc:creator>Leonardo Cumplido</dc:creator>
      <pubDate>Tue, 20 Jul 2021 15:58:48 +0000</pubDate>
      <link>https://dev.to/leocumpli21/seventh-week-check-in-for-psf-gsoc-2021-2e86</link>
      <guid>https://dev.to/leocumpli21/seventh-week-check-in-for-psf-gsoc-2021-2e86</guid>
      <description>&lt;p&gt;Hi! This weeks check in may be short but interesting. Firstly, I was given great news: I passed the first evaluation 😃. Now, the second period begins, and I plan to keep my good workflow ongoing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did I do this week?
&lt;/h2&gt;

&lt;p&gt;I focused on my &lt;strong&gt;third milestone&lt;/strong&gt;. I made two challenges for the course. The first one is a called &lt;em&gt;Hangman challenge&lt;/em&gt;, and it is about creating a hangman game. The hard thing was coding its web test. I'll explain this later. The second challenge is called &lt;em&gt;Pergamino challenge&lt;/em&gt;, and it is about deciphering a parchment with Python code. This was very straightforward, even its web test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did I get stuck anywhere?
&lt;/h2&gt;

&lt;p&gt;Yes. The playwright test for the hangman challenge was tricky. Interacting with the exercises of this challenge meant dealing with several dialog prompts. I already knew how to accept one dialog prompt:&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sth"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But I had trouble when more than one needed to be handled in the same test. By looking at playwrights documentation I found a solution. First, expect for the dialog event to appear on screen. Then, accept that single dialog once. This, translated to code, is:&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;with&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expect_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;prompt1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sth"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expect_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;prompt2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dialog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dialog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;accept&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"sth"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What is coming up next?
&lt;/h2&gt;

&lt;p&gt;I made two PRs this week. Both are regarding the new challenges. They are still awaiting review. So this week I will tackle the review suggestions, and will review my peers PRs.&lt;/p&gt;

</description>
      <category>gsoc</category>
    </item>
    <item>
      <title>Sixth-week blog post for PSF, GSoC 2021</title>
      <dc:creator>Leonardo Cumplido</dc:creator>
      <pubDate>Tue, 13 Jul 2021 17:47:54 +0000</pubDate>
      <link>https://dev.to/leocumpli21/sixth-week-blog-post-for-psf-gsoc-2021-4i79</link>
      <guid>https://dev.to/leocumpli21/sixth-week-blog-post-for-psf-gsoc-2021-4i79</guid>
      <description>&lt;p&gt;Welcome to my sixth blog post about my experience in Google Summer of Code (GSoC) 2021.  &lt;/p&gt;

&lt;h2&gt;
  
  
  What am I working on
&lt;/h2&gt;

&lt;p&gt;Last week I finished working with my second milestone. This is what I did. There is a lecture in the project called &lt;em&gt;Hackeando con Pyhton&lt;/em&gt; that is supposed to teach how to access an API and retrieve information using pyhton. Before last week, this lecture had only one exercise because others were eliminated due to the APIs they used (They have changed a lot and they are no longer public). Now, this lecture has six new exercises. &lt;/p&gt;

&lt;p&gt;You might know these two python modules to do HTTP requests: &lt;code&gt;urllib&lt;/code&gt; and &lt;code&gt;requests&lt;/code&gt;. For the new exercises I used both, mainly because I encountered a problem with Runestone and I had to change the python interpreter to solve this.&lt;/p&gt;

&lt;p&gt;This week I will be working on my third milestone. &lt;/p&gt;

&lt;h2&gt;
  
  
  What have I struggled with this week?
&lt;/h2&gt;

&lt;p&gt;I had trouble with the &lt;code&gt;dumps&lt;/code&gt; method from the &lt;code&gt;json&lt;/code&gt; module. For an exercise I needed to display on screen the json data retrieved from an API. In order to print it with indentation I passed the &lt;code&gt;indent&lt;/code&gt; argument. Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;print(json.dumps(datos, indent=4))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, it wasn't working. &lt;/p&gt;

&lt;h2&gt;
  
  
  What solutions have I found?
&lt;/h2&gt;

&lt;p&gt;So, I was using &lt;code&gt;requests&lt;/code&gt; module before this. I had to change the python interpreter to &lt;code&gt;brython&lt;/code&gt; for some exercises, because in Brython &lt;code&gt;json.dumps&lt;/code&gt; with the &lt;code&gt;indent&lt;/code&gt; arg works just fine, but Brython doesn't support the &lt;code&gt;requests&lt;/code&gt; module yet. This is why I changed to &lt;code&gt;urllib&lt;/code&gt;. In the Brython's console, the output finally dislplayed nicely with indentation. &lt;/p&gt;

</description>
      <category>gsoc</category>
    </item>
    <item>
      <title>Fifth-week check-in for PSF, GSoC 2021</title>
      <dc:creator>Leonardo Cumplido</dc:creator>
      <pubDate>Sun, 04 Jul 2021 20:15:24 +0000</pubDate>
      <link>https://dev.to/leocumpli21/fifth-week-check-in-for-psf-gsoc-2021-3h5o</link>
      <guid>https://dev.to/leocumpli21/fifth-week-check-in-for-psf-gsoc-2021-3h5o</guid>
      <description>&lt;p&gt;It's been a month since the coding period started. The upcoming week in particular is important because it's the last week before the first evaluation period, meaning I have to deliver everything I've worked on for the past month.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did I do this week?
&lt;/h2&gt;

&lt;p&gt;I finally finished my &lt;a href="https://github.com/LeoCumpli21/PyZombis/milestone/1"&gt;first milestone&lt;/a&gt;. In total, for this milestone I opened 21 issues, and closed 16 so far. The missing 5 have linked pull requests (PRs) that are awaiting review.&lt;/p&gt;

&lt;p&gt;For 2 lectures I had to use &lt;a href="https://brython.info/static_doc/en/import.html"&gt;Brython&lt;/a&gt; for their exercises. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Brython is designed to replace Javascript as the scripting language for the Web. As such, it is a Python 3 implementation (you can take it for a test drive through a web console), adapted to the HTML5 environment, that is to say with an interface to the DOM objects and events.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I use &lt;strong&gt;Brython&lt;/strong&gt; beacause the current python interpreter of Runestone is &lt;a href="https://skulpt.org/"&gt;skulpt&lt;/a&gt;, and it doesn't support some python libraries that Brython does. So this past week I had to recall how Brython works. I had to read its &lt;a href="https://brython.info/static_doc/en/intro.html"&gt;documentation&lt;/a&gt; again, where I found the &lt;code&gt;browser.timer&lt;/code&gt; module, which I found helpful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Did I get stuck anywhere?
&lt;/h2&gt;

&lt;p&gt;Not really. The Brython stuff took time to do, but I didn't feel stuck.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is coming up next?
&lt;/h2&gt;

&lt;p&gt;This fifth week I will be aware of my existing PRs in case the mentors suggest changes. Meanwhile, I'll continue with my second milestone.&lt;/p&gt;

</description>
      <category>python</category>
      <category>gsoc</category>
    </item>
    <item>
      <title>Fourth-week blog post for PSF, GSoC 2021</title>
      <dc:creator>Leonardo Cumplido</dc:creator>
      <pubDate>Mon, 28 Jun 2021 01:32:07 +0000</pubDate>
      <link>https://dev.to/leocumpli21/fourth-week-blog-post-for-psf-gsoc-2021-2koo</link>
      <guid>https://dev.to/leocumpli21/fourth-week-blog-post-for-psf-gsoc-2021-2koo</guid>
      <description>&lt;p&gt;Once again, I'm writing this as part of my &lt;em&gt;Google Summer of Code&lt;/em&gt; (GSoC) for &lt;em&gt;Python Software Foundation&lt;/em&gt; (PSF). The third week has ended, and along with it I finished my &lt;a href="https://github.com/LeoCumpli21/PyZombis/milestone/1"&gt;first milestone&lt;/a&gt;... Or at least that's what I thought. &lt;a href="https://dev.to/leocumpli21/third-week-check-in-for-gsoc-2021-4jon"&gt;Last post&lt;/a&gt; I told you I was ready to tackle my &lt;a href="https://github.com/LeoCumpli21/PyZombis/milestone/2"&gt;second milestone&lt;/a&gt;, however, I forgot some lectures need to be checked as well. So, I did start my second milestone, but I had to pause it. Let the questions begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  What am I working on?
&lt;/h2&gt;

&lt;p&gt;I worked on 2 different things. First, regarding my second milestone, I worked on two tickets. Before ellaborating on them, let me introduce you to what this milestone is about:&lt;br&gt;
&lt;em&gt;It consists of refactoring Facebook's API lesson. The expected outcome is to have created new exercises with other APIs, public ones so that no special requirements are asked for.&lt;/em&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;a href="https://github.com/PyAr/PyZombis/pull/113"&gt;1st&lt;/a&gt; ticket was a piece of cake. I just had to debug a block of code that involved using Reddit API. &lt;/li&gt;
&lt;li&gt;The &lt;a href="https://github.com/LeoCumpli21/PyZombis/issues/11"&gt;2nd&lt;/a&gt; one wasn't as easy. Here's why:
I had to create 3 exercises from scratch involving &lt;a href="https://tastedive.com/api/similar"&gt;TestDive API&lt;/a&gt;. Here's one of them:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://tastedive.com/api/similar"&lt;/span&gt;
&lt;span class="n"&gt;proxy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://cors.bridged.cc/"&lt;/span&gt;
&lt;span class="n"&gt;parametros&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"q"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Coco"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"info"&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="n"&gt;solicitud&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;proxy&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;api_url&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;parametros&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;datos&lt;/span&gt; &lt;span class="o"&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;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;solicitud&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="n"&gt;resultados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;datos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Similar"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"Results"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&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="s"&gt;"resultados: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;resultados&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;peliculas_similares&lt;/span&gt; &lt;span class="o"&gt;=&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;peli&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;datos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Similar"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"Results"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;peliculas_similares&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;peli&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Name"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="k"&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="s"&gt;"Pelis: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;peliculas_similares&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; len: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;peliculas_similares&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;pixar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;peli&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;datos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"Similar"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s"&gt;"Results"&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;pal&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;peli&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"wTeaser"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pal&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Pixar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;pixar&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="k"&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="s"&gt;"Pixar: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pixar&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I also added &lt;code&gt;unittest&lt;/code&gt; for automatic grading to it. Apart from that, I made a web test with &lt;code&gt;playwright&lt;/code&gt; for this same exercise. I learned about &lt;code&gt;page.keyboard&lt;/code&gt; from &lt;a href="https://playwright.dev/python/docs/api/class-keyboard/"&gt;Playwright&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keyboard provides an api for managing a virtual keyboard. The high level api is keyboard.type(text, **kwargs), which takes raw characters and generates proper keydown, keypress/input, and keyup events on your page.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before this, to press a key or type something I used:&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text=def remplazar_primer_caracter(s):"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Tab"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"text=def remplazar_primer_caracter(s):"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"return s[0] + s[1:].replace(s[0], '*')"&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;p&gt;Notice the first argument, which is the "&lt;a href="https://playwright.dev/docs/core-concepts/#selectors"&gt;selector&lt;/a&gt;", an element to tell playwright where to apply an event. Now, I just have to place the cursor where I want to start typing and, with the &lt;code&gt;keyboard&lt;/code&gt; class, tell it what to do. For example:&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"#ac_l45_5 &amp;gt;&amp;gt; text=parametros = {}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Clear all code
&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Control+A"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;press&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Backspace"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Type something
&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;keyboard&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello world"&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 thing I worked on was fixing 2 lectures. &lt;/p&gt;

&lt;h2&gt;
  
  
  What have I struggled with this week?
&lt;/h2&gt;

&lt;p&gt;Once again, git gave me trouble. I don't know why, but &lt;code&gt;git push&lt;/code&gt; and all its variants decided to not work anymore. Regarding python, my major problem was when I wanted to clear all code with playwright using the &lt;code&gt;ctrl+A&lt;/code&gt; &amp;amp; &lt;code&gt;backspace&lt;/code&gt; keys. &lt;/p&gt;

&lt;h2&gt;
  
  
  What solutions have I found?
&lt;/h2&gt;

&lt;p&gt;For the git problem, I had to install GitHub Desktop to push my local commits to my remote repo. For playwright doubts, I checked its documentation and found the &lt;code&gt;keyboard&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;This is all for now :)&lt;/p&gt;

</description>
      <category>python</category>
      <category>gsoc</category>
    </item>
    <item>
      <title>Third-week check-in for GSoC 2021</title>
      <dc:creator>Leonardo Cumplido</dc:creator>
      <pubDate>Wed, 23 Jun 2021 14:13:26 +0000</pubDate>
      <link>https://dev.to/leocumpli21/third-week-check-in-for-gsoc-2021-4jon</link>
      <guid>https://dev.to/leocumpli21/third-week-check-in-for-gsoc-2021-4jon</guid>
      <description>&lt;p&gt;Hi! Today June 21 I begin my third week for GSoC 2021. I am loving this experience so far. Now, I'm going to answer 3 questions regarding my work last week, what I struggled with, and what I will focus on this week.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did I do this week?
&lt;/h2&gt;

&lt;p&gt;In my last post, I mentioned I was working on fixing the existing quizzes of the project. I kept doing that, and I finished. I started fixing some lectures. By fixing I mean:&lt;br&gt;
        &lt;/p&gt;
&lt;ul&gt;

            &lt;li&gt;Formatting all python code with &lt;code&gt;black&lt;/code&gt;.&lt;/li&gt;

            &lt;li&gt;Changing static snippets to &lt;code&gt;activecode&lt;/code&gt;.&lt;/li&gt;

            &lt;li&gt;Creating some exercises with automating grading using &lt;code&gt;unit test&lt;/code&gt;
&lt;/li&gt;

            &lt;li&gt;Creating some web tests (where needed), with &lt;code&gt;playwright&lt;/code&gt;
&lt;/li&gt;

        &lt;/ul&gt;
&lt;br&gt;
I've already finished fixing 4 lectures. This week I made 8 PRs, some of which were proposing simple changes. 
&lt;h2&gt;
  
  
  Did I get stuck anywhere?
&lt;/h2&gt;

&lt;p&gt;Yes I did. As I've mentioned over the past posts, we use &lt;code&gt;pytest-playwright&lt;/code&gt; as our web tester. I've been coding easy tests for the quizzes, and I thought that was enough. Until I got to program a test for a lecture that involved writing into a prompt box. I got stuck there beacause I didn't know &lt;code&gt;playwright&lt;/code&gt; had a special way to deal with dialog prompts. So I had to search in their documentation for an answer, and I got one. &lt;br&gt;
Here's the piece of code I was searching for:&lt;/p&gt;

&lt;pre&gt;
    &lt;code&gt;
        def test_TWP18_ac_5(page):
            # Deal with prompt box
            def handle_dialog(dialog):
                dialog.accept("una palabra")

           # Code to go to TWP18 page, do the exercise and run it.
                    
           # Fill prompt box
           page.on("dialog", handle_dialog)
    &lt;/code&gt;
&lt;/pre&gt;

&lt;h2&gt;
  
  
  What is coming up next?
&lt;/h2&gt;

&lt;p&gt;Last Saturday (June 19), the team got together in a videocall to discuss what we've done so far and what is coming up. So, now that I've finished with the quizzes and the easy lectures, some of which are awaiting review, I will begin my &lt;b&gt;second milestone&lt;/b&gt;. It consists on refactoring a lecture that involves accesing some APIs. My idea is to fix one existing exercise that uses Reddit API, and to create two exercises using two different public and easy to manipulate API's. Meanwhile, I'll continue searching for ways to make the SQL lecture work.&lt;/p&gt;

</description>
      <category>python</category>
      <category>github</category>
    </item>
    <item>
      <title>Second-week blog post for PSF, GSoC 2021</title>
      <dc:creator>Leonardo Cumplido</dc:creator>
      <pubDate>Mon, 14 Jun 2021 23:22:17 +0000</pubDate>
      <link>https://dev.to/leocumpli21/second-week-blog-post-for-psf-gsoc-2021-484m</link>
      <guid>https://dev.to/leocumpli21/second-week-blog-post-for-psf-gsoc-2021-484m</guid>
      <description>&lt;p&gt;&lt;strong&gt;Hi everyone!&lt;/strong&gt; This is my second post for &lt;a href="https://summerofcode.withgoogle.com/"&gt;Google Summer of Code&lt;/a&gt; (GSoC) 2021. The coding period has started and so far my experience has been most enjoyable. As I stated in my &lt;a href="https://dev.to/leocumpli21/first-week-check-in-for-psf-gsoc-2021-1kfe"&gt;first post&lt;/a&gt;, this week I began working on my first milestone, and I can say I've done wonderfully.&lt;/p&gt;

&lt;h3&gt;
  
  
  What am I working on?
&lt;/h3&gt;

&lt;p&gt;This week, a fellow collaborator (&lt;a href="https://dev.to/ymartinez"&gt;Ybrahin&lt;/a&gt;) and I worked on fixing the existing &lt;code&gt;python&lt;/code&gt; quizzes of the project &lt;a href="https://github.com/PyAr/PyZombis"&gt;Python para Zombis&lt;/a&gt;. We divided the number of quizzes each one would fix; I get to fix 7. From those, I've completed 5. So far, I've done &lt;strong&gt;5 PR's&lt;/strong&gt;, 4 of which have been merged into the master branch, and 1 awaiting review. Completing a quiz fix also involves making a web test with &lt;code&gt;pytest-playwright&lt;/code&gt; for an exercise in that quiz. Moreover, this weekend I did some research about Runestone &lt;a href="https://github.com/RunestoneInteractive/overview/blob/main/_sources/ActiveCode/sql.rst"&gt;supporting SQL&lt;/a&gt; language. This is relevant to us because there is a lesson in the project that is currently in standby due to the &lt;code&gt;sqlite3&lt;/code&gt; python module used in there. To be clear, Runestone doesn't support that module, and that's why this lesson isn't working. &lt;/p&gt;

&lt;p&gt;Do you remember I talked about &lt;code&gt;flake8&lt;/code&gt; in my first post? Well, Ybrahin introduced me to another tool to automate code formatting, named &lt;a href="https://github.com/psf/black"&gt;Black&lt;/a&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Black&lt;/em&gt; is the uncompromising Python code formatter. &lt;br&gt;
&lt;em&gt;Black&lt;/em&gt; makes code review faster by producing the smallest diffs possible.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What have I struggled with this week?
&lt;/h3&gt;

&lt;p&gt;I had no bigger problems than trouble understanding git commands. I am still getting used to working with GitHub, and sometimes I don't know the exact sintax, nor the correct time to execute them. Regarding &lt;code&gt;python&lt;/code&gt;, I haven't struggled.   &lt;/p&gt;

&lt;h3&gt;
  
  
  What solutions have I found?
&lt;/h3&gt;

&lt;p&gt;If any doubts come to me while programming I tend to do one of two things: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask my workmates and mentors.&lt;/li&gt;
&lt;li&gt;Try to find an answer from Google. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get tunned for the next post.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>python</category>
      <category>gsoc</category>
    </item>
    <item>
      <title>First-week check-in for PSF, GSoC 2021</title>
      <dc:creator>Leonardo Cumplido</dc:creator>
      <pubDate>Mon, 07 Jun 2021 19:00:04 +0000</pubDate>
      <link>https://dev.to/leocumpli21/first-week-check-in-for-psf-gsoc-2021-1kfe</link>
      <guid>https://dev.to/leocumpli21/first-week-check-in-for-psf-gsoc-2021-1kfe</guid>
      <description>&lt;p&gt;&lt;strong&gt;Hi!&lt;/strong&gt; I'm Leo, and I'm glad to share that I was accepted into Google Summer of Code (GSoC) 2021, &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a global program focused on bringing more student developers into open source software development. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On May 17th I received the great news: Python Argentina, the organization I applied to, under the umbrella of Python Software Foundation (PSF), accepted me to contribute to their project titled &lt;em&gt;PyZombis&lt;/em&gt;. Since then, all accepted students entered the community bonding period. For three weeks, I've been in touch with my mentors and with two other students. We have discussed the techniques of how we'll be working this GSoC, and most importantly, we have separated our initial proposals into milestones and issues in GitHub.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PyZombis&lt;/em&gt; is intended to be a MOOC to teach Python to Spanish speakers. Here is an overview of &lt;a href="https://summerofcode.withgoogle.com/projects/#5193086193369088"&gt;my proposal&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What did I do this week?
&lt;/h3&gt;

&lt;p&gt;So far, I've done solid progress. As I stated already, I got to know my working team and discussed everything around the project. Also, as I'm new to GitHub, I had to learn its basics. I learned about PEP8, the Style Guide for Python Code, and about a tool that checks whether my code follows those rules, named &lt;code&gt;flake8&lt;/code&gt;. I learned about Playwright as well, a tool to automate tests for web apps. Now, I feel ready to start the coding period.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is coming up next?
&lt;/h3&gt;

&lt;p&gt;Today, June 7th, the coding period starts. I'll be working for 2 months on this open source software project. These first two weeks, I'll focus on completing my first milestone: fixing existing code, adding python quizzes with unit test and adding minimal Playwright tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Did I get stuck anywhere?
&lt;/h3&gt;

&lt;p&gt;Indeed. My second milestone involves using Brython and the Twitter API. My mentor recomended trying to combine both by integrating &lt;a href="https://github.com/bisguzar/twitter-scraper"&gt;twitter-scraper&lt;/a&gt; library in &lt;a href="https://brython.info/static_doc/en/import.html"&gt;Brython&lt;/a&gt;. Unfortunately, everything I have tried so far hasn't worked.  &lt;/p&gt;

</description>
      <category>gsoc</category>
      <category>opensource</category>
      <category>python</category>
    </item>
  </channel>
</rss>
