<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Niño Francisco Liwa on Medium]]></title>
        <description><![CDATA[Stories by Niño Francisco Liwa on Medium]]></description>
        <link>https://medium.com/@nifrali?source=rss-dc7bc30d3f7d------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*c1s2DIObWFO5KYtvWRyioA.jpeg</url>
            <title>Stories by Niño Francisco Liwa on Medium</title>
            <link>https://medium.com/@nifrali?source=rss-dc7bc30d3f7d------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 13:03:33 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@nifrali/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Sendgrid Event Webhook using FastAPI]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/datamatiks/sendgrid-event-webhook-using-fastapi-5f6a43ca5c6a?source=rss-dc7bc30d3f7d------2"><img src="https://cdn-images-1.medium.com/max/768/1*pehTwco8xX9jR_7NkmrX1Q.png" width="768"></a></p><p class="medium-feed-snippet">I recently implemented a Python-based (FastAPI) Sendgrid Webhook to help our team keep track of events after sending email notifications&#x2026;</p><p class="medium-feed-link"><a href="https://medium.com/datamatiks/sendgrid-event-webhook-using-fastapi-5f6a43ca5c6a?source=rss-dc7bc30d3f7d------2">Continue reading on Datamatiks »</a></p></div>]]></description>
            <link>https://medium.com/datamatiks/sendgrid-event-webhook-using-fastapi-5f6a43ca5c6a?source=rss-dc7bc30d3f7d------2</link>
            <guid isPermaLink="false">https://medium.com/p/5f6a43ca5c6a</guid>
            <category><![CDATA[email-marketing]]></category>
            <category><![CDATA[kafka]]></category>
            <category><![CDATA[fastapi]]></category>
            <category><![CDATA[sendgrid]]></category>
            <category><![CDATA[ngrok]]></category>
            <dc:creator><![CDATA[Niño Francisco Liwa]]></dc:creator>
            <pubDate>Wed, 17 Jul 2024 08:10:47 GMT</pubDate>
            <atom:updated>2024-07-17T12:22:05.556Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Building an IoT Platform Using Modern Data Stack — Part 2]]></title>
            <link>https://medium.com/datamatiks/building-an-iot-platform-using-modern-data-stack-part-2-f8fdce1e8318?source=rss-dc7bc30d3f7d------2</link>
            <guid isPermaLink="false">https://medium.com/p/f8fdce1e8318</guid>
            <category><![CDATA[duckdb]]></category>
            <category><![CDATA[fastapi]]></category>
            <category><![CDATA[streamlit]]></category>
            <dc:creator><![CDATA[Niño Francisco Liwa]]></dc:creator>
            <pubDate>Mon, 30 Oct 2023 21:54:21 GMT</pubDate>
            <atom:updated>2023-12-06T11:56:51.201Z</atom:updated>
            <content:encoded><![CDATA[<h3>Building an IoT Platform Using Modern Data Stack — Part 2 (FastAPI + Streamlit)</h3><h3>Accessing data using FastAPI and Streamlit</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*GC5UgkhGU2copDTe47twxQ.png" /></figure><p>After setting up our infrastructure and data pipeline using <em>DuckDB</em> and <em>Prefect </em>in<em> </em><a href="https://medium.com/datamatiks/building-an-iot-platform-using-modern-data-stack-part-1-02c5460e3f6c">Part 1</a> of this series of articles, we can now proceed to accessing and consuming our sensor data using a modern data stack API framework and visualization tool.</p><p>For our API we will be using <a href="https://fastapi.tiangolo.com/">FastAPI</a> — a modern Python framework for developing API. For the visualization, we will learn how to use and run <a href="https://streamlit.io/">Streamlit</a> — a modern open-source dashboard and visualization tool for building data apps, also using Python.</p><h4>A. Building the Data API</h4><p>Like any other Python package, we first need to install FastAPI using pip or any package manager of your choice.</p><pre>pip install fastapi<br>pip install &quot;uvicorn[standard]&quot;</pre><p>A sample FastAPI app is something like below. You can see more at the FastAPI documentation.</p><pre>from typing import Union<br><br>from fastapi import FastAPI<br><br>app = FastAPI()<br><br><br>@app.get(&quot;/&quot;)<br>def read_root():<br>    return {&quot;Hello&quot;: &quot;World&quot;}<br><br><br>@app.get(&quot;/items/{item_id}&quot;)<br>def read_item(item_id: int, q: Union[str, None] = None):<br>    return {&quot;item_id&quot;: item_id, &quot;q&quot;: q}</pre><p>To execute this, save the code above in the file name <em>main.py</em> then execute the command below</p><pre>uvicorn main:app --reload<br><br>INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)<br>INFO:     Started reloader process [28720]<br>INFO:     Started server process [28722]<br>INFO:     Waiting for application startup.<br>INFO:     Application startup complete.</pre><p>With that out of the way, we can follow a similar pattern to create a basic API using FastAPI. Below is the basic API I have created to access data in a DuckDB database.</p><pre>#<br># Main.py<br>#<br><br>from typing import Union<br>from fastapi import FastAPI<br>from fastapi.responses import StreamingResponse<br>import io<br><br>from  .data  import Database<br><br>app = FastAPI()<br>db = Database()<br><br>@app.get(&quot;/&quot;)<br>def read_root():<br>    return {&quot;Hello&quot;: &quot;World&quot;}<br><br><br>@app.get(&quot;/items/{item_id}&quot;)<br>def read_item(item_id: int, q: Union[str, None] = None):<br>    return {&quot;item_id&quot;: item_id, &quot;q&quot;: q}<br><br>@app.get(&quot;/api/v1/daily_avg&quot;, response_class=StreamingResponse)<br>def get_daily_avg():<br>    avg_df = db.get_daily_avg()<br>    avg_df = avg_df.reset_index()<br>    <br>    return _to_csv(avg_df,&#39;daily_avg&#39;)<br><br>@app.get(&quot;/api/v1/daily_max&quot;, response_class=StreamingResponse)<br>def get_daily_max():<br>    max_df = db.get_daily_max()<br>    max_df = max_df.reset_index()<br>    <br>    return _to_csv(max_df,&#39;daily_max&#39;)<br><br>@app.get(&quot;/api/v1/daily_min&quot; ,response_class=StreamingResponse)<br>def get_daily_min():<br>    min_df = db.get_daily_min()<br>    min_df = min_df.reset_index()<br>    <br>    return _to_csv(min_df,&#39;daily_min&#39;)<br><br><br>@app.get(&quot;/api/v1/metadata&quot;, response_class=StreamingResponse)<br>def get_metadata():<br>    mdf =  db.get_gauge_metadata()<br>    return _to_csv(mdf,&#39;metadata&#39;)<br><br>@app.get(&quot;/api/v1/sensor_metrics&quot;, response_class=StreamingResponse)<br>def get_sensor_metrics():<br>    print(&quot;api/v1/sensor_metrics&quot;)<br>    gdf = db.get_gauge_metrics()<br>    gdf = gdf.reset_index()<br><br>    return _to_csv(gdf,&#39;metrics&#39;)<br><br><br>def _to_csv(df, fname):<br>    stream = io.StringIO()<br>    df.to_csv(stream, index=False)<br>    response = StreamingResponse(<br>        iter([stream.getvalue()]), media_type=&quot;text/csv&quot;)<br>    response.headers[&quot;Content-Disposition&quot;] = f&quot;attachment; filename={fname}.csv&quot;<br><br>    return response</pre><p>I also created another file to represent our DuckDB database.</p><pre>#<br># data.py<br>#<br>import duckdb<br><br>def get_conn(db=&#39;:memory:&#39;,is_shared=False):<br>    return duckdb.connect(database=db, read_only=is_shared)<br><br>class Database():<br><br>    def __init__(self):<br>        self.conn = get_conn(&#39;db/datahack.duckdb&#39;)<br>        self._metrics_df = self.__init_metrics_df(self.conn)<br>        self._attributes = self.__get_attributes()<br>      <br>    def __init_metrics_df(self, conn):<br>        metrics_df = conn.sql(&#39;SELECT * EXCLUDE(measured_day,measured_hour,measured_minute) FROM ingest_gauge_metrics&#39;).df()<br>        metrics_df = metrics_df.set_index(&quot;measured_datetime&quot;)<br>        metrics_df = metrics_df.astype(&#39;Float64&#39;)<br>        <br>        conn.close()<br>        return metrics_df<br><br>    def __get_attributes(self):<br>        conn = get_conn(&#39;db/datahack.duckdb&#39;)<br>        attrs_df = conn.sql(&#39;SELECT *  FROM gauge_sensors_metadata&#39;).df()<br>        conn.close()<br>        return attrs_df #.to_csv(index=False)<br><br>    @property<br>    def metrics_df(self):<br>        return self._metrics_df<br>    <br>    @property<br>    def attributes(self):<br>        return self._attributes<br><br>    def get_gauge_metrics(self):<br>        print(&#39;Get metrics&#39;)<br>        return self.metrics_df<br>     <br>    def get_gauge_metadata(self):<br>        print(&#39;Get metadata&#39;)<br>        return self.attributes<br>    <br>    def get_daily_avg(self):<br>        print(&#39;Daily Avg&#39;)<br>        daily_mean = self.metrics_df.resample(&quot;D&quot;).mean()<br>        print(daily_mean)<br><br>        return daily_mean<br><br>    def get_daily_max(self):<br>        print(&#39;Daily Max&#39;)<br>        daily_max = self.metrics_df.resample(&quot;D&quot;).max()<br>        print(daily_max)<br><br>        return daily_max<br><br>    def get_daily_min(self):<br>        print(&#39;Daily Min&#39;)<br>        daily_min = self.metrics_df.resample(&quot;D&quot;).min()<br>        print(daily_min)<br><br>        return daily_min</pre><p>To test the API, we simply run this command</p><pre>uvicorn src.api.main:app --host 0.0.0.0 --proxy-headers --reload</pre><p>Now, going to this URL <a href="http://0.0.0.0:8000/docs"><em>http://0.0.0.0:8000/docs</em></a><em> </em>will show us the documentation as we will as a testing option as shown below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*g6kFL6OmyyeNLiocSSSU3Q.png" /></figure><p>To test the metadata endpoint, we just click on the name provided like below and click execute.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yLtIWvF30Va8bJmMKxUcvg.png" /></figure><h4>B. Displaying the data using Streamlit</h4><p>Finally, once our API is up, we can invoke them inside a <a href="https://streamlit.io/cloud">Streamlit</a> app and build some cool visualization.</p><p>To be able to run the Streamlit app, we first need to install it like any other Python package:</p><pre>pip install streamlit<br><br>#testing command line<br>streamlit hello<br><br>#testing with a file<br>streamlit run hello.py<br><br></pre><p>To learn more about Streamlit you can check out its documentation for advanced visualization.</p><p>Below is a simple dashboard that I forked from the <a href="https://github.com/dataprofessor/dashboard-v2">DataProfessor</a> GitHub repo and then modified for my use case.</p><pre>#weatherboard_app.py<br><br>import streamlit as st<br>import pandas as pd<br>import plost<br>import altair as alt<br>import math<br><br>from dotenv import load_dotenv<br>from urllib.request import urlopen<br>import json<br>import folium<br>from streamlit_folium import st_folium, folium_static<br>from streamlit_plotly_events import plotly_events<br>import plotly.figure_factory as ff<br>import plotly.express as px<br>import numpy as np<br>from datetime import datetime<br><br>load_dotenv()<br><br>#API_URL = &#39;http://0.0.0.0:8000&#39;<br>API_URL = &#39;http://datahack-api-1:8000/api/v1&#39;<br>st.set_page_config(layout=&#39;wide&#39;, initial_sidebar_state=&#39;expanded&#39;)<br><br>dt_now = datetime.now()<br>dt_day = dt_now.strftime(&#39;%A&#39;)<br>_,colT2 = st.columns([3,7])<br>with colT2:<br>    st.title(&#39;Brisbane Weatherboard&#39;)<br>    st.write(f&quot;### {dt_day}, {dt_now}&quot;)<br>with open(&#39;style.css&#39;) as f:<br>    st.markdown(f&#39;&lt;style&gt;{f.read()}&lt;/style&gt;&#39;, unsafe_allow_html=True)<br>    <br>st.sidebar.header(&#39;Weatherboard `v.1.0`&#39;)<br><br>st.sidebar.subheader(&#39;Map parameter&#39;)<br>time_hist_color = st.sidebar.selectbox(&#39;Display by sensor&#39;, (&#39;Rainfall&#39;, &#39;Stream&#39;)) <br><br>st.sidebar.subheader(&#39;Heat map parameter&#39;)<br>time_hist_color = st.sidebar.selectbox(&#39;Color by&#39;, (&#39;min_value&#39;, &#39;max_value&#39;)) <br><br>#st.sidebar.subheader(&#39;Donut chart parameter&#39;)<br>#donut_theta = st.sidebar.selectbox(&#39;Select data&#39;, (&#39;Active&#39;, &#39;Inactive&#39;))<br><br>st.sidebar.subheader(&#39;Line chart parameters&#39;)<br>plot_data = st.sidebar.multiselect(&#39;Select data&#39;, [&#39;avg_value&#39;, &#39;max_value&#39;,&#39;min_value&#39;], [&#39;avg_value&#39;, &#39;min_value&#39;,&#39;max_value&#39;])<br>plot_height = st.sidebar.slider(&#39;Specify plot height&#39;, 0,2,1)<br><br>st.sidebar.markdown(&#39;&#39;&#39;<br>---<br>Created with ❤️ by [Data Professor](https://youtube.com/dataprofessor/).\n\n<br>                    <br>Modified with passion by Datamatiks.<br>&#39;&#39;&#39;)<br><br># Metrics row <br>st.markdown(&#39;### Weather Metrics&#39;)<br>col1, col2, col3 = st.columns(3)<br><br>#Retrive other weather data from public api<br>response = urlopen(&quot;https://wttr.in/Brisbane?format=j1&quot;)<br>json_data = response.read().decode(&#39;utf-8&#39;, &#39;replace&#39;)<br>d = json.loads(json_data)<br><br>df_brissy = pd.json_normalize(d[&#39;current_condition&#39;])<br>temp = df_brissy[&#39;temp_C&#39;].values[0]<br>wind = df_brissy[&#39;windspeedKmph&#39;].values[0]<br>humid = df_brissy[&#39;humidity&#39;].values[0]<br>col1.metric(&quot;Temperature&quot;, f&quot;{temp} °C&quot;, &quot;1.2 °C&quot;)<br>col2.metric(&quot;Wind&quot;, f&quot;{wind} kph&quot;, &quot;-8%&quot;)<br>col3.metric(&quot;Humidity&quot;, f&quot;{humid}%&quot;, &quot;4%&quot;)<br><br>#retrive metadata<br>metadata = pd.read_csv(<br>    f&#39;{API_URL}/metadata&#39;,<br>    header=0, <br>    names=[&#39;sensor_id&#39;, <br>            &#39;location_id&#39;,<br>            &#39;location_name&#39;,<br>            &#39;sensor_type&#39;,<br>            &#39;unit&#39;,<br>            &#39;latitude&#39;,<br>            &#39;longitude&#39;]<br>)<br><br>#retrieve daily metrics<br>sensors_df = pd.read_csv(f&#39;{API_URL}/sensor_metrics&#39;,header=0)<br>avg_df = pd.read_csv(f&#39;{API_URL}/daily_avg&#39;,header=0)<br>max_df = pd.read_csv(f&#39;{API_URL}/daily_max&#39;,header=0)<br>min_df = pd.read_csv(f&#39;{API_URL}/daily_min&#39;,header=0)<br><br>new_avg_df = avg_df.melt( id_vars=[&quot;measured_datetime&quot;], <br>         var_name=&quot;sensor_id&quot;, <br>         value_name=&quot;avg_value&quot;)<br><br>new_max_df = max_df.melt( id_vars=[&quot;measured_datetime&quot;], <br>         var_name=&quot;sensor_id&quot;, <br>         value_name=&quot;max_value&quot;)<br><br>new_min_df = min_df.melt( id_vars=[&quot;measured_datetime&quot;], <br>         var_name=&quot;sensor_id&quot;, <br>         value_name=&quot;min_value&quot;)<br><br>#merge all metrics<br>merge_keys=[&#39;sensor_id&#39;,&#39;measured_datetime&#39;]<br>daily_metrics_df = new_avg_df.merge(new_max_df, on=merge_keys).merge(new_min_df, on=merge_keys).set_index(&#39;sensor_id&#39;)<br><br>#enrich metrics with metadata<br>joined_df = daily_metrics_df.join(metadata.set_index(&#39;sensor_id&#39;)).reset_index()<br><br><br>date_now_str = str(dt_now.date()) <br>df_filtered = joined_df.loc[(joined_df[&#39;measured_datetime&#39;] == date_now_str)]<br>df_active = df_filtered[df_filtered[&#39;avg_value&#39;]&gt;0].groupby(&#39;sensor_id&#39;).apply(len)<br>df_inactive = df_filtered[df_filtered[&#39;avg_value&#39;]==0].groupby(&#39;sensor_id&#39;).apply(len)<br>df_unknown = df_filtered[df_filtered[&#39;avg_value&#39;].isnull() ].groupby(&#39;sensor_id&#39;).apply(len)<br><br>gauge_status_df = pd.DataFrame(<br>        {&#39;status&#39;:[&#39;Active&#39;,&#39;Inactive&#39;,&#39;Unknown&#39;],                              <br>         &#39;count&#39;: [len(df_active.index),len(df_inactive.index),len(df_unknown.index)]<br>        }<br>)<br><br>### Map plotly version<br>st.markdown(&#39;### Rainfall and Stream AHD Height Sensors Map&#39;)<br>px.set_mapbox_access_token(os.environ[&#39;MAPBOX_TOKEN&#39;] )<br>df = metadata.__deepcopy__()<br><br>df = joined_df.fillna(0)<br>fig = px.scatter_mapbox(df, lat=&quot;latitude&quot;, lon=&quot;longitude&quot;,  hover_name=&quot;location_name&quot;, hover_data=[&quot;sensor_type&quot;, &quot;min_value&quot;], color=&quot;avg_value&quot;, size=&quot;max_value&quot;,<br>                color_continuous_scale=px.colors.cyclical.IceFire, size_max=50, zoom=10, width=1500, height=800)<br>st.plotly_chart(fig)<br><br>#Heatmap<br>c1, c2 = st.columns((7,3))<br>with c1:<br>    st.markdown(&#39;### Heatmap&#39;)<br>    plost.time_hist(<br>    data=joined_df,<br>    date=&#39;measured_datetime&#39;,<br>    x_unit=&#39;hours&#39;,<br>    y_unit=&#39;day&#39;,<br>    color=time_hist_color,<br>    aggregate=&#39;median&#39;,<br>    legend=None,<br>    height=345,<br>    use_container_width=True)<br>with c2:<br>    st.markdown(&#39;### Sensors status&#39;)<br>    plost.donut_chart(<br>        data=gauge_status_df,<br>        theta=&#39;count&#39;,<br>        color=&#39;status&#39;,<br>        legend=&#39;bottom&#39;, <br>        use_container_width=True)<br><br># Row C<br>st.markdown(&#39;### Line chart&#39;)<br>grouped = joined_df.groupby(&#39;sensor_type&#39;)<br>for key, group in grouped:<br>    st.markdown(f&quot;*** {key}&quot;)<br>    #st.write(group)<br>    df = group.set_index(&#39;measured_datetime&#39;)<br>    df = group.astype({&#39;avg_value&#39;: &#39;float&#39;,&#39;max_value&#39;: &#39;float&#39;,&#39;min_value&#39;: &#39;float&#39;})<br>    plost.line_chart(df, <br>        x = &#39;measured_datetime&#39; ,<br>        y = (&#39;min_value&#39;,&#39;max_value&#39;,&#39;avg_value&#39;),<br>        color=&#39;sensor_id&#39;,<br>        height=plot_height, <br>        #pan_zoom=&#39;minimap&#39;<br>    )<br><br>st.write(joined_df.reset_index().sort_values(by=&#39;measured_datetime&#39;, ascending=False))</pre><p>Running the app like this:</p><pre>streamlit run src/web/weatherboard_app.py</pre><p>It will then launch this dashboard.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kmVkioeACyS2vkTPYbSfUw.png" /></figure><p>There we have it, we have access to our DuckDB sensor data using FastAPI and then display the data in a Streamlit dashboard. I added some weather information from another API for weather called <a href="https://wttr.in/">https://wttr.in/</a>.</p><p>Finally, in the last part of this article, we will discuss how we can package all of these technologies together using docker/docker-compose and then deploy them to the Azure cloud. So, watch this space for it.</p><p>Happy reading!</p><blockquote>UPDATE: GitHub repo: <a href="https://github.com/datamatiks/iot-platform-modern-data-stack-poc">https://github.com/datamatiks/iot-platform-modern-data-stack-poc</a></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f8fdce1e8318" width="1" height="1" alt=""><hr><p><a href="https://medium.com/datamatiks/building-an-iot-platform-using-modern-data-stack-part-2-f8fdce1e8318">Building an IoT Platform Using Modern Data Stack — Part 2</a> was originally published in <a href="https://medium.com/datamatiks">Datamatiks</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building an IoT Platform Using Modern Data Stack — Part 1]]></title>
            <link>https://medium.com/datamatiks/building-an-iot-platform-using-modern-data-stack-part-1-02c5460e3f6c?source=rss-dc7bc30d3f7d------2</link>
            <guid isPermaLink="false">https://medium.com/p/02c5460e3f6c</guid>
            <category><![CDATA[prefect]]></category>
            <category><![CDATA[streamlit]]></category>
            <category><![CDATA[fastapi]]></category>
            <category><![CDATA[duckdb]]></category>
            <category><![CDATA[iot]]></category>
            <dc:creator><![CDATA[Niño Francisco Liwa]]></dc:creator>
            <pubDate>Sat, 14 Oct 2023 22:39:39 GMT</pubDate>
            <atom:updated>2023-12-06T11:53:51.921Z</atom:updated>
            <content:encoded><![CDATA[<h3>Building an IoT Platform Using Modern Data Stack — Part 1 (DuckDB + Prefect)</h3><h3>DuckDB + FastAPI + Prefect + Streamlit (DFAPS Stack)</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kqddoRvUhG-tKnl0G2DTPA.png" /></figure><p>Last August 18–20, 2023 I intended to join Australia’s GovHack 2023(<a href="https://govhack.org/">https://govhack.org/</a>) hackathon but because I had other commitments at that time, I had to give it a pass again this year.</p><p>My idea was to create something related to IoT since I am working with IoT data on my day job. I know there will be some open datasets for IoT based on past hackathons. After the event, I browsed through the datasets and luckily enough I found an interesting dataset. Brisbane City Council uploaded the telemetry sensors data for rainfall and stream heights( <a href="https://www.data.brisbane.qld.gov.au/data/dataset/telemetry-sensors-rainfall-and-stream-heights">https://www.data.brisbane.qld.gov.au/data/dataset/telemetry-sensors-rainfall-and-stream-heights</a>). I thought this would be a great starting point for my IoT platform idea.</p><p>Building a platform from scratch is hard enough much more using a modern tech stack that I have not used before. So, my plan is to make it as simple as possible to make it into a Proof-of-Concept (PoC) where I can then make it robust in the future. So here are the technologies that I chose to use for this project. What is so great about these tools is that I can write all my code in Python which is very common nowadays in the data world.</p><ul><li><a href="https://www.prefect.io/">Prefect</a> — For workflow orchestration</li><li><a href="https://duckdb.org/">DuckDB</a> — An in-memory/in-process OLAP SQL database</li><li><a href="https://fastapi.tiangolo.com/">FastAPI</a> — A very fast Python framework for creating APIs</li><li><a href="https://streamlit.io/">Streamlit</a> — A Python web framework for creating Data Apps</li><li><a href="https://www.docker.com/">Docker</a> — For containerization</li><li><a href="https://azure.microsoft.com/">Azure Cloud</a> — For deploying the platform</li></ul><p>At the end of the project, this is what we can see from the dashboard</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qUn5dFCJUdnQ_QlllWJORQ.png" /><figcaption>Credits to the `Data Professor` for the dashboard template</figcaption></figure><blockquote>With so much technology to cover, I decided to break this article into multiple parts to give a more detailed explanation of each component. <strong>For this first part, we are going to discuss Prefect and DuckDB for data extraction and exploration.</strong></blockquote><h4>A. Data Extract using Prefect</h4><p>Upon quick investigation of the dataset site, I found out that there are two CSV files, one contains the metadata of the sensors themselves ( id, type, geolocation, measurement unit, etc), and the other the actual sensor readings for every 15 minutes collected from the device — this file is in wide format CSV where the columns are the sensor_ids and a timestamp and contains the readings for the past 24-hours with 10-mins windows. The figure below might explain better what the file I get looks like.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/622/1*e31uV6zbutolijdGUNDOWw.png" /><figcaption>Data structure inside the sensor reading file.</figcaption></figure><p>From the looks of it, I know I will have multiple duplicate records if I just blindly process and append the records to my raw table. So to minimize this duplication I need to clean them first before inserting them into the DuckDB table. But first I have to extract them from the dataset portal. To do that I just have to grab the download URL from the website and then use a Python script to download it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BJHVJfeZI-j7BnTuQIFxDQ.png" /><figcaption>Inspect the download button and grab the href part</figcaption></figure><blockquote>Now to extract using Prefect we have to create ‘flows’ and ‘tasks’ functions, think of it as ‘DAGS’ for Airflow or Dagster. I like these terminologies because I can immediately relate to the meaning. I should probably have another in-depth article about Prefect one day to discuss all its features. The ‘flow’ decorator in the code below tells the Prefect agent the entry point to the script. Each flow can have multiple tasks and sub-flows but tasks can’t have subtasks inside it( make sense to me, ‘Single Responsibility’)</blockquote><pre>import db as db<br>import httpx<br>from datetime import datetime, date<br><br>from prefect import flow, task, get_run_logger,serve<br>from pathlib import Path<br>from dotenv import load_dotenv<br><br>DOWNLOAD_URL = &quot;https://www.data.brisbane.qld.gov.au/data/dataset/01af4647-dd69-4061-9c68-64fa43bfaac7/resource/78c37b45-ecb5-4a99-86b2-f7a514f0f447/download/&quot;<br><br>&quot;&quot;&quot;<br>Extract latest file from Brisbane Open Data Portal<br>https://www.data.brisbane.qld.gov.au/data/dataset/telemetry-sensors-rainfall-and-stream-heights<br>&quot;&quot;&quot;<br>@task<br>def get_gauge_data() -&gt; str:<br><br>    now = datetime.today()<br>    date_str = str(now.date()) <br>    date_str = date_str.replace(&quot;-&quot;,&quot;&quot;)<br>    print(date_str)<br><br>    filename = f&quot;gauge-data-{date_str}*.csv&quot;<br>    print(&quot;filename:&quot; + filename)<br>    try:<br>        r = httpx.get(f&#39;{DOWNLOAD_URL}{filename}&#39;)<br>    <br>        now_str= str(now).replace(&quot; &quot;,&quot;-&quot;)<br>        outfilename = f&#39;src/data/staging/new-file-{now_str}.csv&#39;<br><br>        with open(outfilename, &#39;w&#39;) as f:<br>            f.write(r.text)<br>    <br>        return outfilename<br>    <br>    except Exception as ex:<br>        print(ex)<br>        raise Exception(f&quot;Something happen: {ex}&quot;)<br><br><br>@flow<br>def extract_file_flow(name=&quot;Gauge Data Extract&quot;, log_prints=True):<br>    logger = get_run_logger()<br>    now = datetime.now()<br>    print(now)<br>    logger.info(f&quot;Run date🤓: - {now} -&quot;)<br><br>    filename = get_gauge_data()<br>    logger.info(f&quot;New file extracted: {filename}&quot;)<br>    #merge_gauge_data(filename)<br><br>if __name__ == &quot;__main__&quot;:<br><br>    extract_file_flow()<br></pre><p>To deploy this to your local environment, you have to start first your local Prefect server. You will need to install Prefect first using ‘pip’ or any Python package manager you have.</p><blockquote><strong><em># pip install prefect</em></strong></blockquote><blockquote><strong><em># prefect server start</em></strong></blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/474/1*U0ONRpf_PSNkUor6XDcZVQ.png" /></figure><p>Now we can deploy our extract flow like this with parameters</p><ul><li>-n — your deployment name</li><li>-q — work pool queue name</li><li>interval — frequency schedule in seconds</li></ul><pre>prefect deployment build src/flows/extract_flow.py:extract_file_flow -n extract-file-dev-deploy-15mins -q default --interval 900</pre><p>Yes! Our flow is now scheduled to be executed every 15 minutes. Prefect will handle all the flow scheduling and execution for us now. If you want to monitor this from the UI you can now head to <a href="http://127.0.0.1:4200/">http://127.0.0.1:4200/</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UvZXvWN_I424H9ITE1xb6A.png" /><figcaption>Slick UI for displaying all deployed flows</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WN_81OVR6RHhOZUJ9_mnEQ.png" /><figcaption>Scheduled flow runs</figcaption></figure><h4>B. Data Seeding to DuckDB</h4><p>To be able to analyse the data we extracted from the Council website we needed a place where we can store them — this is where DuckDB comes in.</p><p>I decided beforehand that I would use a persistent DuckDB instead of the in-session memory so I could save the records in a relation state rather than reading multiples again. In this way, I can append new raw data that comes in and record in an audit table the last insert timestamp that the task processed.</p><p>To create the tables in my DuckDB instance, I downloaded multiple sample files from the source and then wrote a one-off Prefect flow to seed the database taking advantage of DuckDB’s auto-creation of tables from reading a CSV file. The sample code below lets me populate my database.</p><pre>import db as db<br>import pandas as pd<br>import os<br><br>from duckdb import DuckDBPyConnection<br>from prefect import flow, task, get_run_logger,serve<br>from datetime import datetime, date<br>from pathlib import Path<br><br>####### HELPER FUNCTIONS ##########<br>def get_conn():<br>    return db.get_db_conn(&#39;db/datahack.duckdb&#39;)<br><br>def init_seq(seq_name):<br>   conn = get_conn()<br>   return conn.execute(f&quot;CREATE SEQUENCE IF NOT EXISTS {seq_name} START 1;&quot;)<br><br>def init_sensor_table():<br>    conn = get_conn()<br>    #create metadata look up table<br>    conn.execute(<br>        &quot;&quot;&quot;<br>            DROP TABLE IF EXISTS gauge_sensors_metadata; <br>            CREATE TABLE gauge_sensors_metadata AS <br>            SELECT * <br>            FROM read_csv_auto<br>            (&#39;src/data/metadata/rainfall-and-stream-heights-metadata-20230917t110000.csv&#39;)<br>        &quot;&quot;&quot;<br>    )<br>    <br>def init_audit_table():<br>    conn = get_conn()<br>    conn.execute(<br>        &quot;&quot;&quot;<br>            DROP TABLE IF EXISTS ingest_pipeline_audit_log ;<br>            CREATE TABLE ingest_pipeline_audit_log<br>            (<br>                auditid integer primary key DEFAULT NEXTVAL(&#39;seq_auditid&#39;),<br>                ingest_filename varchar(255) not null,<br>                last_datetime datetime not null,<br>                updated_by varchar(255),<br>                updated_date datetime<br>            )<br>        &quot;&quot;&quot;<br>    )<br>   <br><br>def insert_to_audit_table(record):<br>     conn = get_conn()<br>     query = f&quot;&quot;&quot;<br>        INSERT INTO ingest_pipeline_audit_log (<br>            ingest_filename, <br>            last_datetime, <br>            updated_by, <br>            updated_date<br>        ) <br>        VALUES {record}<br>     &quot;&quot;&quot;<br>     return conn.execute(query)<br> <br>def get_last_datetime():<br>    conn = get_conn()<br>    last_datetime = conn.sql(<br>        &quot;&quot;&quot;<br>        SELECT last_datetime <br>        FROM ingest_pipeline_audit_log<br>        WHERE auditid = <br>            ( SELECT max(auditid) <br>            FROM ingest_pipeline_audit_log<br>            )<br>        &quot;&quot;&quot;<br>        ).fetchone()[0]<br>    <br>    print(f&#39;last_datetime:{last_datetime}&#39;)<br>    return last_datetime<br><br><br>####### TASKS FUNCTIONS ##########<br><br>@task<br>def init_tables():<br>    <br>    init_seq(&quot;seq_auditid&quot;)<br>    init_sensor_table()<br>    init_audit_table()<br><br><br>@task<br>def display():<br>    conn = get_conn()<br>    conn.sql(&#39;SELECT * FROM ingest_pipeline_audit_log&#39;).show()<br>    conn.sql(&#39;SELECT * FROM ingest_gauge_metrics&#39;).show()<br><br>@task<br>def process_seeds(filepath: str):<br>    conn = get_conn()<br>    files =[ f for f in os.listdir(filepath) ]<br>    files_sorted = sorted(files)<br><br>    #Loop all through seed files<br>    for index, filename in enumerate(files_sorted):<br>        print(f&quot;Processing --- {filename}&quot; )<br>        filepath = Path(&quot;src/data/seeds&quot;) / filename<br>        tmp_df = conn.sql(f&quot;SELECT * FROM read_csv_auto(&#39;{filepath}&#39;,all_varchar=true)&quot;).df()<br>        tmp_df[&quot;measured_datetime&quot;] = pd.to_datetime(tmp_df[&#39;Measured&#39;])<br>        tmp_df[&quot;measured_day&quot;] = pd.to_datetime(tmp_df[&#39;Measured&#39;]).dt.date<br>        tmp_df[&quot;measured_hour&quot;] = pd.to_datetime(tmp_df[&#39;Measured&#39;]).dt.hour<br>        tmp_df[&quot;measured_minute&quot;] = pd.to_datetime(tmp_df[&#39;Measured&#39;]).dt.minute<br>        tmp_df = tmp_df.drop([&quot;Measured&quot;], axis=1)<br>        <br>        tmp_df = tmp_df.replace([&#39;-&#39;], &#39;0&#39;)<br>        <br>        #Create the table on first file<br>        if index == 0:<br>            <br>            drop_table(&#39;ingest_gauge_metrics&#39;)<br>            conn.execute(&quot;CREATE TABLE ingest_gauge_metrics AS SELECT * FROM tmp_df&quot;)<br><br>            last_datetime = conn.execute(&#39;SELECT MAX(measured_datetime) FROM ingest_gauge_metrics&#39;).fetchone()[0]<br><br>            print(&#39;Initial ingest&#39;)<br>            print(f&#39;Processing {filename}&#39;)<br>            print(f&#39;last_datetime : {last_datetime}&#39;) <br>           <br>            audit_entry = (filename, last_datetime.strftime(&#39;%Y-%m-%dT%H:%M:%S&#39;), &#39;datamatiks&#39;,datetime.now().strftime(&#39;%Y-%m-%dT%H:%M:%S&#39;))<br>            insert_to_audit_table(audit_entry)<br>        else:<br>            sensors_tbl_stg = conn.sql(&quot;SELECT * FROM tmp_df&quot;)<br>            curr_last_datetime = get_last_datetime()<br>            new_df = conn.sql(f&quot;SELECT * FROM sensors_tbl_stg where measured_datetime &gt; &#39;{curr_last_datetime}&#39;&quot;).df()<br><br>            conn.execute(&#39;INSERT INTO ingest_gauge_metrics SELECT * FROM new_df&#39;)<br>            new_last_datetime = conn.sql(&#39;SELECT MAX(measured_datetime) FROM sensors_tbl_stg&#39;).fetchone()[0]<br><br>            audit_entry = (filename, new_last_datetime.strftime(&#39;%Y-%m-%dT%H:%M:%S&#39;),&#39;datamatiks&#39;,datetime.now().strftime(&#39;%Y-%m-%dT%H:%M:%S&#39;))<br>            insert_to_audit_table(audit_entry)<br><br>####### FLOW FUNCTIONS ##########<br>@flow<br>def init_db_flow(name=&quot;Initialize DB flow &quot;, log_prints=True):<br>    logger = get_run_logger()<br>    now = datetime.now()<br>    print(now)<br>    logger.info(&quot;%s Run date🤓:&quot;, now)<br><br>    init_tables()<br>    process_seeds(&#39;src/data/seeds&#39;)<br>    display()<br><br>if __name__ == &quot;__main__&quot;:<br>    init_db_flow()</pre><p>In order to avoid duplicate records, I checked for the <em>last_datetime</em> I recorded in an audit table and then just read the records from the current data frame that has the <em>last_datetime</em> after the last recorded <em>last_datetime</em>. To ensure it is processing the correct order of files. I have sorted the list of filenames before parsing them.</p><p>With that, I was able to create my database now.</p><h4>C. Exploring my data using DuckDB CLI</h4><p>To explore my database, I installed the no-dependencies <a href="https://duckdb.org/docs/archive/0.4.0/installation/index"><em>duckdb</em></a><em> cli</em> binary.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/825/1*3HPMjAopyZU6_mcvGt2pkw.png" /></figure><p>I intentionally put the binary file inside my project folder for convenient access to my duckdb file.</p><p>To use this cli, it is similar to any SQL console application out there. You use the familiar SQL commands to display and query tables.</p><p>In order to open a persistent database you should this command below :</p><pre>D .open &lt;dbfilename&gt;.duckdb</pre><p>More commands can be found in their documentation — <a href="https://duckdb.org/docs/archive/0.4.0/api/cli">https://duckdb.org/docs/archive/0.4.0/api/cli</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FpM7pZOXmflJk7xHAZekDA.png" /><figcaption>A simple view of the tables using DuckDB cli</figcaption></figure><blockquote><em>That’s it for this part and please check out Part 2 of this article to learn more about FastAPI and Streamlit.</em></blockquote><blockquote><em>Thanks for reading.</em></blockquote><blockquote>UPDATE: GitHub repo: <a href="https://github.com/datamatiks/iot-platform-modern-data-stack-poc">https://github.com/datamatiks/iot-platform-modern-data-stack-poc</a></blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=02c5460e3f6c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/datamatiks/building-an-iot-platform-using-modern-data-stack-part-1-02c5460e3f6c">Building an IoT Platform Using Modern Data Stack — Part 1</a> was originally published in <a href="https://medium.com/datamatiks">Datamatiks</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I passed the AZ-900 Microsoft Azure Fundamentals test by riding the bus to work.]]></title>
            <link>https://medium.com/datamatiks/how-i-passed-the-az-900-microsoft-azure-fundamentals-test-by-riding-the-bus-to-work-c73ad14bed91?source=rss-dc7bc30d3f7d------2</link>
            <guid isPermaLink="false">https://medium.com/p/c73ad14bed91</guid>
            <category><![CDATA[az-900-study-material]]></category>
            <category><![CDATA[cloud-certification]]></category>
            <category><![CDATA[azure-cloud]]></category>
            <category><![CDATA[azure-certification]]></category>
            <category><![CDATA[azure]]></category>
            <dc:creator><![CDATA[Niño Francisco Liwa]]></dc:creator>
            <pubDate>Sat, 09 Jan 2021 10:41:45 GMT</pubDate>
            <atom:updated>2021-01-09T10:45:48.066Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WYNSxOlYbg6GSemoSuMP4A.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@chan_lee94?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">chan lee</a> on <a href="https://unsplash.com/s/photos/bus?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>Yes, you heard it right I actually did take a bus to work that time. Last year, about the same time as now, I moved my family to the sunny city of Brisbane, Queensland Australia from Sydney. I had an offer from a big travel company whose HQ is based here. It was still pre COVID-19 period then so it was okay to take public transport. My team here is using Azure Cloud Services to do Data Engineering however by that time I have not much experience with Azure so I have to learn and catch up fast. In order to do that, my manager told all of us to take Azure certifications.</p><p>This article lays out my strategy on how to pass the AZ-900 certification and that includes the daily bus ride to work I had(will come to this, promise ;D ). Of course, everyone has a different preparation style for a certain exam or certification. The strategy I will mention here works for me as I have been<br>applying them since college and it’s what works best for me.</p><h3><strong>Pre-review/study practice test</strong></h3><p>Ever since college, I have always applied this strategy when I am preparing for an exam/certification test. I will always first do a pre-review/study practice test before I embark on learning or studying about something. I will find some practice exam or practice test about the topic or certification I am going to take and then just go through it and try to answer it to the best of my knowledge, it does not matter if I have prior knowledge or not, I just want to evaluate what I know and I don’t know about something. The result tells me how much more I need to learn or study about it. It will also help me gauge how much improvement I have achieved during succeeding practice tests.</p><p>In the case of AZ-900, I researched three possible sites where I can have my practice test.</p><h4>1. Official Practice Test by MindHub</h4><p>If you go to the site of <a href="https://docs.microsoft.com/en-us/learn/certifications/exams/az-900">Microsoft certification</a>, you will actually see a small link there that points you to its official practice test. However, when you click it, you will see that you actually have to pay at least $130 for 30 days of access. For me, this is a lot of money to spend so I opted not to try it. They do have the free demo but will only give you five practice questions. I have not tried their questions but their description says their questions are similar to the actual exam questions. So, if you are up to it, you can try them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WGNPL0h-3E8cGGT6xg44og.png" /></figure><h4>2. ITEXAMS.com</h4><p>I learned about this <a href="https://www.itexams.com/">site</a> from a colleague who uses it on his practice test and review. I love that it has a free tier and has plenty of practice questions for AZ-900(192 questions to be exact). I must say their questions are closely similar to the actual exam questions. It also has an explanation for the correct answer. They seem to update their questions regularly as well.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZXTQjukUvNuBIKpBs1L6dg.png" /></figure><p>What’s annoying though is it can only display 5 questions per page and you have to click a captcha to go to the next page. If you want to activate or unlock a custom appearance like more questions per page then you have to upgrade to a premium account. I can’t complain enough though, you get real exam questions using the free tier and that’s what matters.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_DkP-E0HbgSo1jp4FSX0bw.png" /></figure><p>They also don’t have the timer and mark for review features just like the actual exam. But who knows, maybe they will add it in the future.</p><h4>3. Whizlabs</h4><p>Finally, I stumbled upon <a href="https://www.whizlabs.com/">Whizlabs</a> while reading Quora and I never regretted using them. They offer cloud certification practice tests as well as other courses that will help you pass any certifications. What’s best is, it is cheaper than the previous two considering you don’t need a subscription to have access to their practice test.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mCQxqM0L6Z77jzS78Cl0rg.png" /></figure><p>They mock the actual exam scenario where you can mark questions for review and the timer feature helps you rate yourself if you are able to finish the actual exam on time or not. <br>So I decided to try them and used them for my pre-review/study practice test. It is really helpful because they tracked your score and the number of attempts to the practice test. That way you can see your progress.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*V2xiy_YC2w2aIliBUpdhkw.png" /></figure><p>Check them out if you are interested. They always give vouchers and freebies. And also, you have lifetime access to those practice tests, sweet! right?!</p><h3>Downtime study/review session</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eA9fC65UcV_IPI7jFJKWkw.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@rozetsky?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Ant Rozetsky</a> on <a href="https://unsplash.com/s/photos/bus?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>Alright, so after my pre-review/study evaluation test. I now know where to focus more on my study or in this case review for AZ-900 certification. What is good about Whizlab is that it gives you a report of your practice test showing where you have more incorrect answers. Based on that I can focus my lesson/review plans.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UetuO1tcX8OHodiCjksXXQ.png" /></figure><p>Remember that bus ride that I mentioned above? This is where it comes to play. The bus ride to/from work is downtime for me. I have always tried to use my downtime for something productive or learn new things, so this where I usually insert my review and study for certifications. <br>I have 30 mins bus ride going to work and another 30 mins going home so I make use of them to study/review for the certifications. I allowed five days to study the <strong>AZ-900 Certification course</strong> in<a href="https://acloudguru.com/course/az-900-microsoft-azure-fundamentals-2020"> <strong><em>ACloudGuru by Lars Klint</em></strong></a>. I used to have access to the company subscription so I leverage on that.</p><p>So I will be seated at the back of the bus while watching on my phone Lars Klint explain the topics for Azure fundamentals. I skimmed those topics I already know about or make the video speed at 4X to those I am familiar with. I focus more on those topics I have low scores on my practice tests. For 1 week I did this during my bus ride as well as while waiting for the bus.</p><p>For the next week, it’s all about the practice test. I will do one practice in the morning bus ride to work and another practice test on the evening bus ride home. I accessed the Whizlab practice test using my phone, it has the same experience while doing it on the laptop. Sometimes I am so engaged in a practice test that I will miss my stop or took the wrong bus home. Taking the wrong bus home gave me more time to practice tests though so it’s not bad at all. I did this practice test until the scheduled exam is one day away.</p><h3>Schedule when ready</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yWzpooCciQr7NqGpe9uj-Q.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@zs_photography_s?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Mohd Zuber saifi</a> on <a href="https://unsplash.com/s/photos/alarm-clock?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>What I meant by this is that whenever I felt that I am ready to take the certification then that is the time I will book or schedule the actual certification exams. When do I know I am ready to take it is based on my practice test results. When I see that I got consistently high marks on the practice for the last three or four practice tests then I believe I am ready.<br>I will schedule the exam and then just continue with my bus ride practice test until the exam date.</p><p>For AZ-900, you must at least get an 80% score on the exam to pass it. So to make sure I will pass it, I always aim to get at least 90% or above score during my practice test consistently. That way I have a bit of margin of confidence during the actual exam. I always aim high so if I missed my 90% target at least I will still pass the 80% exam passing score. It’s always good to set yourself a goal so that you know where you going and what needed to be done to achieve it.</p><h3>Point of no return — Exam date!</h3><p>During the exam date, I did one practice test in the morning and then relax the whole afternoon. By the way, I’ve chosen the home or office proctored test as this was the time all the testing centers were closed due to COVID-19. So, this is also my first time taking an exam at home. It is really weird because I have to choose a time in the middle of the night where everything is quiet and the internet is fast. The proctored ask me to show the whole room taking my laptop with me circling around my test area, to see if nobody is around. Once, all the checks were done, the exam started.</p><p>I made use of my time to really read and review questions before marking the answers. It also helps that my practice test questions were really similar to the real questions. It makes the test smooth and the experience similar to the practice test as well. At the end of the exam, you will get your result if you pass or not. Luckily I passed!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CEO5nmQtWT8qIAoIjs4UhQ.png" /></figure><p>So, that’s it! hopefully, you learn something from my experience.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c73ad14bed91" width="1" height="1" alt=""><hr><p><a href="https://medium.com/datamatiks/how-i-passed-the-az-900-microsoft-azure-fundamentals-test-by-riding-the-bus-to-work-c73ad14bed91">How I passed the AZ-900 Microsoft Azure Fundamentals test by riding the bus to work.</a> was originally published in <a href="https://medium.com/datamatiks">Datamatiks</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Using Azure Data Studio to connect to Postgres instance in Docker]]></title>
            <link>https://medium.com/datamatiks/using-azure-data-studio-to-connect-to-postgres-instance-in-docker-5fae4dfff1f4?source=rss-dc7bc30d3f7d------2</link>
            <guid isPermaLink="false">https://medium.com/p/5fae4dfff1f4</guid>
            <category><![CDATA[azure-data-studio]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[postgresql]]></category>
            <category><![CDATA[postgres]]></category>
            <dc:creator><![CDATA[Niño Francisco Liwa]]></dc:creator>
            <pubDate>Mon, 23 Nov 2020 05:22:00 GMT</pubDate>
            <atom:updated>2020-11-23T05:22:00.837Z</atom:updated>
            <content:encoded><![CDATA[<p>For a while, data professionals have been accessing Postgres database using old reliable GUI’s like <a href="https://www.pgadmin.org/"><em>pgAdmin</em></a><em> </em>and <a href="https://dbeaver.io/"><em>DBeaver </em></a>as well as fantastic cli tools like <a href="http://postgresguide.com/utilities/psql.html"><em>psql</em></a> and <a href="https://www.pgcli.com/"><em>pgcli</em></a>.</p><p>However, if you have been working on cloud platforms like AWS, GCP or Azure, it feels more natural to use tools and utilities that are native with your cloud provider.</p><p>Today, I want to introduce how you can <strong>connect </strong>to your <strong>Postgres</strong> DB instance in Azure as well as in your<strong> local docker instance</strong> using <strong>Azure Data Studio</strong>.</p><h4>What is Azure Data Studio?</h4><p>- Azure Data Studio is a cross-platform database tool for data professionals using on-premises and cloud data platforms on Windows, macOS, and Linux.</p><p>- Azure Data Studio offers a modern editor experience with IntelliSense, code snippets, source control integration, and an integrated terminal. It’s engineered with the data platform user in mind, with built-in charting of query result sets and customizable dashboards.</p><p>- The source code for Azure Data Studio and its data providers is available on GitHub under a source code EULA that provides rights to modify and use the software, but not to redistribute it or host it in a cloud service.</p><p>For more of it’s feature you can read it here <a href="https://docs.microsoft.com/en-gb/sql/azure-data-studio/what-is-azure-data-studio?view=sql-server-ver15"><strong>Azure Data Studio</strong></a></p><h4>Download and Install Azure Data Studio</h4><ul><li>Head over to <a href="https://docs.microsoft.com/en-gb/sql/azure-data-studio/download-azure-data-studio?view=sql-server-ver15"><em>download here</em></a> to get the latest version of ADS that is suited to your development setup.</li><li>Once you have installed it, just launched the app you should be able to see homepage similar as below.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wCSw5sMGkfR8Jcn5Rl3muA.png" /><figcaption>Azure Data Studio Homepage</figcaption></figure><h4>Setting up Postgres docker instance</h4><p>If you have not installed <strong>docker desktop</strong> before there is a good documentation on their site showing how to install docker on mac, windows and Linux . I will not be including it in this article but this<strong><em> </em></strong><a href="https://docs.docker.com/desktop/"><em>guide</em></a><em> </em>will get you started.</p><p>For this article, we are going to use <strong>docker-compose</strong> which is included when you install docker desktop.</p><p>Below is a bare minimum docker-compose file that creates a postgres db instance in your local setup.</p><ol><li>Create a directory for your postgres docker ( ex. <em>mkdir postgres_docker</em>)</li><li>Chage to that directory (<em> cd postgres_docker</em> )</li><li>Create a file called docker-compose.yml. Paste the following code</li></ol><pre>version: &#39;3&#39;<br>services: <br>  postgres:<br>    image: postgres<br>    restart: on-failure<br>    environment:<br>       - POSTGRES_USER=postgres<br>       - POSTGRES_PASSWORD=password123<br>       - PGDATA=/var/lib/postgresql/data/pgdata<br>       - POSTGRES_DB=postgres<br>    volumes:<br>       - ./postgres_data:/var/lib/postgresql<br>    ports:<br>       - 5432:5432</pre><p>Here, we grab the postgres docker image, then set default database name(“postgres”) and default credentials ( username + password ).</p><p>We also set a volume in the local machine to make the data persistent even if we shutdown the docker instance. Then finally, mapping the localhost port to the port in the docker image.</p><h4>Start Postgres docker image</h4><p>Start docker image by running <strong><em>“docker-compose up”</em></strong> inside the directory where you save your <strong>docker-compose.yml </strong>file.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QUpa4wp5xW1PmiuD780Y9g.png" /></figure><h4>Connecting to Postgres from Azure Data Studio</h4><h4>pre-requisite</h4><p>In order to access Postgres SQL from Azure Data Studio we first need to add the <a href="https://docs.microsoft.com/en-us/sql/azure-data-studio/extensions/postgres-extension?view=sql-server-ver15"><em>PostgresSQL extensio</em>n</a> which is still in <strong>Preview</strong>. After you install this then you can start connecting to Postgres already.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*itvsNgY2QwgCWUzqduq0Zw.png" /></figure><ol><li>Now, go to your Azure Data Studio and add new connection.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/825/1*po_L2JeDnSGT1IFHoaGDMg.png" /></figure><p>2. Type the server credentials you have set in the docker-compose file.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/517/1*P6lfDOrlvIZv2bsz8KpIjQ.png" /><figcaption>Insert credentials here</figcaption></figure><p>3. Connected!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hv5dwEL7fCJ0OwFnTgiC7A.png" /><figcaption>Green means go!</figcaption></figure><h4>Testing and Querying</h4><p>Now you can start playing around the GUI and write some queries</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/742/1*1j-4Qfa4COMbPd7PwMxuPg.png" /><figcaption>creating a database</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/692/1*k_MdOlMDmiMltFzbUgsl9w.png" /><figcaption>do some query</figcaption></figure><h4>Bonus — Reporting and Extract</h4><p>Some download options , so you can download the result the way you want it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/703/1*dsFI9SfsQ5PPXhS0HoQs1A.png" /><figcaption>Dowload as csv, xls, json and xml</figcaption></figure><h4>Simple Vizualization</h4><p>You can also display the results with some default charting options.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*u6V5lO__W4AWhmk8C9AdMQ.png" /><figcaption>woohoo! colors!</figcaption></figure><p>That’s it! hope you learn something from this article and start using Azure Data Studio on your projects for accessing Postgres databases.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5fae4dfff1f4" width="1" height="1" alt=""><hr><p><a href="https://medium.com/datamatiks/using-azure-data-studio-to-connect-to-postgres-instance-in-docker-5fae4dfff1f4">Using Azure Data Studio to connect to Postgres instance in Docker</a> was originally published in <a href="https://medium.com/datamatiks">Datamatiks</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Running Kafka and KSQL on your Raspberry Pi 4]]></title>
            <link>https://medium.com/datamatiks/running-kafka-and-ksql-on-your-raspberry-pi-4-4053e925f2b1?source=rss-dc7bc30d3f7d------2</link>
            <guid isPermaLink="false">https://medium.com/p/4053e925f2b1</guid>
            <category><![CDATA[covid19]]></category>
            <category><![CDATA[kafka]]></category>
            <category><![CDATA[iot]]></category>
            <category><![CDATA[ksql]]></category>
            <category><![CDATA[raspberry-pi]]></category>
            <dc:creator><![CDATA[Niño Francisco Liwa]]></dc:creator>
            <pubDate>Tue, 14 Apr 2020 13:13:08 GMT</pubDate>
            <atom:updated>2020-04-16T09:53:24.152Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ur4nECOotSRKEKxwVGA-QQ.png" /></figure><p>For all the IoT enthusiasts out there I am sure you already heard the latest release of Raspberry Pi last year which the Raspberry Pi 4 Model B. If you have not heard about it nor have no idea about what is it then probably it’s good checking out this article — <a href="https://www.zdnet.com/article/what-is-the-raspberry-pi-4-everything-you-need-to-know-about-the-tiny-low-cost-computer/">what is the raspberry pi 4?</a> as this is not the focus of this article.</p><p>I received my Pi 4 as a Christmas gift from my sister in law back in the US but I have not it until this COVID-19 home isolation situation going on. At first, I only installed a Minecraft server so that I and my kids can play the hacked version of Minecraft while staying at home. But the kids got bored and leave the server lying around without no one using it.</p><p>So I decided I should try to run something else into it. It’s a good thing I am in the middle of learning about <a href="https://www.confluent.io/product/ksql/">KSQL</a>- Confluent’s streaming SQL engine that enables real-time data processing against Apache Kafka. This is good if you have some real-time analytics or IoT projects you want to build.</p><p>Below is a step-by-step guide on installing and running of Kafka and KSQL into Raspberry Pi 4 based on my own experience with notes and links to some issues I encountered during the process.</p><h3>1. Setup your Raspberry Pi</h3><p>I am not going to expand more about this but head to <a href="https://projects.raspberrypi.org/en/projects/noobs-install">raspberrypi.org</a> to know about preparing your Pi and installing OS. I would recommend using NOOBS and Raspbian OS for a starter.</p><h3>2. Confluent Kafka/KSQL Installation</h3><p>Once your Pi is ready, make sure you can connect to it using ssh so that you can easily access it from your laptop terminal.</p><p>To download the self-manage Confluent software go to <a href="https://www.confluent.io/download">https://www.confluent.io/download</a> and download the appropriate software. You may need to register to get the free software.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ppp6LuCAl5mdD8a1mBO_0w.png" /></figure><p>Once you got the tar file downloaded, you can now secure transfer it to your Pi.</p><pre>scp confluent-5.4.1–2.12.tar.gz pi@xxx.xxx.x.xx:/home/pi/</pre><p>Next, access your Pi through ssh and uncompress the confluent tar file.</p><pre>tar -xzvf confluent-5.4.1–2.12.tar.gz</pre><p>There you have it, you have now the confluent software in your Pi. But wait! How do we run it? Good question.</p><h3>3. Running Apache Kafka</h3><p>By right, running Apache Kafka on a real server ( Linux machine/ Window Server) is simple. Confluent has a good <a href="https://docs.confluent.io/current/cli/command-reference/confluent-local/confluent_local_start.html">utility</a> to start Kafka and all its components automatically for us.</p><p>If you go to the recently created confluent folder and run this command:</p><pre>bin/confluent start local</pre><p>It will have an error instead of running all the services as shown below</p><pre>Starting zookeeper<br>zookeeper is [UP]<br>Starting kafka<br>kafka is [UP]<br>Starting schema-registry<br>schema-registry is [UP]<br>Starting kafka-rest<br>kafka-rest is [UP]<br>Starting connect<br>connect is [UP]<br>Starting ksql-server<br>ksql-server is [UP]<br>Starting control-center<br>control-center is [UP]</pre><p>The error looks like this below. It seems like ARM is not supported by this utility.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xRk2Zl8OuWF8gLpUdt1yWQ.png" /></figure><p>So, what I did is just execute it manually. I would recommend installing screen utility so that you can run the components in the background on its own screen.</p><pre>sudo apt-get install screen</pre><p>Now you can do this.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HYGbrfpAFn8Ogxf6XsHuRQ.png" /></figure><p>To run Apache Kafka, we first need to run the Zookeeper component. Think of it as a Cluster Manager, it manages and syncs all servers in a Kafka cluster. To manually start it, open a new screen with a name.</p><pre>screen -S zookeeper</pre><p>Once inside the screen, you can now start zookeeper. But first, change zookeeper dataDir to a persistent location not ‘/tmp’.</p><blockquote>ex. dataDir=/var/zookeeper</blockquote><pre>bin/zookeeper-server-start etc/kafka/zookeeper.properties</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9qJyPdM5FQrOZnTjEZkTMA.png" /></figure><p>To detach the screen and go back to the main screen just do Ctrl A+D ( for Mac Users ). To resume screen do :</p><pre>screen -r zookeeper or screen -r &lt;screen-id&gt;</pre><p>Now, do the same thing as above to run Kafka server. Modify the server property log.dirs to a persistent location too.</p><blockquote>ex. ‘log.dirs=/var/kafka-logs’</blockquote><pre>screen -S kafka-server<br>bin/kafka-server-start etc/kafka/server.properties</pre><p>If all goes well you now have a running Kafka Cluster with 1 broker and 1 zookeeper. Now we are ready to run KSQL server.</p><h3>4. Testing your Kafka Cluster</h3><h4>Creating a topic</h4><pre>bin/kafka-topics --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic ksql-pi-demo-topic</pre><h4>Publishing to a topic</h4><p>The simplest message we can publish to a topic is a string. But the most common use case in the real-world is a CSV file or delimited records. So let’s try that here. Just input a delimited record per line as seen below.</p><pre>bin/kafka-console-producer --broker-list localhost:9092 --topic ksql-pi-demo-topic</pre><pre>&gt;abc123,test123,nsw<br>&gt;abc456,test456,qld<br>&gt;abc789,test789,vic<br>&gt;abc111,test111,qld</pre><h4>Consuming messages from a topic</h4><p>Now we consume the record. We should see the exact sequence of data we published.</p><pre>bin/kafka-console-consumer --bootstrap-server localhost:9092 --from-beginning --topic ksql-pi-demo-topic</pre><pre>abc123,test123,nsw<br>abc456,test456,qld<br>abc789,test789,vic<br>abc111,test111,qld</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*b518VxBvmJ7toj_hg2rCsQ.png" /></figure><h3>5. Running KSQL Server and CLI</h3><p>KSQL server needs to access the schema registry which contains the schema of all the records for all topics that the Kafka cluster publishes. KSQL Server is like a group JVM nodes connecting to the Kafka cluster to process streams in a realtime manner using a familiar SQL like syntax. It is the query engine for streaming data in Kafka. To access those schemas we need to start the schema-registry.</p><pre>bin/schema-registry-start etc/schema-registry/schema-registry.properties</pre><p>After the schema-registry is up, we can start KSQL server. Since we are running from a Pi, we need to set some environment variables first so that we will not have and issue starting the server By default KSQL server needs a huge amount of RAM since it is doing its process in-memory. But for demo purpose in a Pi, we can set it at lower values.</p><pre>export KSQL_HEAP_OPTS=”-Xms1G -Xmx2G”</pre><p>I set it to that value so that I can still have more RAM left on my Pi. You change it according to your Pi’s spec. We can now start the KSQL Server.</p><pre>KSQL_OPTS=”-Djava.io.tmpdir=/home/pi/javatmp/” bin/ksql-server-start etc/ksql/ksql-server.properties</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tGAlpeiXAGC5mOwN24BdQg.png" /></figure><p>Finally, when the server is up and running we can now start the KSQL-CLI to analyze streams of records from a topic.</p><pre>bin/ksql <a href="http://0.0.0.0:8088/">http://0.0.0.0:8088</a></pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9u5N_mV4xVidWoAgK5uTUA.png" /></figure><h4>6. Creating and Testing KSQL Streams</h4><p>To show the topics you have created in Kafka execute this command from the ksql-cli:</p><pre>show topics;</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/920/1*nYWhIRBx_N3gklH9ixjHqg.png" /></figure><h4>Creating Stream from a topic</h4><p>To be able to analyze streams in KSQL we have to create the stream first. It’s like a table that you can query.</p><pre>ksql&gt; CREATE STREAM ksqlpi_demo_streams (ID VARCHAR, NAME VARCHAR,STATE VARCHAR)<br>&gt; WITH (kafka_topic=’ksql-pi-demo-topic’,value_format=’DELIMITED’);</pre><pre>Message<br> — — — — — — — — <br> Stream created<br> — — — — — — — —</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*T__AeYbE29Irod0jGFSQwQ.png" /></figure><h4>Querying stream in realtime</h4><p>This is how you can finally monitor all the records inside a Kafka topic using SQL.</p><pre>select * from KSQLPI_DEMO_STREAMS EMIT CHANGES;</pre><p>Below is a video showing KSQL in Action.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fgi9mYjX2Tno%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dgi9mYjX2Tno&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fgi9mYjX2Tno%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="640" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/e85010f7b6fe37591f6cfa752f298e3b/href">https://medium.com/media/e85010f7b6fe37591f6cfa752f298e3b/href</a></iframe><p>That’s all folk. Now you can do some amazing IoT stuff in your Raspberry Pi plus the power of Kafka and KSQL. I will continue to document my learning about KSQL and its features, so watch this space for more cool stuff.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4053e925f2b1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/datamatiks/running-kafka-and-ksql-on-your-raspberry-pi-4-4053e925f2b1">Running Kafka and KSQL on your Raspberry Pi 4</a> was originally published in <a href="https://medium.com/datamatiks">Datamatiks</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What is life?!]]></title>
            <link>https://nifrali.medium.com/what-is-life-bd8e8b5aecad?source=rss-dc7bc30d3f7d------2</link>
            <guid isPermaLink="false">https://medium.com/p/bd8e8b5aecad</guid>
            <category><![CDATA[life]]></category>
            <dc:creator><![CDATA[Niño Francisco Liwa]]></dc:creator>
            <pubDate>Thu, 29 Nov 2018 09:34:00 GMT</pubDate>
            <atom:updated>2018-11-29T12:01:07.209Z</atom:updated>
            <content:encoded><![CDATA[<p>Just read thru this over some page in the net. From now on, this is gonna be what life is for me…Thanks Mother Teresa of Calcutta!</p><p><strong>Life is an opportunity, benefit from it.<br> Life is beauty, admire it.<br> Life is bliss, taste it.<br> Life is a dream, realize it.<br> Life is a challenge, meet it.<br> Life is a duty, complete it.<br> Life is a game, play it.<br> Life is a promise, fulfill it.<br> Life is sorrow, overcome it.<br> Life is a song, sing it.<br> Life is a struggle, accept it.<br> Life is a tragedy, confront it.<br> Life is an adventure, dare it.<br> Life is luck, make it.<br> Life is too precious, do not destroy it.<br> Life is life, fight for it.</strong></p><p><strong><em>Mother Teresa</em></strong></p><p><em>Originally published at </em><a href="http://nifrali.blogspot.com/2011/07/what-is-life.html"><em>nifrali.blogspot.com</em></a><em> on November 29, 2018.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bd8e8b5aecad" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[airbnb for breastmilk]]></title>
            <link>https://nifrali.medium.com/airbnb-for-breastmilk-98b9ffdc3f06?source=rss-dc7bc30d3f7d------2</link>
            <guid isPermaLink="false">https://medium.com/p/98b9ffdc3f06</guid>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[parenting]]></category>
            <category><![CDATA[breastfeeding]]></category>
            <dc:creator><![CDATA[Niño Francisco Liwa]]></dc:creator>
            <pubDate>Wed, 30 Mar 2016 03:12:49 GMT</pubDate>
            <atom:updated>2016-03-30T03:12:49.560Z</atom:updated>
            <content:encoded><![CDATA[<p>My wife is giving birth to a “twin” this May — one is our baby boy and the other one is her side project mobile app startup. While she is staying back in Cebu, Philippines to have the baby delivered, she is going to oversee a soft-launch of the mobile app we are currently developing. While the mobile app is her idea, I am helping her do some technical stuff whenever I am free. The main purpose of the app is to become a social platform/marketplace for breastfeeding mothers. She calls it “HandyNanay. For moms, by moms.”</p><p>She had always wanted to breastfeed our children since our first born, but because of commitment to work and lack of milk produced, she had to stop when she had just already started and switched to formula. This time, she wanted to breastfeed the new baby again, but knowing her commitment to work she might face the same challenge of stopping her breastfeeding journey again. Although this time she is more prepared — she browses through the Internet to research ahead and see how other working mothers do it. It turns out that it is not only her having this problem — there a lot of mothers out there who wants to breastfeed but faces the same dilemma. Searching for solutions on the Internet like a marketplace or any local website to do logistics and deliver breast milk within one’s zone was a failure as there was none. She can only find Facebook groups or forums doing it. In Instagram, it’s even more difficult to find for non-tech-savvy moms. That’s why she thought of a possible solution to address this problem, which is to build a marketplace app for this market — an app that can do selling/buying/donating of breast milk in the Philippines, and maybe in the future the whole of Asia.</p><p>One day, I received an email from her, which stated, “I already have a solution! Build me this app, ASAP!” and I was like “Ahhh, okay, so when do you need it?” She replied, “next month!” Luckily, I have some mobile app development background so I scrambled some tech here and there to come out with a prototype and she liked it. Although I initially implemented this, I’m not going to do the entire development as she is going to hire her team of developers if this idea takes off because I also have my full-time job in an uber cool startup here in Singapore.</p><p>We all know that breastfeeding is best for babies as it boosts an infant’s immune system as well as prevents malnutrition. And as it turns out there is a big market in this niche area as we tried to dig more into it. There was a survey conducted by UNICEF in the Philippines, which shows that only 34% of infants below 6 months drink exclusive breast milk. The two most notable reasons for such are 1) mothers don’t have enough milk and 2) mothers are returning back to work. There really is an opportunity here for the mobile app to tap. Also, in terms of customer retention, the survey shows that 34% of those who breastfeed continues until the child is 2 years old. Of course, in terms of supply, a data from WHO will validate that this is a sustainable business model. Based on WHO website, “Of the 135 million babies born every year, only 42% are breastfed within the first hour of life, only 38% of mothers practice exclusive breastfeeding during the first six months and 58% of mothers continue breastfeeding up to the age of two years.”</p><p>We conducted a short poll in Cebu, Philippines to validate this. It’s a revelation that the top two reasons why mothers stop breastfeeding are the same reasons stated above. Furthermore, asking them if they are willing to sell or buy milk through a mobile app shows a huge 80% of the respondents saying yes. Also, 90% of them are in the age range of 25–34, which is mostly the Millennial and Gen Y who are educated and well informed.</p><p>The data above shows that there is a potential for this app to take off and can even scale — imagine deploying it to the whole Philippines or even in South Asia alone: Singapore, Indonesia, Malaysia.</p><p>This niche market is a goldmine ready to be tapped and HandyNanay is going to lead the way for it. The term “Nanay” is a Filipino equivalent for a mother.</p><p>Interested mothers can subscribe through the website <a href="http://www.handynanay.co/">www.handynanay.co</a> and they will receive the first version of the app.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=98b9ffdc3f06" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>