<?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: Paul Bradley</title>
    <description>The latest articles on DEV Community by Paul Bradley (@nhsdeveloper).</description>
    <link>https://dev.to/nhsdeveloper</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%2F119794%2F5b61047f-7241-425e-bd02-8ba9c6c1829d.jpg</url>
      <title>DEV Community: Paul Bradley</title>
      <link>https://dev.to/nhsdeveloper</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nhsdeveloper"/>
    <language>en</language>
    <item>
      <title>Using the Go AWS API to get AWS CloudWatch Metrics for an S3 bucket.</title>
      <dc:creator>Paul Bradley</dc:creator>
      <pubDate>Sat, 27 May 2023 09:14:25 +0000</pubDate>
      <link>https://dev.to/nhsdeveloper/using-the-go-aws-api-to-get-cloudwatch-metrics-for-an-s3-bucket-50d0</link>
      <guid>https://dev.to/nhsdeveloper/using-the-go-aws-api-to-get-cloudwatch-metrics-for-an-s3-bucket-50d0</guid>
      <description>&lt;p&gt;Earlier this year, one of the largest web applications I've ever developed went live. As part of supporting the organisations using the application I'd built an operations dashboard that lets me see all the stats and logs from the application in one place.&lt;/p&gt;

&lt;p&gt;The application uses a private S3 bucket to store cached files. The S3 bucket has a life cycle policy to delete files twenty hours after being created. I wanted the dashboard to show the count of items in this bucket and the total size of the bucket to keep track of consumption.&lt;/p&gt;

&lt;p&gt;This article explains how I used the Go API (Application Programming Interface) to extract the metrics from &lt;a href="https://aws.amazon.com/cloudwatch"&gt;CloudWatch&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Amazon CloudWatch metrics for Amazon S3 can help you understand and improve the performance of applications that use Amazon S3. There are several ways that you can use CloudWatch with Amazon S3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amazon CloudWatch Daily Storage Metrics for Buckets
&lt;/h2&gt;

&lt;p&gt;Monitor bucket storage using CloudWatch, which collects and processes storage data from Amazon S3 into readable, daily metrics. These storage metrics for Amazon S3 are reported once per day and are provided to all customers at no additional cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Amazon S3 CloudWatch Daily Storage Metrics for Buckets
&lt;/h2&gt;

&lt;p&gt;The CloudWatch &lt;code&gt;AWS/S3&lt;/code&gt; namespace includes the following daily storage metrics for buckets.&lt;/p&gt;

&lt;h3&gt;
  
  
  BucketSizeBytes
&lt;/h3&gt;

&lt;p&gt;The amount of data in bytes stored in a bucket in the STANDARD storage class, INTELLIGENT_TIERING storage class, Standard - Infrequent Access (STANDARD_IA) storage class, OneZone - Infrequent Access (ONEZONE_IA), Reduced Redundancy Storage (RRS) class, Deep Archive Storage (S3 Glacier Deep Archive) class or, Glacier (GLACIER) storage class.&lt;/p&gt;

&lt;p&gt;This value is calculated by summing the size of all objects in the bucket (both current and non-current objects), including the size of all parts for all incomplete multipart uploads to the bucket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Valid storage type filters&lt;/strong&gt;: StandardStorage, IntelligentTieringFAStorage, IntelligentTieringIAStorage, IntelligentTieringAAStorage, IntelligentTieringDAAStorage, StandardIAStorage, StandardIASizeOverhead, StandardIAObjectOverhead, OneZoneIAStorage, OneZoneIASizeOverhead, ReducedRedundancyStorage, GlacierStorage, GlacierStagingStorage, GlacierObjectOverhead, GlacierS3ObjectOverhead, DeepArchiveStorage, DeepArchiveObjectOverhead, DeepArchiveS3ObjectOverhead and, DeepArchiveStagingStorage (see the StorageType dimension) Units: Bytes Valid statistics: Average&lt;/p&gt;

&lt;h2&gt;
  
  
  NumberOfObjects
&lt;/h2&gt;

&lt;p&gt;The total number of objects stored in a bucket for all storage classes except for the GLACIER storage class. This value is calculated by counting all objects in the bucket (both current and noncurrent objects) and the total number of parts for all incomplete multipart uploads to the bucket. Valid storage type filters: AllStorageTypes (see the StorageType dimension) Units: Count Valid statistics: Average&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Go to Obtain S3 Bucket Metrics
&lt;/h2&gt;

&lt;p&gt;The first step is to install the AWS software development kit (SDK) for GoLang. This is done by using the following Go get command issued at the terminal or command prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/aws/aws-sdk-go/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the AWS SDK has been installed, you'll need to import the relevant sections into your program to interact with CloudWatch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"strconv"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/credentials"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/session"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/service/cloudwatch"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the main part of your program, you need to create an AWS session using the &lt;code&gt;NewSession&lt;/code&gt; function. In the example below the newly created session is assigned to the &lt;code&gt;awsSession&lt;/code&gt; variable. The session object gets created by supplying the AWS region identifier and your AWS credentials for &lt;code&gt;Access Key&lt;/code&gt; and &lt;code&gt;Secret Key&lt;/code&gt;. You can obtain these keys from your AWS account. The &lt;code&gt;NewSession&lt;/code&gt; function returns a pointer to the session object, and if there was an error, i.e. you specified an invalid region, then an error struct is returned.&lt;/p&gt;

&lt;p&gt;You should, therefore, check the status of the error variable and handle it appropriately for your use case. In this example, we log the error to the console as a fatal message. &lt;a rel="noopener" href="https://pkg.go.dev/log#Fatal"&gt;Logfatal&lt;/a&gt; equivalent to Print() followed by a call to os.Exit(1); so the program will terminate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;awsSession&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;awsSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSession&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;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"eu-west-2"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Credentials&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewStaticCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AccessKeyID"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SecretAccessKey"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&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;Once you've got a pointer to a valid AWS session you can reuse that session to make multiple calls against the CloudWatch API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Obtaining the S3 Bucket Size Metric
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;getS3BucketSize&lt;/code&gt; function shown below takes one parameter, which is the bucket name for which we want to get the CloudWatch metrics. The function returns three values. The first value is the average data point which contains the bucket size. Secondly, the date/time that CloudWatch logged the value. Finally, the third parameter returns any errors within the function.&lt;/p&gt;

&lt;p&gt;We're requesting the metric for S3 objects stored under the &lt;code&gt;StandardStorage&lt;/code&gt; tier in the example below. If you want the same function to query different storage types, then you'd need to make this an input parameter to the function.&lt;/p&gt;

&lt;p&gt;After getting the result from the &lt;code&gt;GetMetricStatistics&lt;/code&gt; function call, we iterate over the &lt;code&gt;result.Datapoints&lt;/code&gt; getting the results to return in the return values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getS3BucketSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucketName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bucketSize&lt;/span&gt; &lt;span class="kt"&gt;float64&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;metricDateTime&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;

    &lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cloudwatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;awsSession&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMetricStatistics&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;cloudwatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetMetricStatisticsInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MetricName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BucketSizeBytes"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Namespace&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AWS/S3"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;StartTime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;48&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hour&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;EndTime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
        &lt;span class="n"&gt;Period&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Statistics&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Average"&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
        &lt;span class="n"&gt;Dimensions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cloudwatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dimension&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;cloudwatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dimension&lt;/span&gt;&lt;span class="p"&gt;{&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;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BucketName"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;),&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;cloudwatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dimension&lt;/span&gt;&lt;span class="p"&gt;{&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;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"StorageType"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"StandardStorage"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="p"&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;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;err&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;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataPoint&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Datapoints&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;bucketSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dataPoint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Average&lt;/span&gt;
        &lt;span class="n"&gt;metricDateTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dataPoint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timestamp&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;bucketSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metricDateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Obtaining the S3 Bucket Count Metric
&lt;/h2&gt;

&lt;p&gt;To get the count of files/objects stored in the S3 bucket, you'd need to change the &lt;code&gt;MetricName&lt;/code&gt; from &lt;code&gt;BucketSizeBytes&lt;/code&gt; to &lt;code&gt;NumberOfObjects&lt;/code&gt; and change the storage type to &lt;code&gt;AllStorageTypes&lt;/code&gt; as shown in the snippet below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;MetricName&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NumberOfObjects"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Namespace&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AWS/S3"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;StartTime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;48&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Hour&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="n"&gt;EndTime&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="n"&gt;Period&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;Statistics&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Average"&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;

    &lt;span class="n"&gt;Dimensions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cloudwatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dimension&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;cloudwatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dimension&lt;/span&gt;&lt;span class="p"&gt;{&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;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BucketName"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;),&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;cloudwatch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Dimension&lt;/span&gt;&lt;span class="p"&gt;{&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;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"StorageType"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AllStorageTypes"&lt;/span&gt;&lt;span class="p"&gt;),&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;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I'm using these functions within a Linux command-line utility which gets invoked from a &lt;a rel="noopener" href="https://crontab.guru/%22"&gt;cron job&lt;/a&gt; daily. The results from the last thirty days are stored in a database so that I can present the values on my dashboard as a &lt;a rel="noopener" href="https://en.wikipedia.org/wiki/Sparkline%22"&gt;Sparkline&lt;/a&gt; chart. A Sparkline is a small line chart, typically drawn without axes or coordinates. It presents the general shape of the variation and lets me quickly see the growth trends.&lt;/p&gt;

</description>
      <category>go</category>
      <category>aws</category>
      <category>cloudwatch</category>
    </item>
    <item>
      <title>Improving web application performance by using session storage with JavaScript</title>
      <dc:creator>Paul Bradley</dc:creator>
      <pubDate>Wed, 14 Sep 2022 10:21:30 +0000</pubDate>
      <link>https://dev.to/nhsdeveloper/improving-web-application-performance-by-using-session-storage-with-javascript-4oe4</link>
      <guid>https://dev.to/nhsdeveloper/improving-web-application-performance-by-using-session-storage-with-javascript-4oe4</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my day job, I work on a web application that consolidates &lt;a rel="noopener" href="https://aws.amazon.com/blogs/publicsector/breaking-down-patient-data-silos-uk-healthcare-serverless-cloud-technology/"&gt;clinical information from multiple sources&lt;/a&gt;. One of those sources is a &lt;a href="https://www.ihe.net/"&gt;Health Information Exchange&lt;/a&gt;. Depending on the patient, it can take several seconds to return the data for a patient with a complex medical history.&lt;/p&gt;

&lt;p&gt;As such, the data is returned via an AJAX request so that I can present the user with a progress bar while the health information exchange responds. This article demonstrates how I use sessionStorage to cache the results and make the web application feel more responsive to the user.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API"&gt;Web Storage API&lt;/a&gt; provides mechanisms by which browsers can store key/value pairs in a much more intuitive fashion than using cookies. The Web Storage API provides two mechanisms for caching data, localStorage and sessionStorage.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;localStorage&lt;/em&gt; API persists data even when the browser is closed and reopened. It stores data with no expiration date and gets cleared only through JavaScript or clearing the browser cache. As my web application holds clinical data, I didn't want to cache that data between browsing sessions.&lt;/p&gt;

&lt;p&gt;As such, I choose &lt;em&gt;sessionStorage&lt;/em&gt; as it maintains a separate storage area for each given origin. It stores data &lt;strong&gt;only&lt;/strong&gt; for a session, meaning that the information is only stored until the browser (or tab) is closed. The data is never transferred to the server. It also has a much larger storage limit than a cookie, allowing up to 5MB of data to be stored.&lt;/p&gt;

&lt;h2&gt;
  
  
  sessionStorage.getItem
&lt;/h2&gt;

&lt;p&gt;First, we need to use &lt;em&gt;getItem&lt;/em&gt; function to see if anything is already stored in the cache. The &lt;em&gt;getItem&lt;/em&gt; function has one parameter, the key name,&lt;br&gt;
a string containing the name of the key you want to retrieve.&lt;/p&gt;

&lt;p&gt;If the cache is empty, we perform the AJAX request by checking if it equals null. If the cache isn't empty, we load the HTML code from the cache by using the &lt;em&gt;getItem&lt;/em&gt; function and assign it the HTML element with an id attribute of &lt;em&gt;LloyedGeorge&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/David_Lloyd_George"&gt;Lloyed George&lt;/a&gt; was the person known for inventing the &lt;a href="https://pcse.england.nhs.uk/help/gp-record-movement/lloyd-george-envelopes-for-paper-patient-records/"&gt;paper patient records&lt;/a&gt; and as this is a medical application the use of his name as a variable is a hat tip to the man. The placeholder &lt;em&gt;.Patient.LloyedGeorge&lt;/em&gt; in the sample below is replaced with a unique value for each patient. This ensures that if a user is viewing multiple patient records, the cached data for another patient isn't retrieved and displayed incorrectly.&lt;/p&gt;

&lt;p&gt;It's a good idea to use a unique key name that has a meaning within your application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.Patient.LloyedGeorge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;xds_ajax_request&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="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LloyedGeorge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.Patient.LloyedGeorge&lt;/span&gt;&lt;span class="dl"&gt;"&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;
  
  
  sessionStorage.setItem
&lt;/h2&gt;

&lt;p&gt;When our AJAX call responds with a valid result, we assign &lt;em&gt;responseText&lt;/em&gt; to the HTML element with an id attribute of &lt;em&gt;LloyedGeorge&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We also save a copy of the &lt;em&gt;responseText&lt;/em&gt; into the session storage using the &lt;em&gt;setItem&lt;/em&gt; function call. The &lt;em&gt;setItem&lt;/em&gt; function accepts two parameters. The first is a key name, a string containing the name of the key you want to create/update. The second is the key value, a string containing the value you want to give the key you are creating or updating.&lt;/p&gt;

&lt;p&gt;Once we've saved the AJAX response into the session storage, we can refresh the web page, and the AJAX call won't be triggered, as the code example above will load the data directly from the session storage. Again, this makes your application feel fast, as the data will appear immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;xmlHttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readyState&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;xmlHttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LloyedGeorge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;xmlHttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.Patient.LloyedGeorge&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;xmlHttp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;responseText&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;
  
  
  sessionStorage.removeItem
&lt;/h2&gt;

&lt;p&gt;If you decide to use the &lt;em&gt;sessionStorage&lt;/em&gt; API's, you need a way for your users to invalidate the cache, so your application can re-query the AJAX endpoint/service. On a mobile application, this might be a &lt;a href="https://en.wikipedia.org/wiki/Pull-to-refresh"&gt;pull to refresh&lt;/a&gt; gesture, or it could be a button the user needs to press. Either way, the &lt;em&gt;refresh&lt;/em&gt; event should call the &lt;em&gt;removeItem&lt;/em&gt; function to invalidate the cache.&lt;/p&gt;

&lt;p&gt;The &lt;em&gt;removeItem&lt;/em&gt; function has one parameter: the key name for the key you want to remove.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;force_refresh&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.Patient.LloyedGeorge&lt;/span&gt;&lt;span class="dl"&gt;'&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;
  
  
  Need Help or Advice
&lt;/h2&gt;

&lt;p&gt;If you need help or advice on improving your web application performance, don't hesitate to get in touch with me via Twitter. Send me a direct message to &lt;a rel="noopener" href="https://twitter.com/nhsdeveloper"&gt;@nhsdeveloper&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>frontend</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Provisioning an AWS PostgreSQL Aurora RDS Cluster with Babelfish using Terraform</title>
      <dc:creator>Paul Bradley</dc:creator>
      <pubDate>Mon, 05 Sep 2022 07:07:34 +0000</pubDate>
      <link>https://dev.to/nhsdeveloper/provisioning-an-aws-postgresql-aurora-rds-cluster-with-babelfish-using-terraform-5730</link>
      <guid>https://dev.to/nhsdeveloper/provisioning-an-aws-postgresql-aurora-rds-cluster-with-babelfish-using-terraform-5730</guid>
      <description>&lt;h2&gt;
  
  
  Introduction to Babelfish
&lt;/h2&gt;

&lt;p&gt;Migrating an SQL Server database to Amazon Web Services, while possible, can be expensive. Microsoft's SQL Server licensing model is expensive when compared to an open source alternative like PostgreSQL. Thanks to an open-source project called &lt;a href="https://github.com/babelfish-for-postgresql/babelfish-for-postgresql"&gt;Babelfish for PostgreSQL&lt;/a&gt; you can move your SQL Server workloads to the cloud and benefit from the cost efficiencies that PostgreSQL offer.&lt;/p&gt;

&lt;p&gt;Babelfish for PostgreSQL adds a Microsoft SQL Server-compatible end-point to PostgreSQL. Babelfish allows PostgreSQL to understand T-SQL, SQL Server's proprietary SQL dialect, and supports the TDS communication protocol, so applications originally written for SQL Server may work with PostgreSQL with fewer code changes and without changing database drivers.&lt;/p&gt;

&lt;p&gt;Software suppliers will need to test their software using a Babelfish endpoint, but most applications will work with minor changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provisioning an Aurora PostgreSQL Cluster with Babelfish Enabled Using Terraform
&lt;/h2&gt;

&lt;p&gt;Below is a sample Terraform script to provision an RDS Aurora Cluster with a Babelfish endpoint enabled.&lt;br&gt;
I hope you find this useful.&lt;br&gt;
The code is commented, and you can change elements to suit your requirements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_rds_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"rds_cluster_maywoods_audit_tool"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;cluster_identifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"maywoods-audit-tool"&lt;/span&gt;
    &lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"eu-west-2a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-2b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-2c"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;// database engine type, mode and version&lt;/span&gt;
    &lt;span class="nx"&gt;engine&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aurora-postgresql"&lt;/span&gt;
    &lt;span class="nx"&gt;engine_mode&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"provisioned"&lt;/span&gt;
    &lt;span class="nx"&gt;engine_version&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"13.7"&lt;/span&gt;

    &lt;span class="c1"&gt;// database_nam must begin with a letter and &lt;/span&gt;
    &lt;span class="c1"&gt;// contain only alphanumeric characters.&lt;/span&gt;
    &lt;span class="c1"&gt;// ensure a strong long password is used.&lt;/span&gt;
    &lt;span class="nx"&gt;database_name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MaywoodsAuditToolsQA"&lt;/span&gt;
    &lt;span class="nx"&gt;master_password&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"**********************************"&lt;/span&gt;
    &lt;span class="nx"&gt;master_username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MaywoodsAuditAdmin"&lt;/span&gt;

    &lt;span class="nx"&gt;backup_retention_period&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"60"&lt;/span&gt;
    &lt;span class="nx"&gt;copy_tags_to_snapshot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

    &lt;span class="nx"&gt;db_cluster_parameter_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_rds_cluster_parameter_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_cluster_maywoods_audit_tool_pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="nx"&gt;db_subnet_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_db_subnet_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_cluster_maywoods_audit_tool_sng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="nx"&gt;deletion_protection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;enabled_cloudwatch_logs_exports&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"postgresql"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;skip_final_snapshot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flase&lt;/span&gt;
    &lt;span class="nx"&gt;final_snapshot_identifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"maywoods-audit-tool-cluster-final-snapshot"&lt;/span&gt;

    &lt;span class="nx"&gt;iam_database_authentication_enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="nx"&gt;iam_roles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="nx"&gt;preferred_backup_window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"23:00-00:00"&lt;/span&gt;
    &lt;span class="nx"&gt;preferred_maintenance_window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sun:01:00-sun:02:00"&lt;/span&gt;

    &lt;span class="nx"&gt;storage_encrypted&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;kms_key_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws/rds"&lt;/span&gt;

    &lt;span class="nx"&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sg_id_list&lt;/span&gt;

    &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// override any tags already set within the&lt;/span&gt;
        &lt;span class="c1"&gt;// default_tags block with the providers.tf&lt;/span&gt;
        &lt;span class="nx"&gt;Application&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CORE SERVICE - AUDIT TOOL DATABASE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MAYWOODS"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_rds_cluster_instance"&lt;/span&gt; &lt;span class="s2"&gt;"rds_cluster_maywoods_audit_tool_instance"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;cluster_identifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_rds_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_cluster_maywoods_audit_tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

    &lt;span class="c1"&gt;// change instance type/size and count&lt;/span&gt;
    &lt;span class="c1"&gt;// based on the workloads requirements&lt;/span&gt;
    &lt;span class="nx"&gt;instance_class&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"db.r5.large"&lt;/span&gt;
    &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="nx"&gt;identifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"maywoods-audit-tool-inst-0"&lt;/span&gt;

    &lt;span class="c1"&gt;// use the same engine/version as define in the cluster&lt;/span&gt;
    &lt;span class="nx"&gt;engine&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_rds_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_cluster_maywoods_audit_tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine&lt;/span&gt;
    &lt;span class="nx"&gt;engine_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_rds_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_cluster_maywoods_audit_tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;engine_version&lt;/span&gt;

    &lt;span class="nx"&gt;auto_minor_version_upgrade&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;copy_tags_to_snapshot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;db_parameter_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"maywoods-audit-tool-pg"&lt;/span&gt;
    &lt;span class="nx"&gt;db_subnet_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_db_subnet_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_cluster_maywoods_audit_tool_sng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="nx"&gt;monitoring_interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt;
    &lt;span class="nx"&gt;monitoring_role_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
    &lt;span class="nx"&gt;performance_insights_enabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;preferred_maintenance_window&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sun:01:00-sun:02:00"&lt;/span&gt;
    &lt;span class="nx"&gt;publicly_accessible&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

    &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// override any tags already set within the&lt;/span&gt;
        &lt;span class="c1"&gt;// default_tags block with the providers.tf&lt;/span&gt;
        &lt;span class="nx"&gt;Application&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CORE SERVICE - AUDIT TOOL DATABASE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MAYWOODS"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_rds_cluster_parameter_group"&lt;/span&gt; &lt;span class="s2"&gt;"rds_cluster_maywoods_audit_tool_pg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"maywoods-audit-tool-pg"&lt;/span&gt;
    &lt;span class="nx"&gt;family&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aurora-postgresql13"&lt;/span&gt;

    &lt;span class="c1"&gt;# enable babelfish to be active&lt;/span&gt;
    &lt;span class="nx"&gt;parameter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"rds.babelfish_status"&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"on"&lt;/span&gt;
        &lt;span class="nx"&gt;apply_method&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"pending-reboot"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// override any tags already set within the&lt;/span&gt;
        &lt;span class="c1"&gt;// default_tags block with the providers.tf&lt;/span&gt;
        &lt;span class="nx"&gt;Application&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CORE SERVICE - AUDIT TOOL DATABASE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MAYWOODS"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_db_subnet_group"&lt;/span&gt; &lt;span class="s2"&gt;"rds_cluster_maywoods_audit_tool_sng"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name_prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;maywoods-db-subnet"&lt;/span&gt;
    &lt;span class="nx"&gt;subnet_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnet_zero&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnet_one&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Application&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ScR CORE SERVICE - AUDIT TOOL DB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;Provider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MAYWOODS"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_cloudwatch_log_group"&lt;/span&gt; &lt;span class="s2"&gt;"rds_cluster_maywoods_audit_tool_lg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/aws/rds/cluster/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aws_rds_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_cluster_maywoods_audit_tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_identifier&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/postgresql"&lt;/span&gt;
    &lt;span class="nx"&gt;retention_in_days&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Security Groups
&lt;/h2&gt;

&lt;p&gt;To allow servers to connect to the RDS Aurora PostgreSQL Cluster you'll also need to define a security group.&lt;br&gt;
The security group will need to open ports 5432 for native PostgreSQL traffic, and port 1433 for Babelfish traffic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"rds_maywoods_audit_tool_sg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name_prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rds_maywoods_audit_tool_sg"&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Maywoods Audit Tool Database Access"&lt;/span&gt;
    &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&lt;/span&gt;

    &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name_prefix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rds_maywoods_audit_tool_sg"&lt;/span&gt;&lt;span class="p"&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"rds_maywoods_audit_tool_psql_inbound"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_maywoods_audit_tool_sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Ingress PostgreSQL traffic from hospital servers"&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5432&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5432&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.164.33.38/32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"10.164.33.37/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"rds_maywoods_audit_tool_psql_outbound"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_maywoods_audit_tool_sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Egress PostgreSQL traffic from hospital servers"&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"egress"&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5432&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5432&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.164.33.38/32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"10.164.33.37/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"rds_maywoods_audit_tool_babelfish_inbound"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_maywoods_audit_tool_sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Ingress Babelfish traffic from hospital servers"&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ingress"&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1433&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1433&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.164.33.38/32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"10.164.33.37/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group_rule"&lt;/span&gt; &lt;span class="s2"&gt;"rds_maywoods_audit_tool_babelfish_outbound"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;security_group_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rds_maywoods_audit_tool_sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Egress Babelfish traffic from hospital servers"&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"egress"&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1433&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1433&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"10.164.33.38/32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"10.164.33.37/32"&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;



</description>
      <category>aws</category>
      <category>rds</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Using SQLite to generate documentation and software configurations</title>
      <dc:creator>Paul Bradley</dc:creator>
      <pubDate>Thu, 01 Sep 2022 07:54:41 +0000</pubDate>
      <link>https://dev.to/nhsdeveloper/using-sqlite-to-generate-documentation-and-software-configurations-34b5</link>
      <guid>https://dev.to/nhsdeveloper/using-sqlite-to-generate-documentation-and-software-configurations-34b5</guid>
      <description>&lt;h1&gt;
  
  
  Using SQLite to generate documentation and software configurations
&lt;/h1&gt;

&lt;p&gt;Why would you want to use SQLite to maintain documentation? Or use it to manage software configuration files?&lt;/p&gt;

&lt;p&gt;In this article I explain the benefits and demonstrate how to use SQLite to generate documentation.&lt;/p&gt;

&lt;p&gt;I maintain several documents for a large health project. The documents display the same data in different ways depending on the intended audience of the document.&lt;/p&gt;

&lt;p&gt;The first is a list of publishing standards, and the second is a role-based access control matrix. The data set grows and evolves as the project responds to new use cases. Having an automated process for generating the documents from the data is crucial.&lt;/p&gt;

&lt;p&gt;Rather than attempting to maintain the two documents separately and potentially getting them out of synchronisation, I decided to use SQLite to hold a central copy of the data and use SQL (Structured Query Language) queries to produce the documentation formatting codes required.&lt;/p&gt;

&lt;p&gt;SQLite uses a single terminal command to access all its features. This command, &lt;em&gt;sqlite3&lt;/em&gt;, makes it easy to script the steps in your document creation process. The ability to chain SQLite commands together makes the process flexible and repeatable. Also, the output from these commands can be piped into external files. Most documentation build process can include these external files ensuring the latest data is presented within the document.&lt;/p&gt;

&lt;p&gt;Leveraging the power of the SQL syntax, you can also control the ordering of the data and the splitting of the large results across multiple pages.&lt;/p&gt;

&lt;p&gt;Also, as the whole process is scripted, in my case using &lt;em&gt;make&lt;/em&gt;, the commands that generate the documentation can be managed using source code control. Using a source code version system, like Git &lt;a href="https://git-scm.com/"&gt;GNU Make&lt;/a&gt;, allows you to track the history of changes made to your documentation data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Document Types
&lt;/h2&gt;

&lt;p&gt;The data set I maintain is a list of medical document types used in a regional Health Information Exchange.&lt;br&gt;
The data set contains three fields: document name, clinical code, and schema. I maintain the list in a simple comma-separated (CSV) data file, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Discharge Letter,823701000000103,SNOMED-CT
Drug/substance use,1064501000000103,SNOMED-CT
End of Life Care Document,861411000000103,SNOMED-CT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Importing the data into SQLite
&lt;/h2&gt;

&lt;p&gt;The CSV file is my master data source. If I need to change an entry, I change it within the CSV file and then re-process the data. If I add a new document type to the health exchange, I will add the entry to the CSV file and then run &lt;em&gt;make load&lt;/em&gt; to load the data into SQLite for further processing.&lt;/p&gt;

&lt;p&gt;I use &lt;a href="https://www.gnu.org/software/make/manual/make.html"&gt;GNU Make&lt;/a&gt; to run the commands I need.&lt;br&gt;
It's not necessary to use make. If your more familiar with using bash scripts, or on Windows batch files, then use those. The make system, for me, is an easy way to group a collection of commands to together.&lt;/p&gt;

&lt;p&gt;The make system reads a makefile within your project folder. The makefile contains the commands you want to run in the order you want to run them. The makefile can have multiple targets; denoted by a keyword followed by a colon character. When you run the make command followed by a target name, just the commands within that section will be executed.&lt;/p&gt;

&lt;p&gt;I use two targets within my &lt;em&gt;makefile&lt;/em&gt;, making it easy for me to &lt;em&gt;load&lt;/em&gt; the data and produce the documentation &lt;em&gt;docs&lt;/em&gt;. The code below shows the SQLite commands I use to import the CSV file into a database table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;load&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    sqlite3 &lt;span class="nt"&gt;-cmd&lt;/span&gt; &lt;span class="s2"&gt;".read create_tables.sql"&lt;/span&gt;
            &lt;span class="p"&gt;-&lt;/span&gt;cmd &lt;span class="s2"&gt;".mode csv"&lt;/span&gt;
            &lt;span class="p"&gt;-&lt;/span&gt;cmd &lt;span class="s2"&gt;".import document_types.csv document_types"&lt;/span&gt;
            document_types.db &amp;lt; count.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;em&gt;-cmd&lt;/em&gt; parameter of the &lt;em&gt;sqlite3&lt;/em&gt; command-line utility allows you to specify which commands need executing. We need to use multiple &lt;em&gt;-cmd&lt;/em&gt; sequences to execute the commands we need in order. In the example above, we start by reading and running the SQL statements within the &lt;em&gt;create_tables.sql&lt;/em&gt; file, which drops and re-creates the document types table within the database file &lt;em&gt;document_types.db&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We then switch the mode of operation to CSV before importing the CSV file. The &lt;em&gt;.import&lt;/em&gt; command has two parameters. The first parameter is the name of the CSV file containing your data. The second parameter instructs SQLite as to which table the data should be imported into. Finally, we read and execute the &lt;em&gt;count.sql&lt;/em&gt; file, which counts the number of records in the table and prints it to the terminal screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  The create table SQL
&lt;/h2&gt;

&lt;p&gt;The SQL code below shows the contents of the &lt;em&gt;create_tables.sql&lt;/em&gt; file. It starts by dropping the table &lt;em&gt;document_types&lt;/em&gt; to clear out the old data; and then re-creates the table by supplying the column definitions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="n"&gt;document_types&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;document_types&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;"name"&lt;/span&gt;      &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"code"&lt;/span&gt;      &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"scheme"&lt;/span&gt;    &lt;span class="nb"&gt;TEXT&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The contents of the file &lt;em&gt;count.sql&lt;/em&gt; is shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' : Records Imported'&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;document_types&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have our data in an SQLite database file; we can start by producing the documentation code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating documentation code
&lt;/h2&gt;

&lt;p&gt;It shouldn't matter which documentation system you use; they all generally support including files. LaTex, Asciidocter and DocBook all support including files. The key is getting SQLite to produce output in the format that your documentation system requires.&lt;/p&gt;

&lt;p&gt;Personally, I use &lt;a href="https://www.latex-project.org/"&gt;LaTeX&lt;/a&gt; and the code to display the data within a table is shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\section*&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Document Types&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;\begin{center}&lt;/span&gt;
    &lt;span class="nt"&gt;\begin{tabular}&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;p&lt;span class="p"&gt;{&lt;/span&gt;8cm&lt;span class="p"&gt;}&lt;/span&gt; p&lt;span class="p"&gt;{&lt;/span&gt;3cm&lt;span class="p"&gt;}&lt;/span&gt; p&lt;span class="p"&gt;{&lt;/span&gt;2.7cm&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;\bfseries&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Value&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;\bfseries&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Node&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;\bfseries&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;Schema&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;\\&lt;/span&gt;
        &lt;span class="k"&gt;\toprule&lt;/span&gt;
            &lt;span class="k"&gt;\input&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;doc-types-table.tex&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;\bottomrule&lt;/span&gt;
    &lt;span class="nt"&gt;\end{tabular}&lt;/span&gt;
&lt;span class="nt"&gt;\end{center}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The line of interest in the above code snippet is the line that says &lt;em&gt;input{doc-types-table.tex}&lt;/em&gt; This line instructs the LaTeX system to import the commands contained within a file called &lt;em&gt;doc-types-table.tex&lt;/em&gt;.&lt;br&gt;
We need to get SQLite to generate that file with the data and the correct formatting codes to render it as part of the table.&lt;/p&gt;

&lt;p&gt;The LaTeX format for the table row is shown below.&lt;br&gt;
The ampersand character separates each column within the table. The end of each row is depicted by the two backslash characters. The &lt;em&gt;\midrule&lt;/em&gt; command produces a thin line between the table rows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;Discharge Letter &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; 823701000000103 &lt;span class="p"&gt;&amp;amp;&lt;/span&gt; SNOMED-CT &lt;span class="k"&gt;\\&lt;/span&gt; &lt;span class="k"&gt;\midrule&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Therefore, we need an SQLite SQL command to output the data in the above format. The SQL I use is shown below. Using the SQLite construct &lt;em&gt;||&lt;/em&gt; to concatenate (join) the data fields together, separated by ampersand characters. The SQL statement also sorts the data into name order for me limits the number of records returned to 30, as only 30 entries will fit on a given page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' &amp;amp; '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' &amp;amp; '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;scheme&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="se"&gt;\m&lt;/span&gt;&lt;span class="s1"&gt;idrule '&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;LaTeX&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;document_types&lt;/span&gt;
 &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
 &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the above SQL is saved into a file called &lt;em&gt;publishing_standards_page.sql&lt;/em&gt; then we can update our makefile to include a &lt;em&gt;docs:&lt;/em&gt; target so we can issue the command *make docs" every time we want to re-generate the documentation.&lt;/p&gt;

&lt;p&gt;The SQLite command is slightly different as we switch to &lt;em&gt;list&lt;/em&gt; mode and redirect the output to the file &lt;em&gt;doc-types-table.tex&lt;/em&gt; within our LaTeX project folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    sqlite3 &lt;span class="nt"&gt;-cmd&lt;/span&gt; &lt;span class="s2"&gt;".mode list"&lt;/span&gt;
        document_types.db
        &amp;lt; publishing_standards_page.sql
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /path/to/latex/doc-types-table.tex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you get your head around the idea of your database generating your documentation, you can automate the creation of many different documents for different audiences from the same data set.&lt;br&gt;
For example, we also have a &lt;em&gt;request for a new role&lt;/em&gt; document, which users must complete before we define new roles. This document is a PDF that again lists the document types but needs to generate tick boxes so people can express which documents the new role should have access to view.&lt;/p&gt;

&lt;p&gt;LaTeX is pretty good as it can create forms with a PDF. The SQL below is saved into a file called &lt;em&gt;roles_request_form.sql&lt;/em&gt; and generates the LaTeX code required to produce a PDF form. Each checkbox within a form needs to have a unique name. Therefore, I've used the SQLite inbuilt keyword ROWID, which returns each record's unique internal row number.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' &amp;amp; &lt;/span&gt;&lt;span class="se"&gt;\C&lt;/span&gt;&lt;span class="s1"&gt;heckBox[name='&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;ROWID&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="s1"&gt;', width=1em, bordercolor={lightgray}]{Yes} &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="se"&gt;\m&lt;/span&gt;&lt;span class="s1"&gt;idrule '&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;LaTeX&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;document_types&lt;/span&gt;
 &lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
 &lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The updated &lt;em&gt;makefile&lt;/em&gt; is shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;docs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    sqlite3 &lt;span class="nt"&gt;-cmd&lt;/span&gt; &lt;span class="s2"&gt;".mode list"&lt;/span&gt;
        document_types.db
        &amp;lt; publishing_standards_page.sql
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /path/to/latex/doc-types-table.tex
    sqlite3 &lt;span class="nt"&gt;-cmd&lt;/span&gt; &lt;span class="s2"&gt;".mode list"&lt;/span&gt;
        document_types.db
        &amp;lt; roles_request_form.sql
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /path/to/latex/doc-roles-form.tex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating software configurations
&lt;/h2&gt;

&lt;p&gt;The same process can also be used to generate your software configuration files. The health exchange uses XML files to apply restrictive polices to groups of users. For example, a clinical user can view all document types except two specific documents. The database is used to generate the XML configuration files the software needs to configure these polices.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="s1"&gt;'&amp;lt;AttributeValue DataType="http://www.w3.org/2001/XMLSchema#string"&amp;gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="n"&gt;code&lt;/span&gt;  &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;/AttributeValue&amp;gt;'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;ClinicalRole&lt;/span&gt;
 &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;document_types&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'149741000000107'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'723394009'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example SQL above extracts the document clinical codes for the two documents clinical staff aren't allowed to view.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nl"&gt;config&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    sqlite3 &lt;span class="nt"&gt;-cmd&lt;/span&gt; &lt;span class="s2"&gt;".mode list"&lt;/span&gt;
        document_types.db
        &amp;lt; clinical_role_restrictions.sql
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /path/to/software/configs/roles.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>sqlite</category>
      <category>documentation</category>
      <category>automation</category>
    </item>
    <item>
      <title>Amazon Rekognition using the Go AWS API</title>
      <dc:creator>Paul Bradley</dc:creator>
      <pubDate>Sat, 16 Jan 2021 12:03:38 +0000</pubDate>
      <link>https://dev.to/nhsdeveloper/amazon-rekognition-using-the-go-aws-api-3lkb</link>
      <guid>https://dev.to/nhsdeveloper/amazon-rekognition-using-the-go-aws-api-3lkb</guid>
      <description>&lt;h1&gt;
  
  
  Amazon Rekognition using the Go AWS API
&lt;/h1&gt;

&lt;p&gt;Amazon Rekognition is a highly scalable, deep learning technology that let's you identify objects, people, and text within images and videos. It also provides highly accurate facial analysis and facial search capabilities. In this short article we'll explore two use cases with the Go API. The first will be to OCR (extract text) from and image and the second will be to identify the contents of an image.&lt;/p&gt;

&lt;p&gt;The first step is to install the AWS software development kit (SDK) for Go. This is done by using the following command issued at the terminal or command prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/aws/aws-sdk-go/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the AWS SDK has been installed, you'll then need to import the relevant sections into your program to be able to interact with the Rekognition service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"bufio"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"io/ioutil"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"strings"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/credentials"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/session"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/service/rekognition"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first step within our interaction with any AWS service is to initialise a session; by passing in your AWS access key and AWS secret key. For security reasons these aren't included within our program but obtained from environment variables. After calling the NewSession function we need to check the error state. If an error has occurred then we terminate the program and log the error message to the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSession&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;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"eu-west-2"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Credentials&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewStaticCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AccessKeyID"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SecretAccessKey"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&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;
  
  
  Optical Character Recognition
&lt;/h2&gt;

&lt;p&gt;The image below is the later flow packaging for a Covid19 test. Let's assume we want to perform analysis of this image and extract the text into a machine readable format so that we can use the data within our application/database. This would be a good test for the API as the packaging is quite shiny and thus reflects the light in different ways. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WAafFa9C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hadleybradley.com/img/photos/lfa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WAafFa9C--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hadleybradley.com/img/photos/lfa.jpg" alt="Optical Character Recognition" width="500" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We initialise the rekognition API using the DetectTextInput option. We need to pass in the raw bytes for the image we want to process. This can either be a reference to an S3 object (ideal for running this type of analysis as a Lambda function) or like in this example an image loaded from the local file system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rekognition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rekognition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DetectTextInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rekognition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;loadLocalFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"lfa.jpg"&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;The function to load the image from local disk is shown below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;loadLocalFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;File&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bufio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Reader&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;

    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bufio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ioutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;())&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;data&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then run the DetectText function and capture the results. Once we have the results we can then loop over the entires and print out the text that has been detected within the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DetectText&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&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;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextDetections&lt;/span&gt; &lt;span class="p"&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;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"LINE"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DetectedText&lt;/span&gt;&lt;span class="p"&gt;))&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;Which produces the following output. As you can see, its done a very good job of detecting the text within the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INNOVA
1 Test
Antigen Test Cartridge
LOT X2010008
2020. 10. 30
2022. 10. 29
CE
INNOVA
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As with all AWS Rekognition results a confidence score is returned, which you can use to decide if you want to trust a particular result. You can also return the bounding box coordinates for the detected text by accessing the Geometry values. I plan to do a follow up article which demonstrates how to use these values to draw highlighting boxes back onto the original image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TextDetections&lt;/span&gt; &lt;span class="p"&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;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"LINE"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TrimSpace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DetectedText&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Confidence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Geometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Polygon&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Geometry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Polygon&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;)&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;
  
  
  Image Identification
&lt;/h2&gt;

&lt;p&gt;Let's try using the image identification service to see what it makes of this image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zfzz__9s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hadleybradley.com/img/photos/blue-tit.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zfzz__9s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://hadleybradley.com/img/photos/blue-tit.jpg" alt="Photo of a Blue Tit bird sat on a fence" width="500" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So preparing the input is slightly different. This time we need to use the DetectLabelsInput function which again let's us load the image content from either an S3 bucket or a local file. However, the DetectLabelsInput function has two additional parameters to specify the maximum number of identifications (labels) and the minium confidence score to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rekognition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rekognition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DetectLabelsInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;rekognition&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;loadLocalFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"input.jpg"&lt;/span&gt;&lt;span class="p"&gt;)},&lt;/span&gt;
    &lt;span class="n"&gt;MaxLabels&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;123&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;MinConfidence&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Float64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;70.000000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;svc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DetectLabels&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;detectedLabel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Labels&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;detectedLabel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&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;Once we've defined the input, then we can call the DetectLabels function and loop over the results printing out the label names. For the above image the following identifications were returned:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bird
Animal
Jay, Finch
Blue Jay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Costs
&lt;/h2&gt;

&lt;p&gt;At $1.16 for every 1,000 images it would cost around $60 to categorise 50,000 images. So I'm going to be looking at categorising my entire photo albums so that I can search by the generated labels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Need Help or Advice
&lt;/h2&gt;

&lt;p&gt;If you need any help or advice in using AWS Rekognition with Go then please &lt;a href="https://hadleybradley.com/contact.html"&gt;get in touch&lt;/a&gt; I'd be happy to help.&lt;/p&gt;

</description>
      <category>todayilearned</category>
      <category>go</category>
      <category>aws</category>
    </item>
    <item>
      <title>Terminal Image Manipulation</title>
      <dc:creator>Paul Bradley</dc:creator>
      <pubDate>Mon, 04 Jan 2021 09:38:21 +0000</pubDate>
      <link>https://dev.to/nhsdeveloper/terminal-image-manipulation-2dc8</link>
      <guid>https://dev.to/nhsdeveloper/terminal-image-manipulation-2dc8</guid>
      <description>&lt;p&gt;As a web developer there are many instances in which you need to interact with images. Maybe you need to rotate or resize an image before posting it to your web site. In most cases, you'll not need a fully blown image editor like Gimp installed on your machine. With &lt;a href="https://imagemagick.org/"&gt;ImageMagick&lt;/a&gt; and other command line tools, such as &lt;a href="https://linux.die.net/man/1/jpegtran"&gt;jpegtran&lt;/a&gt;, you can accomplish a lot from the terminal. Here is a list of the commands I use on a regular basis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get dimensions of an image
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;identify input.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which will return the dimensions like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;input.jpg JPEG 3264x2448 3264x2448+0+0 8-bit sRGB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rotate an image
&lt;/h2&gt;

&lt;p&gt;You can rotate an image through 90 or 180 degrees by using the jpegtran command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jpegtran &lt;span class="nt"&gt;-rotate&lt;/span&gt; 90 input.jpg &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; output.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Resize an image maintaining aspect ratio
&lt;/h2&gt;

&lt;p&gt;You can use the convert command from the ImageMagic library to resize an image to a specific width while maintaining the aspect ration for the images height. In this example, we resize the input image to a maximum width of 700 pixels. We also reduce the quality of the JPG compression to 80%.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;convert &lt;span class="nt"&gt;-geometry&lt;/span&gt; 700x input.jpg &lt;span class="nt"&gt;-quality&lt;/span&gt; 80 output.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prepare an image for a website
&lt;/h2&gt;

&lt;p&gt;To prepare a JPG image which is as small as possible for website performance we should remove all the EXIF data and remove the embedded thumbnail that many cameras generate. The command below does this, while also converting the JPG to use a progressive encoding. The progressive encoding can make the image appear to load faster over slow internet connections, as it renders the image in stages to make it appear it's being streamed in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jpegtran &lt;span class="nt"&gt;-copy&lt;/span&gt; none &lt;span class="nt"&gt;-progressive&lt;/span&gt; &lt;span class="nt"&gt;-optimize&lt;/span&gt; input.jpg &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; output.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Remove colour from an image
&lt;/h2&gt;

&lt;p&gt;To convert a JPG image to a greyscale black and white photo use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jpegtran &lt;span class="nt"&gt;-greyscale&lt;/span&gt; input.jpg &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; grey.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>linux</category>
      <category>ubuntu</category>
      <category>imagemagick</category>
    </item>
    <item>
      <title>Upgrading Ubuntu Server from 19.04 to 20.04 (Focal Fossa)</title>
      <dc:creator>Paul Bradley</dc:creator>
      <pubDate>Wed, 29 Apr 2020 06:39:31 +0000</pubDate>
      <link>https://dev.to/nhsdeveloper/upgrading-ubuntu-server-from-19-04-to-20-04-focal-fossa-523d</link>
      <guid>https://dev.to/nhsdeveloper/upgrading-ubuntu-server-from-19-04-to-20-04-focal-fossa-523d</guid>
      <description>&lt;h2&gt;
  
  
  Preparation
&lt;/h2&gt;

&lt;p&gt;Before upgrading your Ubuntu server take a backup or snapshot of your existing server so that you can restore it to a working server if the upgrade fails at any point. Most hosting providers offer a facility via their control panel to make a snap shot backup. It's probably best practice to take this snap shot while the server is powered down.&lt;/p&gt;

&lt;p&gt;Upgrading to Ubuntu 20.04 LTS (Focal Fossa) is a good idea as it's a long term support release which means you'll get security updates for the next five years. Focal Fossa comes with a lot of new packages and major software upgrades, including the latest versions of Java, Python, Ruby, PHP and many more. This release is based on the Linux 5.4 LTS kernel and adds support for new hardware and file systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check for held packages &amp;amp; update
&lt;/h2&gt;

&lt;p&gt;Start by checking if you have any held packages by using the the &lt;code&gt;showhold&lt;/code&gt; command. If you do have any then mark them as un-held by issuing an &lt;code&gt;unhold&lt;/code&gt; command followed by the package names listed in the previous command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-mark showhold
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-mark unhold package_name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have un-held any packages, perform a standard update and upgrade and then reboot the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Purge &amp;amp; remove unwanted packages
&lt;/h2&gt;

&lt;p&gt;After the server restarts then perform a full upgrade and then purge any unwanted or unused packages to recover some disk space.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt full-upgrade
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nt"&gt;--purge&lt;/span&gt; autoremove
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Upgrade to 20.04
&lt;/h2&gt;

&lt;p&gt;At this point you're ready to upgrade from 19.04 to 20.04 by performing the release upgrade commands. As the upgrade progresses you'll be prompted to answer various questions in relation to your setup. These questions will vary depending on what software you've already installed and configured on your Ubuntu server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;update-manager-core
&lt;span class="nb"&gt;sudo &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="nt"&gt;-release-upgrade&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verifying the version number after the upgrade
&lt;/h2&gt;

&lt;p&gt;After the upgrade has completed and the server has rebooted you can check the current version of your Ubuntu server by checking the release version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsb_release &lt;span class="nt"&gt;-a&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which should output something like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04 LTS
Release:    20.04
Codename:   focal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>todayilearned</category>
      <category>ubuntu</category>
      <category>linux</category>
    </item>
    <item>
      <title>Using Google Chrome's Socks5 Proxy With SSH Tunnels</title>
      <dc:creator>Paul Bradley</dc:creator>
      <pubDate>Mon, 04 Nov 2019 08:06:38 +0000</pubDate>
      <link>https://dev.to/nhsdeveloper/using-google-chrome-s-socks5-proxy-with-ssh-tunnels-4fdg</link>
      <guid>https://dev.to/nhsdeveloper/using-google-chrome-s-socks5-proxy-with-ssh-tunnels-4fdg</guid>
      <description>&lt;h1&gt;
  
  
  Using Google Chrome's Socks5 Proxy With SSH Tunnels
&lt;/h1&gt;

&lt;p&gt;At work we're migrating our core infrastructure to Amazon's AWS service. We have a pretty standard set-up with an infrastructure account hosting our bastion servers, allowing suppliers to connect to our different environments. Currently we have three Virtual Private Clouds (VPC) - production, pre-production and development.&lt;/p&gt;

&lt;p&gt;On premise access to these environments is provided via a Direct Connect pipe provided by our WAN supplier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   INTERNET
      |
+-------------+       +----------------------+
|     |       |       |                      |
|  +--v-----+ |       | +--------+ +-------+ |
|  | Bastion| |       | | App    | |DB     | |
|  | Server |-----------&amp;gt; Server | |Server | |
|  +--------+ |       | |        | |       | |
|             |       | +--------+ +-------+ |
+-------------+       +----------------------+

INFRASTRUCTURE                     PRODUCTION
VPC                                       VPC
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Accessing the web interfaces of the applications running on our application servers is fine while working on premise. However, when I work from home I need to create an SSH tunnel through the bastion server and then use Chromes socks5 proxy to direct requests through the SSH tunnel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the SSH tunnel
&lt;/h2&gt;

&lt;p&gt;SSH into your bastion server using the &lt;code&gt;-D&lt;/code&gt; option to bind an addressing port from the local machine to the connection. I tend to use port 1080&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh ec2-user@bastion &lt;span class="nt"&gt;-D1080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Launching Chrome to use the binding&lt;br&gt;
Launch Chrome and pass in the &lt;code&gt;--proxy-server&lt;/code&gt; option to use the socks5 proxy on your localhost. All traffic/requests will be directed to the port number you used in the SSH connection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"&lt;/span&gt;
    &lt;span class="nt"&gt;--user-data-dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOMEproxy&lt;/span&gt;&lt;span class="s2"&gt;-profile"&lt;/span&gt;
    &lt;span class="nt"&gt;--proxy-server&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"socks5://localhost:1080"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this Chrome session you should be able to connect to the web interfaces or applications running on servers within your VPC's&lt;/p&gt;




&lt;p&gt;Also posted @ &lt;a href="https://paulbradley.org/chrome-socks5/"&gt;https://paulbradley.org/chrome-socks5/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ssh</category>
      <category>chrome</category>
      <category>aws</category>
      <category>vpc</category>
    </item>
    <item>
      <title>Building a digital wingman to assist busy clinical staff</title>
      <dc:creator>Paul Bradley</dc:creator>
      <pubDate>Mon, 08 Apr 2019 14:20:44 +0000</pubDate>
      <link>https://dev.to/nhsdeveloper/building-a-digital-wingman-to-assist-busy-clinical-staff-4mj7</link>
      <guid>https://dev.to/nhsdeveloper/building-a-digital-wingman-to-assist-busy-clinical-staff-4mj7</guid>
      <description>&lt;h1&gt;
  
  
  Building a digital wingman to assist busy clinical staff
&lt;/h1&gt;

&lt;p&gt;I've been working on creating a "digital wingman" to assist busy clinical staff, by detecting if particular drugs have been missed off a discharge letter.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/9KJzKkSG5jo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Sorry, the video doesn't have any voice over so this is what you're seeing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An unstructured discharge letter is sent to AWS's Medical Comprehend AI&lt;/li&gt;
&lt;li&gt;A list of medications, dosage and route information is detected within the text, along with forty items of interest. These include medical procedurals and diagnosis.&lt;/li&gt;
&lt;li&gt;A visual model of the medical terms and their relationship with each other is then produced and displayed.&lt;/li&gt;
&lt;li&gt;We then remove the drug that treats hypertension (high blood pressure) and rerun the program. As hypertension is mentioned within the clinical narrative the system then checks all possible drugs that could be prescribed to treat hypertension.&lt;/li&gt;
&lt;li&gt;Not finding one, it then sends an alert to the caregiver for investigation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  AWS medical comprehend
&lt;/h2&gt;

&lt;p&gt;The medical comprehend API is a recent offering from AWS which accepts unstructured medical text and returns a JSON object outlining all the medical, procedural and diagnosis terms found within the text. For medications, it also returns the dose, route and frequency if found within the text. For each medical term found a confidence score is returned to help you decided if you should trust the value. An offset position is also returned indicating the starting point for the medical term found.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example using Go
&lt;/h2&gt;

&lt;p&gt;The first step is to install the AWS software development kit (SDK) for Go. This is done by using the following Go get command issued at the terminal or command prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;go&lt;/span&gt; &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;sdk&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;go&lt;/span&gt;&lt;span class="o"&gt;/...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the AWS SDK has been installed, you’ll then need to import the relevant sections into your program to be able to interact with the medical comprehend API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"bytes"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/credentials"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/session"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/service/comprehendmedical"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll need to create an AWS session select a region which supports the medical comprehend API (not all do). In the example below the newly created session is assigned to the &lt;code&gt;s&lt;/code&gt; variable. The session object is created by passing in the region identifier and your AWS credentials for id and secret key. Your id and secret key can be obtained from your AWS account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c"&gt;// create an AWS session which can be&lt;/span&gt;
    &lt;span class="c"&gt;// reused if we're uploading many files&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSession&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;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"us-east-1"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Credentials&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewStaticCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"XXX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"YYY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;comprehendmedical&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;input&lt;/span&gt;  &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;comprehendmedical&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DetectEntitiesInput&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SetText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`
        Pt is 40yo mother, highschool teacher
        HPI   : Sleeping trouble on present dosage of Clonidine.
                Severe Rash  on face and leg, slightly itchy
        Meds  : Vyvanse 50 mgs po at breakfast daily,
                Clonidine 0.2 mgs -- 1 and 1 / 2 tabs po qhs
        HEENT : Boggy inferior turbinates, No oropharyngeal lesion
        Lungs : clear
        Heart : Regular rhythm
        Skin  : Mild erythematous eruption to hairline
    `&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DetectEntities&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;input&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&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;Start by defining a medical comprehend client using the New function passing in your AWS session variable &lt;code&gt;s&lt;/code&gt;. Then define an input object and use the &lt;code&gt;SetText&lt;/code&gt; function to define the medical text you want to process. The processing of the medical text is done when the &lt;code&gt;DetectEntities&lt;/code&gt; function is called and the results are stored in the results variable.&lt;/p&gt;

&lt;p&gt;To get the JSON string returned you can use the &lt;code&gt;GoString&lt;/code&gt; function as shown below. This is useful if you want to store the JSON in a Postgres JSONB database field to perform search queries across many patients.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GoString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can loop over the detected terms and only print out the medications found by using the following pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Entities&lt;/span&gt; &lt;span class="p"&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;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Category&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"MEDICATION"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&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;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Score&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"-----------"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="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;Which will output the following results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;Clonidine&lt;/span&gt;
&lt;span class="n"&gt;GENERIC_NAME&lt;/span&gt;
&lt;span class="m"&gt;0.9948381781578064&lt;/span&gt;
&lt;span class="o"&gt;-----------&lt;/span&gt;
&lt;span class="n"&gt;Vyvanse&lt;/span&gt;
&lt;span class="n"&gt;BRAND_NAME&lt;/span&gt;
&lt;span class="m"&gt;0.9995348453521729&lt;/span&gt;
&lt;span class="o"&gt;-----------&lt;/span&gt;
&lt;span class="n"&gt;Clonidine&lt;/span&gt;
&lt;span class="n"&gt;GENERIC_NAME&lt;/span&gt;
&lt;span class="m"&gt;0.997945249080658&lt;/span&gt;
&lt;span class="o"&gt;-----------&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Categories
&lt;/h2&gt;

&lt;p&gt;Other useful category names include:-&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DX_NAME&lt;/li&gt;
&lt;li&gt;DIAGNOSIS&lt;/li&gt;
&lt;li&gt;SYMPTON&lt;/li&gt;
&lt;li&gt;SYSTEM_ORGAN_SITE&lt;/li&gt;
&lt;li&gt;DOSAGE &amp;amp; DIRECTION.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>ai</category>
      <category>aws</category>
      <category>comprehend</category>
    </item>
    <item>
      <title>Uploading file(s) to AWS S3 using Go</title>
      <dc:creator>Paul Bradley</dc:creator>
      <pubDate>Thu, 10 Jan 2019 11:02:04 +0000</pubDate>
      <link>https://dev.to/nhsdeveloper/uploading-files-to-aws-s3-using-go-2dei</link>
      <guid>https://dev.to/nhsdeveloper/uploading-files-to-aws-s3-using-go-2dei</guid>
      <description>&lt;h1&gt;
  
  
  Uploading file(s) to AWS S3 using Go
&lt;/h1&gt;

&lt;p&gt;My current side project is &lt;a href="https://glacial.io"&gt;Glacial&lt;/a&gt;, a secure cloud-based document storage &amp;amp; viewing solution for long term archiving of medical records &amp;amp; documents. Clinical documents are encrypted locally before leaving the hospital network and are encrypted again using Amazon’s S3 server-side AES256 encryption.&lt;/p&gt;

&lt;p&gt;This quick tutorial demonstrates how to upload your file(s) to an AWS S3 bucket using the Go programming language.&lt;/p&gt;

&lt;p&gt;The first step is to install the AWS software development kit (SDK) for Go. This is done by using the following &lt;code&gt;go get&lt;/code&gt; command issued at the terminal or command prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/aws/aws-sdk-go/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the AWS SDK has been installed, you’ll then need to import the relevant sections into your program to be able to interact with S3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"bytes"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/credentials"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/aws/session"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/aws/aws-sdk-go/service/s3"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You’ll need to assign a couple of variables. These will hold the AWS region identifier and the S3 bucket name that you wish to upload files to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;S3_REGION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"eu-west-1"&lt;/span&gt;
    &lt;span class="n"&gt;S3_BUCKET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"glacial-io"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Within the main part of your program, you need to create an AWS session using the &lt;code&gt;NewSession&lt;/code&gt; function. In the example below the newly created session is assigned to the &lt;code&gt;s&lt;/code&gt; variable. The session object is created by passing in the region identifier and your AWS credentials for id and secret key. Your id and secret key can be obtained from your AWS account. The &lt;code&gt;NewSession&lt;/code&gt; function returns a pointer to the session object and if there was an error, i.e. you specified an invalid region, then an error struct is returned.&lt;/p&gt;

&lt;p&gt;You should, therefore, check the status of the error variable and handle appropriately for your use case. In this example, we simply log the error to the console as a fatal message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c"&gt;// create an AWS session which can be&lt;/span&gt;
    &lt;span class="c"&gt;// reused if we're uploading many files&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewSession&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;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Region&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;S3_REGION&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Credentials&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;credentials&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewStaticCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"XXX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"YYY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uploadFileToS3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"discharge-letter-787653.pdf"&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&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;Once you’ve got a pointer to a valid AWS session you can reuse that session to upload multiple files. In the above example, we’re only using it once as we pass it to the upload function to upload a discharge letter.&lt;/p&gt;

&lt;h2&gt;
  
  
  The upload function
&lt;/h2&gt;

&lt;p&gt;The upload function takes two parameters, the pointer to the session object and the name of the file to upload. Firstly, we attempt to open the file. If that fails, because the filename is invalid, then we return the error and exit the function. If the file does exist, then we proceed to calculate the size of the file and read the file's bytes into a buffer.&lt;/p&gt;

&lt;p&gt;We use the &lt;code&gt;PutObject&lt;/code&gt; function of the SDK to upload the file. Here we can specify various S3 object parameters. For example, we specify the bucket name and the access control list (ACL) in this case we’re flagging the file as private. We also specify which S3 storage classification should be used and in this example, we implement server-side encryption.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;uploadFileToS3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fileName&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c"&gt;// open the file for use&lt;/span&gt;
    &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileName&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// get the file size and read&lt;/span&gt;
    &lt;span class="c"&gt;// the file content into a buffer&lt;/span&gt;
    &lt;span class="n"&gt;fileInfo&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;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// config settings: this is where you choose the bucket,&lt;/span&gt;
    &lt;span class="c"&gt;// filename, content-type and storage class of the file&lt;/span&gt;
    &lt;span class="c"&gt;// you're uploading&lt;/span&gt;
    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s3err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObject&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;s3&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PutObjectInput&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Bucket&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;               &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                  &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ACL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                  &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;                 &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ContentLength&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;        &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ContentType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DetectContentType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
        &lt;span class="n"&gt;ContentDisposition&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"attachment"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;ServerSideEncryption&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"AES256"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;StorageClass&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;         &lt;span class="n"&gt;aws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"INTELLIGENT_TIERING"&lt;/span&gt;&lt;span class="p"&gt;),&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;s3err&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Storage Classifications
&lt;/h2&gt;

&lt;p&gt;Besides the standard storage class &lt;code&gt;STANDARD&lt;/code&gt; and the infrequent access class &lt;code&gt;STANDARD_IA&lt;/code&gt;, there is now a new (as of Nov 2018) storage class called intelligent tiering. This is ideal for use cases where the file access patterns aren’t known and you’re unsure which class to use. The intelligent tiering option attempts to save you money by automatically moving files between standard and infrequent access based on usage patterns.&lt;/p&gt;

&lt;p&gt;You can also now upload files directly to the Glacier storage engine using the value &lt;code&gt;GLACIER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If cost is an issue, and you don’t need the level of redundancy that the other storage classes offer then you can use &lt;code&gt;REDUCED_REDUNDANCY&lt;/code&gt; to lower your monthly fees.&lt;/p&gt;




&lt;p&gt;Also posted @ &lt;a href="https://paulbradley.org/gos3/"&gt;https://paulbradley.org/gos3/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>aws</category>
      <category>s3</category>
    </item>
    <item>
      <title>Typesetting Musical Scores Using Code</title>
      <dc:creator>Paul Bradley</dc:creator>
      <pubDate>Thu, 06 Dec 2018 10:56:43 +0000</pubDate>
      <link>https://dev.to/nhsdeveloper/typesetting-musical-scores-using-code-23bi</link>
      <guid>https://dev.to/nhsdeveloper/typesetting-musical-scores-using-code-23bi</guid>
      <description>&lt;p&gt;&lt;em&gt;An introductory guide to typesetting musical scores using LilyPond. In this guide, we'll learn how to typeset the music for the lullaby “Hush, Little Baby”. Besides adding musical notes we'll also cover adding song lyrics.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;For the last couple of years, I've been teaching an after-school coding club at several schools in my region. For one term each year we focus on coding music using the excellent programmable synth &lt;a href="https://sonic-pi.net" rel="noopener noreferrer"&gt;Sonic Pi&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Music is a fun way to learn the concepts of computer programming as many songs learnt as children use structures which are commonly used in computer programming. Note durations can be defined using variables and songs typically have repeated sections or rhythms which can be easily coded using simple loops and functions.&lt;/p&gt;

&lt;p&gt;To create the worksheets which the children used during class I needed to typeset sections of musical scores to demonstrate a key concept or learning objective.&lt;/p&gt;

&lt;p&gt;In the photograph below we’re peer programming the Star Wars theme tune :-)&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%2F2e9lk6xtkc153slukd8q.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%2F2e9lk6xtkc153slukd8q.jpg" alt="Coding the Star Wars Theme Tune" width="600" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  LilyPond
&lt;/h2&gt;

&lt;p&gt;When I started looking for a suitable program to create the musical scores I wanted something that was free and preferably something that would work from the command line. I'm a big fan of the document preparation system &lt;a href="https://www.latex-project.org" rel="noopener noreferrer"&gt;LaTeX&lt;/a&gt;, so I was looking for something similar for music. &lt;/p&gt;

&lt;p&gt;Then I found the perfect fit for my needs, &lt;a href="http://lilypond.org" rel="noopener noreferrer"&gt;LillyPond&lt;/a&gt;. LilyPond is a music engraving program, devoted to producing the highest-quality sheet music possible. It brings the aesthetics of traditionally engraved music to computer printouts.&lt;/p&gt;

&lt;p&gt;LilyPond is similar to a programming language, you don't write music by dragging notes from a graphical toolbar and placing them on a dynamically refreshing score; you write music by typing text.&lt;/p&gt;

&lt;p&gt;Yes, that's right, you type LilyPond commands into a simple text file and this text is then interpreted by LilyPond producing beautifully engraved sheet music. Once you get a basic understanding of the various LilyPond commands, it's quite easy to produce the music you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;In this tutorial, we're going create the musical score shown below. Start by grabbing a copy of LilyPond from the projects &lt;a href="http://lilypond.org/download.html" rel="noopener noreferrer"&gt;download page&lt;/a&gt;. Choose the appropriate link for your operating system.&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%2Fnhs.io%2Fimg%2Fmocking-bird.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%2Fnhs.io%2Fimg%2Fmocking-bird.jpg" alt="Mocking Bird Score" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start by creating a text file called &lt;code&gt;mocking.ly&lt;/code&gt; in your favourite text editor program. To get started, type in the commands shown below into your text editor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\version&lt;/span&gt; "2.14.2"
&lt;span class="k"&gt;\header&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;title = "Mocking Bird"&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;\time&lt;/span&gt; 4/4
    &lt;span class="k"&gt;\clef&lt;/span&gt; treble

    &lt;span class="k"&gt;\relative&lt;/span&gt; c' &lt;span class="p"&gt;{&lt;/span&gt;
        d4 b'8 b8 b4 b4
    &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;Every LilyPond file should start with a &lt;code&gt;version&lt;/code&gt; statement. The version statement is a line which describes which version of LilyPond the source file is intended for. The second command sets up a header with an appropriate title for the score.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;time&lt;/code&gt; statement sets the &lt;a href="https://en.wikipedia.org/wiki/Time_signature" rel="noopener noreferrer"&gt;time signature&lt;/a&gt; for the piece. In this example, we're using common time or four-four time. Other time signatures could be 3/4 for a waltz or 2/2 for a march.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;clef&lt;/code&gt; statement instructs LilyPond to output the treble clef at the start of the score. Other styles are available, such as bass, baritone, tenor and soprano.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;relative c'&lt;/code&gt; statement instructs LilyPond to typeset all the notes enclosed within the curly brackets relative to the middle C note on a piano keyboard. The letters and numbers within the curly brackets represent the notes for the first bar of music. That’s one d followed by four b notes.&lt;/p&gt;

&lt;p&gt;The numbers after the letters depict the duration of the note to typeset. As we've already set the time signature to 4/4 (or common time) then to depict a crochet we use the number four, as there are four crochets per bar in common time.&lt;/p&gt;

&lt;p&gt;So to depict a minim we would follow the letter with a number two. To depict a quaver we use the number eight, and a semi-quaver would, therefore, be represented with a number sixteen. Lilypond is clever enough to join the stems of multiple quavers together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating the score
&lt;/h2&gt;

&lt;p&gt;When you've finished typing in the code then save the text file &lt;code&gt;mocking.ly&lt;/code&gt; and shell out to a command line or terminal session depending on the operating system you're using. You then run the LilyPond program against your text file to generate an image which will contain the musical score. The command syntax is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;lilypond --png mocking.ly

GNU LilyPond 2.14.2
Processing `mocking.ly'
Preprocessing graphical objects...
Layout output to `mocking.ps'...
Converting to PNG...
success: Compilation successfully completed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming there are no errors in your &lt;code&gt;mocking.ly&lt;/code&gt; file, you should find a new image file in your folder called &lt;code&gt;mocking.png&lt;/code&gt;. If you view this file you should have something which looks like:&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%2Fnhs.io%2Fimg%2Fmocking-bird-01.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%2Fnhs.io%2Fimg%2Fmocking-bird-01.jpg" alt="Mocking Bird Score" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yeah!, we've typeset our first musical score. Now we just need to continue with the remaining bars of music. I'd recommend putting each subsequent bar of music on a separate line within your text file as it'll make it easier to keep track of where in the score you're up to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\version&lt;/span&gt; "2.14.2"
&lt;span class="k"&gt;\header&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;title = "Mocking Bird"&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;\time&lt;/span&gt; 4/4
    &lt;span class="k"&gt;\clef&lt;/span&gt; treble

    &lt;span class="k"&gt;\relative&lt;/span&gt; c' &lt;span class="p"&gt;{&lt;/span&gt;
        d4 b'8 b8 b4 b4
        b4 a8 a8 a2
        d,8 d8 a'8 a8 a4 a8 b8
        a4 g4 g2
        d4 b'4 b4 b4
        b4 a4 a2
        d,8 d8 a'8 a8 a4 a8 b8
        a4 g4 g2
    &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;
  
  
  Removing the indent on the first stave
&lt;/h2&gt;

&lt;p&gt;When you re-run the text file through LilyPond you should get a png file which shows two staves of music. Notice how each bar line is automatically numbered for you. This is a great help when you're trying to explain something to children as you can refer to these numbers.&lt;/p&gt;

&lt;p&gt;Notice how the first stave is indented slightly. This is the default behaviour of LilyPond, however, it's very easy to change this behaviour by inserting a layout statement to your source file and specifying an indent value of zero. See the code below which has the &lt;code&gt;layout&lt;/code&gt; statement added.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\version&lt;/span&gt; "2.14.2"
&lt;span class="k"&gt;\header&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;title  = "Mocking Bird"&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;\layout&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;indent = 0&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;\time&lt;/span&gt; 4/4
    &lt;span class="k"&gt;\clef&lt;/span&gt; treble

    &lt;span class="k"&gt;\relative&lt;/span&gt; c' &lt;span class="p"&gt;{&lt;/span&gt;
        d4 b'8 b8 b4 b4
        b4 a8 a8 a2
        d,8 d8 a'8 a8 a4 a8 b8
        a4 g4 g2
        d4 b'4 b4 b4
        b4 a4 a2
        d,8 d8 a'8 a8 a4 a8 b8
        a4 g4 g2
    &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;
  
  
  Adding lyrics
&lt;/h2&gt;

&lt;p&gt;The next step is to add the lyrics so that they appear under the correct notes. We do this by including an &lt;code&gt;addlyrics&lt;/code&gt; statement with an opening and closing curly brackets. Again, everything that is included within the curly brackets is applied to the lyrics command. By default I found the text size to be too big for lyrics, so I use the &lt;code&gt;set&lt;/code&gt; statement to reduce the default font size by three points.&lt;/p&gt;

&lt;p&gt;When typing out the lyrics it's a good idea to keep each bar to a single line for easier reading. The key to getting the lyrics to line up with the correct notes is to add the correct duration numbers after the words or part of words.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\version&lt;/span&gt; "2.14.2"
&lt;span class="k"&gt;\header&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;title  = "Mocking Bird"&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;\layout&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;indent = 0&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;\time&lt;/span&gt; 4/4
    &lt;span class="k"&gt;\clef&lt;/span&gt; treble

    &lt;span class="k"&gt;\relative&lt;/span&gt; c' &lt;span class="p"&gt;{&lt;/span&gt;
        d4 b'8 b8 b4 b4
        b4 a8 a8 a2
        d,8 d8 a'8 a8 a4 a8 b8
        a4 g4 g2
        d4 b'4 b4 b4
        b4 a4 a2
        d,8 d8 a'8 a8 a4 a8 b8
        a4 g4 g2
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;\addlyrics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;\set&lt;/span&gt; fontSize = #-3

        Hush4 lit-8 tle8 ba4 by4
        don't4 say8 a8 word,2
        Mam8 my's8 going8 to8 buy4 you8 a8
        mock4 ing4 bird2

        If4 that4 mock4 ing4
        bird4 won't4 sing2
        Mam8 my's8 going8 to8 buy4 you8 a8
        dia mond4 ring2
    &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;
  
  
  Adding comments
&lt;/h2&gt;

&lt;p&gt;If you re-look at my finished score, you'll notice that above the first stave the first two notes have their value above, for example, D and B. Every time a note appears for the first time in the music I wanted to add its notation above the stave to help those children who weren’t confident at reading music.&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%2Fnhs.io%2Fimg%2Fmocking-bird.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%2Fnhs.io%2Fimg%2Fmocking-bird.jpg" alt="Mocking Bird Score" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To achieve this you can use the &lt;code&gt;markup&lt;/code&gt; statement enclosing the text you want to appear above the stave within the curly brackets. The &lt;code&gt;tiny&lt;/code&gt; statement sets the font size to a smaller font so that the text doesn't look too big and out of place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tex"&gt;&lt;code&gt;&lt;span class="k"&gt;\version&lt;/span&gt; "2.14.2"
&lt;span class="k"&gt;\header&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;title  = "Mocking Bird"&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;\layout&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;indent = 0&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;\time&lt;/span&gt; 4/4
    &lt;span class="k"&gt;\clef&lt;/span&gt; treble

    &lt;span class="k"&gt;\relative&lt;/span&gt; c' &lt;span class="p"&gt;{&lt;/span&gt;
        d&lt;span class="p"&gt;^&lt;/span&gt;&lt;span class="k"&gt;\markup&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;\tiny&lt;/span&gt; D&lt;span class="p"&gt;}&lt;/span&gt; b'8&lt;span class="p"&gt;^&lt;/span&gt;&lt;span class="k"&gt;\markup&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;\tiny&lt;/span&gt; B&lt;span class="p"&gt;}&lt;/span&gt; b8 b4 b4
        b4 a8&lt;span class="p"&gt;^&lt;/span&gt;&lt;span class="k"&gt;\markup&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;\tiny&lt;/span&gt; A&lt;span class="p"&gt;}&lt;/span&gt; a8 a2
        d,8 d8 a'8 a8 a4 a8 b8
        a4 g4 g2&lt;span class="p"&gt;^&lt;/span&gt;&lt;span class="k"&gt;\markup&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;\tiny&lt;/span&gt; G&lt;span class="p"&gt;}&lt;/span&gt;
        d4 b'4 b4 b4
        b4 a4 a2
        d,8 d8 a'8 a8 a4 a8 b8
        a4 g4 g2
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;\addlyrics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;\set&lt;/span&gt; fontSize = #-3

        Hush4 lit-8 tle8 ba4 by4
        don't4 say8 a8 word,2
        Mam8 my's8 going8 to8 buy4 you8 a8
        mock4 ing4 bird2

        If4 that4 mock4 ing4
        bird4 won't4 sing2
        Mam8 my's8 going8 to8 buy4 you8 a8
        dia mond4 ring2
    &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;I hope you found this introduction to Lilypond useful. Please check out the official &lt;a href="http://lilypond.org/manuals.html" rel="noopener noreferrer"&gt;manuals&lt;/a&gt; for LilyPond as there are lots of commands and statements you can use to tweak and configure the output of your musical scores.&lt;/p&gt;

&lt;p&gt;If you don't have the need to typeset music at least give &lt;a href="https://sonic-pi.net" rel="noopener noreferrer"&gt;Sonic Pi&lt;/a&gt; a try, it really is great fun.&lt;/p&gt;

</description>
      <category>lilypond</category>
      <category>music</category>
      <category>typesetting</category>
    </item>
  </channel>
</rss>
