Skip to content

Commit 6101f4a

Browse files
committed
Add REAME.md files
1 parent 239483f commit 6101f4a

File tree

2 files changed

+308
-0
lines changed

2 files changed

+308
-0
lines changed

README.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Local Weather Service
2+
3+
## The Value of Weather Information
4+
Weather information is powerful knowledge used in forcasting the production of certain operation to help determining the economic bottom line. There are countless human endearvours affecting by the weather factors. Rain is good for growers, fore warning is valuable in extreme weather, while sunny sky, windy days could be good for energy production. For examples, solar power production needs to know the number of hours of sunlight specific to geographical location. The travel and hospitality industry is most affected by dynamic weather patterns. From Agriculture to space faring, weather plays the key role in the decision making process.
5+
6+
This project demnonstrates how you can use the geolocation function to get current position in longitude and latitude. Then, to get pin point weather data, you will construct an API query for weather statistics using the lng-lat value.
7+
8+
<br />
9+
10+
> Below is the template for rudimentary completion of an example on interfacing with a weather data provider. For indepth logics and real life data implementation, continue to [here](./public/README.md).
11+
12+
<br />
13+
14+
## NodeJS and Express
15+
16+
I recently wrote this Local Weather app using the cloud editor `replit` where the bootstraping of node JS is hidden. All I had to do was to focus on the core logic in app.js, style.css, and index.html. To create a fully functional development environment locally, I needed to bootstrap NodeJS, Express to my replit code.
17+
18+
Here is how I add scalffolding my Nodejs project.
19+
20+
**Configure Project Development Environment for ES6**
21+
22+
>Let's assume you are using NodeJS with a version later than 13. Then, it is required to configure the development environment to work for ExpressJS with dotenv and ES6 modules.
23+
24+
First, you will need to change your `package.json` to include:
25+
26+
```c
27+
...
28+
"type": "module",
29+
...
30+
```
31+
32+
Then, use imports in your `app.js`. Here is an express start with dotenv:
33+
34+
```c
35+
import dotenv from "dotenv"
36+
import express from "express"
37+
38+
dotenv.config()
39+
40+
const app = express()
41+
const port = process.env.PORT || 5000
42+
43+
app.get('/', (req, res) => {
44+
res.send("Local Weather. Don't leave home without reading it")
45+
})
46+
47+
app.listen(port, () => {
48+
console.log(`Local weather server is serving you from http://0.0.0.0:${port}`)
49+
})
50+
```
51+
52+
<br />
53+
54+
Step 1: Migrate replit code to local VSCode.
55+
Create a folder for your project. Create sub folders `views` and `public` to place client-side programming.
56+
57+
`mkdir views`
58+
59+
`mkdir public`
60+
61+
Step 2: Initialize the folder as a node project
62+
63+
` npm init -y`
64+
65+
Step 3: Install *express* to confiure a lightweight Node server
66+
67+
`npm install express`
68+
69+
Step 4: Install *helmet* to configure runtime security
70+
71+
`nmp install helmet`
72+
73+
Now, you are good to start developing and runing from VSCode.
74+
75+
### Project Layout
76+
77+
It is a typical NodeJS project layout.
78+
79+
'app.js' - server file
80+
81+
'./public/script.js' - interface to https://weather-proxy.freecodecamp.rocks/api/
82+
83+
'./views/index.html' - client-side presentation.
84+
85+
## Runtime
86+
87+
`npm start`
88+
89+
<strong>Output</strong>
90+
91+
![codepen.io output](./public/Runtime.PNG)
92+
93+
## Build and Run Docker Image
94+
95+
Build a docker image base on the given Dockerfile and .dockerignore is this folder. After successful docker build, run the image to verify correctness. Then it can be pushed to dockerhub or your favorite cloud provider.
96+
97+
### Build
98+
Docker container is built and saved to current working directory. Replace tag name 'hurricanemark' with your own username.
99+
100+
`docker build -t hurricanemark/localweather:1.0 .`
101+
102+
```c
103+
[+] Building 139.3s (11/11) FINISHED
104+
=> [internal] load build definition from Dockerfile 0.1s
105+
=> => transferring dockerfile: 210B 0.1s
106+
=> [internal] load .dockerignore
107+
...
108+
=> [internal] load build context 0.8s
109+
=> => transferring context: 143.24kB 0.6s
110+
=> [2/5] WORKDIR /app 1.2s
111+
=> [3/5] COPY package.json ./ 0.1s
112+
=> [4/5] RUN npm install 6.6s
113+
=> [5/5] COPY . . 0.2s
114+
=> exporting to image 0.4s
115+
=> => exporting layers 0.3s
116+
=> => writing image sha256:bb89b0646be41055287fdac18ea1e405a2a19ef4b2919a0a02c213b9dc947b34 0.0s
117+
=> => naming to docker.io/hurricanemark/localweather:1.0
118+
```
119+
120+
** List the image **
121+
122+
```c
123+
PS D:\DEVEL\NODEJS\BrainUnscramblers\LocalWeather> docker image ls
124+
REPOSITORY TAG IMAGE ID CREATED SIZE
125+
hurricanemark/localweather 1.0 bb89b0646be4 6 minutes ago 949MB
126+
```
127+
128+
### Run docker
129+
130+
Note that Dockerfile exposes port 8080. This needs to be forwarded to a port on your local machine. i.e. `LOCAL_PORT:CONTAINER_PORT` for example *4321:8080*
131+
132+
`docker run -p 4321:8080 bb89b0646be4`
133+
134+
```c
135+
PS D:\DEVEL\NODEJS\BrainUnscramblers\LocalWeather> docker run -p 4321:8080 bb89b0646be4
136+
137+
> LocalWeather@1.0.0 start /app
138+
> node app.js
139+
140+
Local Weather is listening on port 8080
141+
```
142+
143+
<br />
144+
145+
To access the Local Weather app running in docker container, point your browser to the forwarding port 4321.
146+
147+
`http:/localhost:4321`

public/REAME.md

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# Core Logic
2+
3+
In order to obtain the pin point weather data, earth location is a required parameter. For this, the longitude and latitude would surfice programmatically. For the interactive option, a physical postal address (partial address of City, State, Country) is needed. Optionally, you could also register with google map service (*additional charge will incur*). Then, integrate google-map API where the user can point to location on the map to trigger locolized weather report.
4+
5+
* The geolocation of a known address can be obtained by using the client-side javascript [Windows Navigator](https://www.w3schools.com/jsref/obj_navigator.asp). Your browser uses different types and sources of information to identify your location. These include your IP address, geolocation via HTML5 in your browser, and your PC's language and time settings. For more detail how Navigator.geolocation is determined, [read more here](https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition).
6+
7+
Sample code for client-side geolocation:
8+
9+
```c
10+
<script type=javascript>
11+
if (navigator.geolocation) {
12+
navigator.geolocation.getCurrentPosition(showPosition);
13+
} else {
14+
document.getElementById("geo-coord").innerHTML =
15+
"Geolocation is not supported by this browser.";
16+
}
17+
18+
function showPosition(position) {
19+
document.getElementById("geo-coord").innerHTML =
20+
"Latitude: " + position.coords.latitude +
21+
"Longitude: " + position.coords.longitude;
22+
}
23+
</script>
24+
```
25+
26+
* The API query for weather data can be describe as follow.
27+
28+
> <https://weather.visualcrossing.com/VisualCrossingWebServices/rest/services/timeline/LOCATION?unitGroup=metric&key=API_ACCESS_KEY&contentType=json>
29+
30+
Where, **LOCATION** can be City, State
31+
32+
and **API_ACCESS_KEY** can be obtained from registering with an API provider.
33+
34+
* With the return JSON object representing the weather data, you wrap the individual data objects in grid to make it user friendly.
35+
36+
<br />
37+
38+
# Getting Real Data from A Paid API Provider
39+
40+
Replace freecodecamp proxy URI (file: public/scripts.js) for weather data provider via a paid subscription. In this project, you can use the API service from the [VisualCrossing.com](https://www.visualcrossing.com). The good news is if you registered to use the free tier, a good fit for demonstration purposes, there is no charge as long as you stay within the allowed call limit. Read [here](https://www.visualcrossing.com/resources/blog/five-easy-weather-api-calls-to-get-the-weather-data-you-need/) to learn ways to make weather API calls.
41+
42+
<br />
43+
44+
# Steps to Integrate A New API Provider
45+
46+
1. Register with a weather reporting service. Consult with their API documentation on how to call their APIs.
47+
2. Obtain the API access key from the same provider above.
48+
3. Build an API https query and manually test it out.
49+
4. Write an async function to incorporate the API call and retrieve the weather data.
50+
51+
52+
> On the server, the `API_KEY` is hidden in the `.env` file. By design, environment variables are not accessible to the client. For the client to directly query weather data, the API_KEY has to be available on the browser. This will break the security of our application!
53+
54+
**Options**:
55+
56+
* Make the API calls on the server-side and then send response containing the weather data to the client;
57+
* or, require the client to use their own API keys;
58+
* or, using the serverless option with locked down to a service. *This bad option multiplies your cost as your user base grows*.
59+
60+
**Decision:**
61+
62+
You would want to live free and die harder, let's make the client calls the server whose in turn requests weather data and sends responses back. The client then can parse the response into a grid for display. This means, the server will need to provide GET requests!
63+
64+
---
65+
66+
**Options**:
67+
68+
You need to implement a middleware to retrieve weather data and serve it to the client using either
69+
- `EJS` embedded templates
70+
- or `ReactJS`.
71+
72+
**Decision:**
73+
Let's introduce the EJS middleware as embedded template rendering weather data.
74+
75+
e.g.
76+
```c
77+
app.post("/geoLngLatWeather", (req, res) => {...})
78+
79+
app.get("/geoLngLatWeather", (req, res) => {...})
80+
81+
app.get("/physicalAddrWeather", (req, res, next) => {...})
82+
```
83+
<br />
84+
85+
<strong><span style="color:gray; font-size:18px"> Staying within the Free Tier Limit</span> </strong>
86+
87+
A typical free daily allowance cycle consists of up to 1000 free API queries. In which case, the user has to wait until the begining of the next free tier cycle. To avoid being charged, this source code has a *a server-side accounting logic* to pause querying the weathercrossing API once its periodic allowance has been reached. Unpause is automatic once the clock rolls to the begin of a new daily free tier cycle.
88+
89+
>*Potential development needed to notify the server admin if demands grew and if there are ways to monetize it.*
90+
91+
<strong><span style="color:gray; font-size:18px">Caching the Weather Data</span></strong>
92+
93+
Weather forecast data from `visualcrossing.com` typically contains infomation spanning 15 days, each day contains 24 hourly objects. Additional queries within the same period would usually resulted in the same data. We need to cache this information so not to incur needless and sometimes costly API calls.
94+
95+
We could elect to use the DOM Storage API, also called the [Web Storage API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API) methods: `Window.sessionStorage` where data persists as long as the session is running, or `Window.localStorage` which has no expiration. Note: when the last private tab is closed, data stored in the localStorage object of a site opened in a private tab or incognito mode is cleared.
96+
97+
<br />
98+
99+
<span style="color:green">**IMPORTANT:**</span> Web Storage API needs to be implemented on the the browser side (client with the DOM). In this case, we make sure to place the javascript script at the bottom of HTML body section.
100+
101+
>Another option is to use no-sql storage such as `redis`. A topic not in the scope of this exercise.
102+
103+
A sample of Web Storage API test:
104+
105+
```c
106+
/* client-side script */
107+
<script type=javascript>
108+
function storageAvailable(type) {
109+
let storage;
110+
try {
111+
storage = window[type];
112+
const x = '__storage_test__';
113+
storage.setItem(x, x);
114+
storage.removeItem(x);
115+
return true;
116+
}
117+
catch (e) {
118+
return e instanceof DOMException && (
119+
// everything except Firefox
120+
e.code === 22 ||
121+
// Firefox
122+
e.code === 1014 ||
123+
// test name field too, because code might not be present
124+
// everything except Firefox
125+
e.name === 'QuotaExceededError' ||
126+
// Firefox
127+
e.name === 'NS_ERROR_DOM_QUOTA_REACHED') &&
128+
// acknowledge QuotaExceededError only if there's something already stored
129+
(storage && storage.length !== 0);
130+
}
131+
}
132+
133+
if (storageAvailable(methodString)) {
134+
// Yippee! We can use localStorage awesomeness
135+
// Save data to sessionStorage
136+
sessionStorage.setItem("lw_name", "marcus");
137+
138+
// Get saved data from sessionStorage
139+
let data = sessionStorage.getItem("lw_name");
140+
141+
alert(methodString +' says my name is ' + data);
142+
// Remove saved data from sessionStorage
143+
sessionStorage.removeItem("lw_name");
144+
}
145+
else {
146+
// Too bad, no localStorage for us
147+
alert(methodString + ' says Web Storage API is disabled.')
148+
}
149+
</script>
150+
```
151+
152+
153+
## Next Phases
154+
155+
The followings are potential developments in subsequent iterations to this weather report project:
156+
157+
* Integrate the server with auth0 and/or passport authentication
158+
159+
* Registered members have access to full blown dashboard with maritime alerts, historical weather data, extended forecast, etc.
160+
161+
* Build and make mobile apps available to subscribed members.

0 commit comments

Comments
 (0)