<?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: Frank Riccobono</title>
    <description>The latest articles on DEV Community by Frank Riccobono (@aelfric5578).</description>
    <link>https://dev.to/aelfric5578</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F215195%2F0cd22fe5-e464-43be-b017-55736a5c70d7.jpeg</url>
      <title>DEV Community: Frank Riccobono</title>
      <link>https://dev.to/aelfric5578</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aelfric5578"/>
    <language>en</language>
    <item>
      <title>How do you Parse a Tournament?</title>
      <dc:creator>Frank Riccobono</dc:creator>
      <pubDate>Tue, 16 Jun 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/aelfric5578/how-do-you-parse-a-tournament-374k</link>
      <guid>https://dev.to/aelfric5578/how-do-you-parse-a-tournament-374k</guid>
      <description>&lt;p&gt;This article is a continuation of my series on how I created the certificate generator for our local speech and debate league.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://alistaircockburn.com/" rel="noopener noreferrer"&gt;Alistair Cockburn&lt;/a&gt; describes the “walking skeleton” of your system as “a minimal implementation of a system that is functional from end to end”. He suggests you write that first and then expand to cover the edge cases.&lt;/p&gt;

&lt;p&gt;In the previous article, I mentioned that it is possible to export results from the tournament management software (&lt;a href="https://www.tabroom.com" rel="noopener noreferrer"&gt;Tabroom&lt;/a&gt;) as comma-separated value (CSV) text files. In this next part, I’ll talk about how I addressed our core use case by converting those CSV results files into the data model used to render the certificates. Then I’ll start examining how the parser has evolved to address new use cases I didn’t envision in the first few commits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interpreting Results
&lt;/h2&gt;

&lt;p&gt;In a competitive speech tournament, students compete in one of several speaking events. In each event, groups of five to seven students give short performances in front of a judge, who then ranks the competitors according to objective and subjective criteria. Tournaments usually consist of multiple preliminary rounds where students are randomly paired against each other. Students’ final placements are determined by adding up the ranks from each prelim round and applying a tournament-specific set of tiebreakers. At the end of the tournament, we can get the final results for each event in a format like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csvs"&gt;&lt;code&gt;&lt;span class="k"&gt;Ranking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Name&lt;/span&gt; &lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;School&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Ranks&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;Prelims&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;Reciprocals&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;Prelims&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;Points&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;Prelims&lt;/span&gt;
&lt;span class="mf"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"807"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"John Smith"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Some Academy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;4.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;2.50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;286&lt;/span&gt;
&lt;span class="mf"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"804"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Jane Doe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"P.S. 1337"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;5.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;2.33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;278&lt;/span&gt;  
&lt;span class="mf"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"802"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Mary Mack"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"Williams Prep"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;6.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;2.25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mf"&gt;278&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The first three columns are fairly generic. The remaining columns will depend on what set of tiebreakers we decide to use.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Ranking&lt;/code&gt; is the student’s overall placement. In this example, John Smith was ranked first overall, Jane was second, and Mary was third.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Code&lt;/code&gt; is a unique identifier for each entry in the event. We use this to preserve some anonymity while the tournament is in progress.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Name 1&lt;/code&gt; is the student’s full name. You may wonder why it’s &lt;code&gt;Name 1&lt;/code&gt; instead of &lt;code&gt;Name&lt;/code&gt;. We’ll get back to that later.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;School&lt;/code&gt; is the school this student is affiliated with. We are an academic competition, and each student is part of a school team.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full data model consists of several entities. In short, a &lt;code&gt;Tournament&lt;/code&gt; consists of one or more &lt;code&gt;Events&lt;/code&gt;. Each &lt;code&gt;Event&lt;/code&gt; would conclude with one or more &lt;code&gt;Results&lt;/code&gt;. Each &lt;code&gt;Result&lt;/code&gt; is attributed to the &lt;code&gt;School&lt;/code&gt; that the entry attends. The initial structure of the &lt;code&gt;Result&lt;/code&gt; entity is below. It’s a direct translation of the first four columns in the CSV, plus an auto-incrementing primary key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Result&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SEQUENCE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;place&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;School&lt;/span&gt; &lt;span class="n"&gt;school&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// foreign key reference&lt;/span&gt;

    &lt;span class="c1"&gt;// getters and setters omitted for brevity&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Aside: How to Parse CSV
&lt;/h2&gt;

&lt;p&gt;Now that we have sample data, we can consider how to extract it from the file. CSV is a deceptively simple format. Many developers might instinctively reach for the naive approach: read the file line by line and split each line by commas. Some of the fields are wrapped in quotes, so those could be stripped out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;module&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReadFile&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BufferedReader&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBufferedReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nc"&gt;Paths&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/path/to/file.csv"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="nc"&gt;StandardCharsets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;UTF_8&lt;/span&gt;&lt;span class="o"&gt;);){&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;src&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readLine&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;","&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;rank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\""&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;school&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;columns&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
            &lt;span class="c1"&gt;// do something with the results&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;ioe&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// do something to recover from the error&lt;/span&gt;
            &lt;span class="n"&gt;ioe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In very simple cases, this may seem to work, but it breaks down fairly quickly. Consider what happens when the column value contains a comma. For example, we had two schools in our league known as “Convent of the Sacred Heart, NYC” and “Convent of the Sacred Heart, Greenwich”. If we chop off the part of these names after the comma, we will certainly cause some confusion.&lt;/p&gt;

&lt;p&gt;Aside from nested commas, the use of column position is also a limitation of this approach. The header row contains important semantic information about each column, but if we are just splitting strings, we discard that information and become tightly coupled to the &lt;em&gt;order&lt;/em&gt; of information in the file. Since we don’t control the producer of the CSV file, we can’t guarantee every column will always have the same order. I already mentioned that the last few columns will almost certainly differ from one tournament to another. Even if we do control the output order, it means that any new columns we introduce in the future must always be appended to the end to avoid incompatibility.&lt;/p&gt;

&lt;p&gt;A better approach is to use a proper CSV parser. For this project, I chose &lt;a href="https://commons.apache.org/proper/commons-csv/" rel="noopener noreferrer"&gt;Apache Commons CSV&lt;/a&gt; because I had already used it on a prior project. Parsing with this library looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CSVFormat&lt;/span&gt; &lt;span class="no"&gt;CSV_FORMAT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CSVFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CSVFormat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DEFAULT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHeader&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setSkipHeaderRecord&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAllowMissingColumnNames&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;CSVParser&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CSV_FORMAT&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputStream&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CSVRecord&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRecords&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;ranking&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ranking"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Name 1"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ...etc&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;DEFAULT&lt;/code&gt; format assumes a comma (&lt;code&gt;,&lt;/code&gt;) is the delimiter, a double quote (&lt;code&gt;"&lt;/code&gt;) is used to surround values that might contain the delimiter, and Windows-style line endings (&lt;code&gt;\r\n&lt;/code&gt;) are used to separate rows. The last three directives instruct the parser to interpret the first row as named headers, allowing you to access values by name rather than by index.&lt;/p&gt;

&lt;p&gt;One limitation, though, is that the &lt;code&gt;CSVRecord&lt;/code&gt; class only returns string values, so I also needed to use &lt;code&gt;Integer.parseInt(record.get("Ranking"))&lt;/code&gt; to get the numeric value for &lt;code&gt;place&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s in a &lt;code&gt;Name 1&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;Back in 2020, I hadn’t read any Cockburn, but I had built a decent walking skeleton all the same. The first iteration of the software successfully processed all the results from our first speech tournament and generated certificates without issue, but it promptly fell over after our second tournament a week later.&lt;/p&gt;

&lt;p&gt;I mentioned students can compete in multiple events, each of which has a somewhat different format. Our second tournament of the year was the first one that season to offer an event called Duo Interpretation. In this event, as the name suggests, teams of two students perform a short acting piece together. When it came time to ingest the results of this event, I noticed that the certificates only displayed the names of one of the two team members.&lt;/p&gt;

&lt;p&gt;It turned out that in partner events, &lt;code&gt;Name 1&lt;/code&gt; would contain the name of one partner, and a new column, &lt;code&gt;Name 2&lt;/code&gt;, would contain the other partner’s name. Because I was already using column names rather than indexes, the fix was localized.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getHeaderNames&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Name 2"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csvRecord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Name 1"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;" &amp;amp; "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;csvRecord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Name 2"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csvRecord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Name 1"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Note that, as a secondary benefit of referencing columns by name instead of index, it didn’t matter that sometimes there was another column between &lt;code&gt;Name 1&lt;/code&gt; and &lt;code&gt;School&lt;/code&gt;. Without this, we would have seen even stranger results, such as a certificate rewarding John Smith from the school of Jane Doe.&lt;/p&gt;

&lt;h2&gt;
  
  
  When is a Place not a Place?
&lt;/h2&gt;

&lt;p&gt;My troubles did not end there. Even though we use various tiebreakers, there are still sometimes unbreakable ties. When all is said and done, two students or teams might have received exactly the same ranks throughout the day. What I learned from this tournament was that when an unbreakable tie happens, the &lt;code&gt;Ranking&lt;/code&gt; column in the results export indicates this with a prefix. For example, if students were tied for second place, they would both be ranked as “T-2”. When this “T-2” value appeared in a result for the first time, my naive &lt;code&gt;Integer.parseInt(ranking)&lt;/code&gt; failed with a &lt;code&gt;NumberFormatException&lt;/code&gt;. I had assumed &lt;code&gt;Ranking&lt;/code&gt; was a numeric field, but the export treated it as richer information that encoded both the placement and whether that placement was tied.&lt;/p&gt;

&lt;p&gt;The tactical fix for this was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;place&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseInt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;csvRecord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Ranking"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"T-"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Is That Your Final Answer?
&lt;/h2&gt;

&lt;p&gt;Unfortunately, this tournament was not done teaching me lessons. In larger events, we held a final round wherein the top 6 to 8 students competed against each other for final placement. However, in the smaller events, we did not hold a final round. Instead, we calculated the final places based purely on preliminary round scores. When we got the results, I learned that some of the columns had completely different names when exporting the results from an event that did not have a final round versus one that did. For example, in some cases &lt;code&gt;Ranking&lt;/code&gt; was &lt;code&gt;Place&lt;/code&gt;. In other cases, there was neither a &lt;code&gt;Name 1&lt;/code&gt; nor &lt;code&gt;Name 2&lt;/code&gt; column, but rather an already combined &lt;code&gt;Name&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;This variability led me to introduce a utility method to return the value for the first matching column name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getOrAlternateColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CSVRecord&lt;/span&gt; &lt;span class="n"&gt;csvRecord&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csvRecord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isMapped&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;)){&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;csvRecord&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not find any of "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
        &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



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

&lt;p&gt;I mentioned that CSV is a deceptively simple format. Definitely don’t parse it with string splitting, but, even if you do have column names, they may not guarantee a stable contract. In the end, the hardest part of this project wasn’t parsing the file, but understanding the assumptions behind the data. What seemed like an easy challenge from a bird’s-eye view ultimately exposed the need to handle several data quirks and special cases.&lt;/p&gt;

&lt;p&gt;Thankfully, because this logic was encapsulated in the parser, I was able to make these changes on the fly while limiting most of the complexity to the application boundary. The parser needed to evolve, but the internal data model remained stable.&lt;/p&gt;

</description>
      <category>java</category>
      <category>softwareengineering</category>
      <category>csv</category>
    </item>
    <item>
      <title>But What About the Trophies?</title>
      <dc:creator>Frank Riccobono</dc:creator>
      <pubDate>Sat, 25 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/aelfric5578/but-what-about-the-trophies-5bp1</link>
      <guid>https://dev.to/aelfric5578/but-what-about-the-trophies-5bp1</guid>
      <description>&lt;h2&gt;
  
  
  Backstory
&lt;/h2&gt;

&lt;p&gt;It was the fall of 2020. A new school year had just started, but amidst the continuing pandemic safety measures, we in the speech and debate community were wondering whether we would be able to continue the activity in some form or another. Thanks to a video platform developed that summer by the National Speech and Debate Association, we were fairly confident we could hold virtual tournaments, but we had another practical issue: how would we recognize the winners?&lt;/p&gt;

&lt;p&gt;Speech and debate is primarily an educational activity. We don’t encourage students to participate for the trophies, but at the same time, doing well at a tournament is difficult. We still wanted to reward these students for their success. Trophies were the league’s largest expense when running in-person tournaments, and mailing them out after virtual tournaments would have been prohibitive.&lt;/p&gt;

&lt;p&gt;Our league director came to me with an idea: certificates. More specifically, we needed a way to generate hundreds of printable certificates automatically, personalize them, and distribute them digitally. Here’s how I did it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layout
&lt;/h2&gt;

&lt;p&gt;Maybe I was just biased from years of working in web development, but when I looked at the sample certificates we had printed, I saw a familiar box model.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxxuwsudue9rz4jm2e7ej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxxuwsudue9rz4jm2e7ej.png" alt="The original reference image for a certificate" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The layout was primarily centered text with some borders and spacing. I decided to use HTML and CSS. A certificate can be represented as just a few lines of text and markup. The structure was straightforward.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"certificate_border gold finalist"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"certificate_body placement_certificate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"The tournament logo"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/nycfl-logo.svg"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100px"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nycfl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;New York Catholic Forensic League&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"award_certificate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Award Certificate&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tournament_details"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"host"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Regis High School&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tournament_name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;NYCFL First Regis&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tournament_date"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;September 26, 2020&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;        
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"place"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;First Place&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"before_text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This certificate is given to&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Jane Doe&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"before_text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;to recognize outstanding performance in the category of&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"event_name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Dramatic Performance&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"signature_block"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"signature"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Thomas Beck&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"position"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;NYCFL President &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"date_block"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"signature-date"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;09/26/2020&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"date-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Date&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;I found a royalty-free guilloché border vector image and found a few suitable web fonts with the right look. My reference certificate was a Word Document based on a print template from an office supply store. When translating a design into HTML/CSS, we usually use units like pixels or REMs, but in this case, it was easier to use inches, a less commonly used unit in web design. That allowed me to make the full size of a certificate to be based on US Letter paper size (8.5 x 11 inches).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8.5in&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;11in&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5in&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;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fany1megzlg3fpix57y4r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fany1megzlg3fpix57y4r.png" alt="Example certificate" width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because certificates are fixed-size documents, HTML print styles made it easy to produce consistent, printable layouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Templates
&lt;/h2&gt;

&lt;p&gt;The core idea was to extract data from &lt;a href="https://www.tabroom.com" rel="noopener noreferrer"&gt;Tabroom&lt;/a&gt;, the software we used to run tournaments and use it to generate the certificates. Tabroom was built with a programmer’s mindset and makes much of its data available as comma-separated-value (CSV) exports. There were several types of reports we wanted to generate from this data so I decided the best starting point was to parse the files once and import the data into a database.&lt;/p&gt;

&lt;p&gt;The basic workflow would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Import CSV results&lt;/li&gt;
&lt;li&gt;Store in database for reuse&lt;/li&gt;
&lt;li&gt;Render certificates using a template&lt;/li&gt;
&lt;li&gt;Export as printable HTML/PDF&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I decided to use &lt;a href="https://quarkus.io" rel="noopener noreferrer"&gt;Quarkus&lt;/a&gt; to scaffold the application along with its Qute templating language. Its quick startup time, developer tooling, and simple Jakarta EE API implementations made it a great choice for building a prototype that could evolve into the final product.&lt;/p&gt;

&lt;p&gt;Students compete in one of several categories. For each category, I would get a final results CSV file that looked something like this. I used the Apache Commons CSV library to parse the files.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ranking&lt;/th&gt;
&lt;th&gt;Code&lt;/th&gt;
&lt;th&gt;Name 1&lt;/th&gt;
&lt;th&gt;School&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;508&lt;/td&gt;
&lt;td&gt;Jane Doe&lt;/td&gt;
&lt;td&gt;Some Academy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;512&lt;/td&gt;
&lt;td&gt;John Smith&lt;/td&gt;
&lt;td&gt;P.S. 1337&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Since I would be the one generating the certificates, I used an H2 file-based database. We were all working remotely so both the application and the database file lived on my desktop. When I needed to generate something, I could start up the application using Quarkus development mode.&lt;/p&gt;

&lt;p&gt;To interact with the application, I created a REST API that could power a small React front end. Below is a simplified version of the JAX-RS resource. Most of the endpoints are general CRUD operations. These endpoints provided for the following workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create a tournament with basic information like the name, date, and virtual host school&lt;/li&gt;
&lt;li&gt;create a list of events in the tournament&lt;/li&gt;
&lt;li&gt;upload the CSV results for each event&lt;/li&gt;
&lt;li&gt;render the certificates into the template
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Consumes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Produces&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;APPLICATION_JSON&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CertificatesResource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Inject&lt;/span&gt;
    &lt;span class="nc"&gt;Template&lt;/span&gt; &lt;span class="n"&gt;certificate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@POST&lt;/span&gt;
    &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/tournaments"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Tournament&lt;/span&gt; &lt;span class="nf"&gt;createTournament&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Tournament&lt;/span&gt; &lt;span class="n"&gt;tournament&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@POST&lt;/span&gt;
    &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/events"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Tournament&lt;/span&gt; &lt;span class="nf"&gt;createEvents&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EventList&lt;/span&gt; &lt;span class="n"&gt;eventList&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@POST&lt;/span&gt;
    &lt;span class="nd"&gt;@Consumes&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MULTIPART_FORM_DATA&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/tournaments/{tournamentId}/events/{eventId}/results"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Tournament&lt;/span&gt; &lt;span class="nf"&gt;addResults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@MultipartForm&lt;/span&gt; &lt;span class="nc"&gt;MultipartBody&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@PathParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"eventId"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;eventId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@PathParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"tournamentId"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;tournamentId&lt;/span&gt;
    &lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;
    &lt;span class="nd"&gt;@Produces&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MediaType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TEXT_HTML&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/tournaments/{id}/certificates"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;generateCertificates&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;tournamentId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;generateCertificates&lt;/code&gt; endpoint would render the Qute template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;'stylesheet'&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;'/certs.css'&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
{#for event in tournament.events}
{#for result in event.results}
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"overflow: hidden; height: 8.5in; width: 11in; padding: 0.5in"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"certificate_border {result.certColor}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"certificate_body"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"/nycfl-logo.svg"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"100px"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"nycfl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;New York Catholic Forensic League&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"award_certificate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Award Certificate&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tournament_details"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"host"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{tournament.host}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tournament_name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{tournament.name}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"tournament_date"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{tournament.longDate}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"place"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{result.placeString}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"before_text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;This certificate is given to&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{result.name}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"before_text"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;to recognize outstanding performance in the category of&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"event_name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{event.name}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"signature_block"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"signature"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Thomas Beck&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"position"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;NYCFL President&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"date_block"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"signature-date"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{tournament.shortDate}&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"date-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Date&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"page-break-after: always; visibility: hidden"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
{/for}
{/for}

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

&lt;/div&gt;



&lt;p&gt;In the first version of this, I had several border images to produce different placements. Gold for first place, silver for second, bronze for third, red for fourth through sixth place, and light blue for any other finalists. I quickly realized that it would be beneficial to have more flexibility to support more placements and custom designs without maintaining multiple static images. Thankfully, since the border was an SVG file, I could also turn &lt;em&gt;it&lt;/em&gt; into a Qute template as well and add one more endpoint that would adjust colors in the SVG.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GET&lt;/span&gt;  
&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/background.svg"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
&lt;span class="nd"&gt;@Produces&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"image/svg+xml"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  
&lt;span class="nd"&gt;@PermitAll&lt;/span&gt;  
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getBackgroundImage&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;  
    &lt;span class="nd"&gt;@QueryParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"color"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nd"&gt;@DefaultValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ffffff"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;  
    &lt;span class="nd"&gt;@QueryParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"color2"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nd"&gt;@DefaultValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"323131"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;color2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;  
    &lt;span class="nd"&gt;@QueryParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"color3"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nd"&gt;@DefaultValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"323131"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;color3&lt;/span&gt;  
&lt;span class="o"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;And we could reference this endpoint in CSS like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.certificate_border.gold&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('/certs/background.svg?color=D4AF37')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.certificate_border.black&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('/certs/background.svg?color=D8D8FF')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.certificate_border.silver&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('/certs/background.svg?color=C0C0C0')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.certificate_border.bronze&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('/certs/background.svg?color=CD7F32')&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.certificate_border.red&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url('/certs/background.svg?color=C76C67')&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;Finally, since the certificates were rendered as HTML, generating PDFs was straightforward. I used Chrome’s built-in “Print to PDF” feature, which handled layout and pagination reliably without needing additional libraries or tooling. While this wasn’t fully automated, it was simple and dependable—and more than sufficient for our needs. For ease of downloading, I uploaded the resulting PDF to an Amazon S3 bucket because the file was too large to email.&lt;/p&gt;

&lt;p&gt;On September 30th, I sent out the first set of certificates for our first online speech tournament, 45 pages altogether, and it was just the beginning. Soon, I needed to accommodate events with other formats, larger tournaments with quarterfinals and semifinals, tournaments for other leagues, and customization for other purposes. The strength of the original implementation was how easy it was to evolve. I’ve since generated certificates and slides (more on that later) for over 150 tournaments, and the software has continued to evolve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTML and CSS work well for print layouts.&lt;/strong&gt; Browsers provided a reliable and flexible way to generate certificates without specialized tooling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Templates make scaling easy.&lt;/strong&gt; Once the certificate layout was templated, generating hundreds of certificates became trivial.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SVGs can be treated as templates too.&lt;/strong&gt; Converting the border into a template made it easy to customize designs without maintaining multiple assets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual steps can be the right starting point.&lt;/strong&gt; Using Chrome’s Print to PDF feature was simple, dependable, and fast to implement.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple systems can go a long way.&lt;/strong&gt; A lightweight architecture was enough to support over 150 tournaments and six years of iterative feature development.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>html</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>A Programmer's Journey</title>
      <dc:creator>Frank Riccobono</dc:creator>
      <pubDate>Sat, 20 Jul 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/aelfric5578/a-programmers-journey-1bd</link>
      <guid>https://dev.to/aelfric5578/a-programmers-journey-1bd</guid>
      <description>&lt;p&gt;I was fortunate to be introduced to computers at an early age. According to an old school report I found in my hoard, we got our first family computer when I was four. You probably wouldn't take my dad for your typical computer geek, but he latched onto the technology when it started transforming the workplace. He, in turn, taught me the basics. By the time I was five years old, I already knew a handful of basic DOS commands. (At that point it was limited to switching directories and launching games – &lt;a href="https://en.wikipedia.org/wiki/Reader_Rabbit" rel="noopener noreferrer"&gt;Reader Rabbit&lt;/a&gt; was an early favorite). By eight, I was using Microsoft Office to write silly short stories and PowerPoint to make “art."&lt;/p&gt;

&lt;p&gt;I started programming almost by accident. Although I liked school and my teachers, I wasn't quite a star student in my earliest primary education. I hated coloring inside the lines, my penmanship was atrocious, and I've always tended towards absent-mindedness. I would forget to do homework or frequently leave books at home. That changed once I got to third grade and even more so by fourth and fifth. That's the point where school became less about learning behaviors and more about acquiring knowledge. I was (and still am) insatiably curious, and this new chapter of education was what I had been waiting for. I was also very impatient. I would look ahead in my textbooks, anxious for the next lesson even before the current one had fully sunken in. What was worse was that many of our books were longer than we'd be able to cover in a school year. I found myself particularly drawn to the final chapters that we would otherwise never see.&lt;/p&gt;

&lt;p&gt;One day in fifth grade, something in the appendix of my math textbook caught my eye. It was an extra credit assignment of sorts written in a mono-spaced font. The instructions said to type the instructions into a program called QBasic, which at that time was still bundled with Windows. I don't quite remember what the program was, but it was something simple like a coin flip simulator. Whatever it was, when I entered the instructions, it worked! I was experiencing some new kind of magic that I didn't fully appreciate at the time. I flipped through the book and found a few more extended code listings and tried them out. I was exclusively following the textbook instructions, though. I had no idea what any of the keywords meant, and it never occurred to me to change anything or to try to write my own program.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn8ellnpsygaf0wr4eyoq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn8ellnpsygaf0wr4eyoq.png" alt="Fake QBasic Screenshot" width="640" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;BASIC...the entrypoint for many a programmer&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Another joy at that time was the annual Scholastic Book Fair (I’m a nerd if you haven’t figured that out by now). A year or so after my first foray into QBasic, I found a book and accompanying CD at the book fair called&lt;a href="https://www.kotaku.com.au/2016/04/heres-the-movie-interplay-made-in-1998-about-learning-to-program-games/" rel="noopener noreferrer"&gt;“Learn to Program BASIC”&lt;/a&gt; by Interplay. In it, a bizarre-looking cartoon character named Media Man( reminiscent of Clippy) taught you through wacky video clips how to write a dialect of BASIC developed by the game company. I learned about strings, subroutines, sprites, arrays, and much more. I still didn't stray far from the tutorials, but I was able to internalize much of the material. In particular, programming in an XY-coordinate system gave me a leg up in math class when we started learning how to plot various graphs. It also helped in Computers Class where Sr. Blanche was introducing us to Logo.&lt;/p&gt;

&lt;p&gt;LTPB felt more like a video game than a useful tool, though, and I lost interest when I’d finished all the lessons. I still never thought to try creating something original, and I did not do much more than that until another coincidence occurred in high school. I was chatting with a friend at the end of freshman year, and we somehow got into the topic of programming. His father was a programmer, and he was learning Visual Basic 6. I'd never heard of it, but it had Basic in the name, so I assumed it was similar to what I'd seen before. He agreed to teach two other friends and me. The four of us had bold plans to create a video game company that summer, powered by VB6. Those plans never materialized, but I did learn the language and my friend, the ringleader, did write an Apple Catcher game that "went viral" in the sophomore class (before "going viral" was a thing).&lt;/p&gt;

&lt;p&gt;Part of the problem with our ill-fated video game company endeavor was that we couldn’t agree on what type of game to make. I had suggested an RPG, not imagining at the time, how much work was involved. I spent the summer learning a bunch of things including how to do Bit Block Transfers (BitBlt) to remove the backgrounds from sprites (something I missed about LTPB), how to generate tile-based maps, and, most-ambitiously, how to implement the A* path-finding algorithm. When that last one (mostly) worked, I was ecstatic. Mind you, at that time, I still had no sort of formal training.&lt;/p&gt;

&lt;p&gt;Throughout the rest of high school, I dabbled in various other technical things. I learned HTML/CSS in the age of CssZenGarden. I set up a personal website on Tripod and even wrote a few Perl-CGI scripts to automate some of the maintenance of it. My dad found this browser-based 3D game engine from Wild Tangent that allowed you to write in Javascript. I built a few simple websites for a theater company I was working with at the time. One of my favorite TV shows at the time, &lt;a href="https://en.wikipedia.org/wiki/The_Screen_Savers" rel="noopener noreferrer"&gt;&lt;em&gt;The Screensavers&lt;/em&gt;&lt;/a&gt;, introduced me to Linux and I started playing around with that in my spare time. It was all a hobby for me. I had no idea that I could make a career out of it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2y6q1tuilfvw89rpoa2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2y6q1tuilfvw89rpoa2y.png" alt="My First Website" width="640" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;My first website...what was I thinking?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When the college admissions process started in my junior year, I received an advertisement for a summer program at one of the schools I was considering (Stevens Institute of Technology). The program allowed high school students to take an online college-level class over the summer. I signed up for CS115, Introduction to Computer Science. I didn't know what "computer science" was at the time, but the syllabus mentioned programming, and that was enough for me. The class involved learning Java, which had enough in common with VB6 that I was able to pick it up pretty quickly. That class convinced me to choose the heretofore unknown Computer Science as my major when the time came.&lt;/p&gt;

&lt;p&gt;I ended up attending Stevens for college. It was a blur of languages including C, PHP, Assembly, Scheme, Matlab, MySQL, and even a smidge of COBOL. Like most graduates, I thought all that was everything I needed to know about programming. One concept I later learned was conspicuously missing from the CS course load was the idea of "Legacy Code" and how to work with it.&lt;/p&gt;

&lt;p&gt;My first job out of college had me working with a several-years-old Java project that had been cobbled together by outsourced contractors. I can't say too much about the experience due to confidentiality agreements. It suffices to say that academic knowledge of Java did not prepare me for J2EE, or for making sense of tens of thousands of lines of code and a bunch of terrible variable names. I was not given a lot of guidance since our lead developer worked remotely and only at night. And my conservative inclination left me afraid to change much. I did eventually get to the point where I could contribute to the project. I was writing software for a business, but I had no real sense of what business software engineering entailed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxovof9k0uzixzog8e0l.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxovof9k0uzixzog8e0l.jpg" alt="Programming in the Real World" width="640" height="381"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Programming in the "Real World"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The next milestone didn’t come until three years later. There were two sources. First, I was taking some graduate-level classes in Natural Language Processing, a topic that had long interested me. As bad as corporate code can get, academic code is generally worse. PhDs and grad students typically write software for an upcoming paper and, unless their discipline is software engineering, they usually don't write with maintenance in mind. "Battle-scarred" as I was by the corporate world, I rejected this writing style and strove to try to make any code I wrote for those courses as clean as possible. This goal required reading lots of tutorials outside of class, but I think it paid off. Most of those courses required us to code in Python, and I made an effort to learn the Pythonic way. For the first time, I wasn’t just thinking about whether or not my code worked but also how it was structured.&lt;/p&gt;

&lt;p&gt;The other influence was a part-time gig I took on in between grad school semesters. One of my college professors contacted me regarding an opportunity with a startup looking for an intern. They were developing a Ruby on Rails e-commerce site. Ruby and Python had enough similarities that I was able to pick up the former pretty quickly, but Rails was eye-opening. I had never used a framework. I was fascinated by tools that not only reduced boilerplate code, but that also imposed structure on the application.&lt;/p&gt;

&lt;p&gt;On top of that, the team was also using Capistrano to automate building and deploying the app, and they had AUTOMATED UNIT TESTS through rake! I had used simple makefiles in my college C-programming days, but, for a technology aimed at creating efficiencies, programming and testing of programs always seemed very manual to me. My college professors had even convinced us not to use IDEs when learning because they introduced too much magic. The idea that you could introduce automation and tooling seemed revolutionary.&lt;/p&gt;

&lt;p&gt;In the end, the startup didn't work out (long story for another time, but no hard feelings toward anyone involved). In the meantime, I found myself in the lead developer role for that software product I had started working on after college. The time had come to put some of what I'd recently learned to use. Over the next few years, a junior developer and I worked to wrangle the legacy code. We imposed structure where there was disorder and wrote automated tests where there had been none. It was exciting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3fgn57o01lx83y5udna4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3fgn57o01lx83y5udna4.png" alt="Test-Driven Development" width="640" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All Hail Test-Driven Development!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today we’re working on a new system. We’re using technologies like Hibernate in the back-end and React in the front-end. It's been a bit of a paradigm shift from never using any frameworks. There are various challenges and successes every day, but the best part is there is always plenty to learn. I can still be that curious kid, stealing furtive glances at advanced topics, that I was when I started.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
    </item>
  </channel>
</rss>
