Skip to content

Commit 4d182fb

Browse files
committed
add readme
1 parent 690e3d9 commit 4d182fb

File tree

3 files changed

+306
-5
lines changed

3 files changed

+306
-5
lines changed

README.md

Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
# Welcome to PowerDale
2+
3+
PowerDale is a small town with around 100 residents. Most houses have a smart meter installed that can save and send
4+
information about how much power a house is drawing/using.
5+
6+
There are three major providers of energy in town that charge different amounts for the power they supply.
7+
8+
- _Dr Evil's Dark Energy_
9+
- _The Green Eco_
10+
- _Power for Everyone_
11+
12+
# Introducing JOI Energy
13+
14+
JOI Energy is a new start-up in the energy industry. Rather than selling energy they want to differentiate themselves
15+
from the market by recording their customers' energy usage from their smart meters and recommending the best supplier to
16+
meet their needs.
17+
18+
You have been placed into their development team, whose current goal is to produce an API which their customers and
19+
smart meters will interact with.
20+
21+
Unfortunately, two members of the team are on annual leave, and another one has called in sick! You are left with
22+
another ThoughtWorker to progress with the current user stories on the story wall. This is your chance to make an impact
23+
on the business, improve the code base and deliver value.
24+
25+
## Story Wall
26+
27+
At JOI energy the development team use a story wall or Kanban board to keep track of features or "stories" as they are
28+
worked on.
29+
30+
The wall you will be working from today has 7 columns:
31+
32+
- Backlog
33+
- Ready for Dev
34+
- In Dev
35+
- Ready for Testing
36+
- In Testing
37+
- Ready for sign off
38+
- Done
39+
40+
Examples can be found
41+
here [https://leankit.com/learn/kanban/kanban-board/](https://leankit.com/learn/kanban/kanban-board/)
42+
43+
## Users
44+
45+
To trial the new JOI software 5 people from the JOI accounts team have agreed to test the service and share their energy
46+
data.
47+
48+
| User | Smart Meter ID | Power Supplier |
49+
| ------- | --------------- | --------------------- |
50+
| Sarah | `smart-meter-0` | Dr Evil's Dark Energy |
51+
| Peter | `smart-meter-1` | The Green Eco |
52+
| Charlie | `smart-meter-2` | Dr Evil's Dark Energy |
53+
| Andrea | `smart-meter-3` | Power for Everyone |
54+
| Alex | `smart-meter-4` | The Green Eco |
55+
56+
These values are used in the code and in the following examples too.
57+
58+
## Requirements
59+
60+
The project requires [Java 1.8](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) or
61+
higher.
62+
63+
The project makes use of Gradle and uses
64+
the [Gradle wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html), which means you don't need Gradle
65+
installed.
66+
67+
## Useful Gradle commands
68+
69+
The project makes use of Gradle and uses the Gradle wrapper to help you out carrying some common tasks such as building
70+
the project or running it.
71+
72+
### Build the project
73+
74+
Compiles the project, runs the test and then creates an executable JAR file
75+
76+
```console
77+
$ ./conan install
78+
```
79+
80+
Run the application using Java and the executable JAR file produced by the Gradle `build` task. The application will be
81+
listening to port `8080`.
82+
83+
```console
84+
$ java -jar build/libs/joi-energy.jar
85+
```
86+
87+
### Run the tests
88+
89+
There are two types of tests, the unit tests and the functional tests. These can be executed as follows.
90+
91+
- Run unit tests only
92+
93+
```console
94+
$ ./gradlew test
95+
```
96+
97+
- Run functional tests only
98+
99+
```console
100+
$ ./gradlew functionalTest
101+
```
102+
103+
- Run both unit and functional tests
104+
105+
```console
106+
$ ./gradlew check
107+
```
108+
109+
### Run the application
110+
```console
111+
$ ....
112+
```
113+
114+
Run the application which will be listening on port `8080`.
115+
116+
```console
117+
$ ./gradlew bootRun
118+
```
119+
120+
## API
121+
122+
Below is a list of API endpoints with their respective input and output. Please note that the application needs to be
123+
running for the following endpoints to work. For more information about how to run the application, please refer
124+
to [run the application](#run-the-application) section above.
125+
126+
### Store Readings
127+
128+
Endpoint
129+
130+
```text
131+
POST /readings/store
132+
```
133+
134+
Example of body
135+
136+
```json
137+
{
138+
"smartMeterId": <smartMeterId>,
139+
"electricityReadings": [
140+
{
141+
"time": <time>,
142+
"reading": <reading>
143+
}
144+
]
145+
}
146+
```
147+
148+
Parameters
149+
150+
| Parameter | Description |
151+
| -------------- | ----------------------------------------------------- |
152+
| `smartMeterId` | One of the smart meters' id listed above |
153+
| `time` | The date/time (as epoch) when the _reading_ was taken |
154+
| `reading` | The consumption in `kW` at the _time_ of the reading |
155+
156+
Example readings
157+
158+
| Date (`GMT`) | Epoch timestamp | Reading (`kW`) |
159+
| ----------------- | --------------: | -------------: |
160+
| `2020-11-29 8:00` | 1606636800 | 0.0503 |
161+
| `2020-11-29 8:01` | 1606636860 | 0.0621 |
162+
| `2020-11-29 8:02` | 1606636920 | 0.0222 |
163+
| `2020-11-29 8:03` | 1606636980 | 0.0423 |
164+
| `2020-11-29 8:04` | 1606637040 | 0.0191 |
165+
166+
In the above example, the smart meter sampled readings, in `kW`, every minute. Note that the reading is in `kW` and
167+
not `kWH`, which means that each reading represents the consumption at the reading time. If no power is being consumed
168+
at the time of reading, then the reading value will be `0`. Given that `0` may introduce new challenges, we can assume
169+
that there is always some consumption, and we will never have a `0` reading value. These readings are then sent by the
170+
smart meter to the application using REST. There is a service in the application that calculates the `kWH` from these
171+
readings.
172+
173+
The following POST request, is an example request using CURL, sends the readings shown in the table above.
174+
175+
```console
176+
$ curl \
177+
-X POST \
178+
-H "Content-Type: application/json" \
179+
"http://localhost:8080/readings/store" \
180+
-d '{"smartMeterId":"smart-meter-0","electricityReadings":[{"time":1606636800,"reading":0.0503},{"time":1606636860,"reading":0.0621},{"time":1606636920,"reading":0.0222},{"time":1606636980,"reading":0.0423},{"time":1606637040,"reading":0.0191}]}'
181+
```
182+
183+
The above command does not return anything.
184+
185+
### Get Stored Readings
186+
187+
Endpoint
188+
189+
```text
190+
GET /readings/read/<smartMeterId>
191+
```
192+
193+
Parameters
194+
195+
| Parameter | Description |
196+
| -------------- | ---------------------------------------- |
197+
| `smartMeterId` | One of the smart meters' id listed above |
198+
199+
Retrieving readings using CURL
200+
201+
```console
202+
$ curl "http://localhost:8080/readings/read/smart-meter-0"
203+
```
204+
205+
Example output
206+
207+
```json
208+
{
209+
"readings": [
210+
{
211+
"reading": 0.4,
212+
"time": "2021-08-20T07:04:49"
213+
},
214+
{
215+
"reading": 0.123,
216+
"time": "2021-08-20T07:07:49"
217+
},
218+
{
219+
"reading": 0.23,
220+
"time": "2021-08-20T07:10:49"
221+
},
222+
{
223+
"reading": 0.432,
224+
"time": "2021-08-20T07:13:49"
225+
},
226+
{
227+
"reading": 0.44,
228+
"time": "2021-08-20T07:16:49"
229+
}
230+
]
231+
}
232+
```
233+
234+
### View Current Price Plan and Compare Usage Cost Against all Price Plans
235+
236+
Endpoint
237+
238+
```text
239+
GET /price-plans/compare-all/<smartMeterId>
240+
```
241+
242+
Parameters
243+
244+
| Parameter | Description |
245+
| -------------- | ---------------------------------------- |
246+
| `smartMeterId` | One of the smart meters' id listed above |
247+
248+
Retrieving readings using CURL
249+
250+
```console
251+
$ curl "http://localhost:8080/price-plans/compare-all/smart-meter-0"
252+
```
253+
254+
Example output
255+
256+
```json
257+
{
258+
"pricePlanComparisons": {
259+
"price-plan-0": 4.0,
260+
"price-plan-1": 0.8,
261+
"price-plan-2": 0.4
262+
},
263+
"pricePlanId": "price-plan-1"
264+
}
265+
```
266+
267+
### View Recommended Price Plans for Usage
268+
269+
Endpoint
270+
271+
```text
272+
GET /price-plans/recommend/<smartMeterId>[?limit=<limit>]
273+
```
274+
275+
Parameters
276+
277+
| Parameter | Description |
278+
| -------------- | ---------------------------------------------------- |
279+
| `smartMeterId` | One of the smart meters' id listed above |
280+
| `limit` | (Optional) limit the number of plans to be displayed |
281+
282+
Retrieving readings using CURL
283+
284+
```console
285+
$ curl "http://localhost:8080/price-plans/recommend/smart-meter-0?limit=2"
286+
```
287+
288+
Example output
289+
290+
```json
291+
{
292+
"recommend": [
293+
{
294+
"price-plan-2": 0.4
295+
},
296+
{
297+
"price-plan-1": 0.8
298+
}
299+
]
300+
}
301+
```

rest/controller/MeterReadingController.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ namespace detail {
3333

3434
auto renderReadingAsJson(const ElectricityReading &r) {
3535
return nlohmann::json{{"time", toRfc3339(r.getTime())},
36-
{"reading", r.getReading()}};
36+
{"reading", double(r.getReading()) / 10000}};
3737
}
3838
} // namespace detail
3939

rest/controller/PricePlanComparatorController.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ class PricePlanComparatorController {
3333
res.set(http::field::content_type, "application/json");
3434
res.keep_alive(req.keep_alive());
3535
nlohmann::json j;
36-
j["pricePlanComparisons"] = {{"price-plan-0", double(costs.value()["price-plan-0"])/10000},
37-
{"price-plan-1", double(costs.value()["price-plan-1"])/10000},
38-
{"price-plan-2", double(costs.value()["price-plan-2"])/10000}};
36+
j["pricePlanComparisons"] = {{"price-plan-0", double(costs.value()["price-plan-0"]) / 10000},
37+
{"price-plan-1", double(costs.value()["price-plan-1"]) / 10000},
38+
{"price-plan-2", double(costs.value()["price-plan-2"]) / 10000}};
3939
j["pricePlanId"] = current_price_plans[meterId];
4040
res.body() = j.dump();
4141
res.prepare_payload();
@@ -63,7 +63,7 @@ class PricePlanComparatorController {
6363
res.keep_alive(req.keep_alive());
6464
auto results = nlohmann::json::array();
6565
std::transform(ordered_costs.begin(), ordered_costs.end(), std::back_inserter(results),
66-
[](auto &cost) { return nlohmann::json{{cost.first, cost.second}}; });
66+
[](auto &cost) { return nlohmann::json{{cost.first, double(cost.second) / 10000}}; });
6767
nlohmann::json j;
6868
j["recommend"] = results;
6969
res.body() = j.dump();

0 commit comments

Comments
 (0)