Application has been deployed to AWS Amplify and is available here: https://master.d32wii2sjje0ac.amplifyapp.com/
Source code:
Web
:: https://github.com/JohnMcNulty/shell-api-demo/dotnet core API
:: https://github.com/JohnMcNulty/Shell-API-dotnet-core-service
I have made an assumption from page 4 of the slides: "For each file, calculate the minimum, maximum and median values ... Distribute the data via an API based on Date (no time), Meter and Data Type”
I am assuming that this is required for each unique Date/Meter/DataType across all the data. Obviously, in the ‘real world’ I would ask for clarification on this assumption from the business sponsor.
The following npm and nuget packages have been used:
Application has been created using create-react-app as a base.
- react-bootstrap : styling
- plotly : charting
- moment : formatting date labels (
Energy values by date
chart) - prettier: code formatting
- miragejs: mock API - this takes the data generated by the Shell API - dotnet core service and stores locally (see
api/server.js
)
- CsvHelper : converts source flat file data to class objects
- LinqStatistics : used for median calculation across each resultset
* Represent the results in a simple UI (graphs, charts or other ways)
A ReactJS app (created from create-react-app
npx package runner). Application data is self contained and accessed via a mock API.
Contains main 'connected' component (EnergyMain
), calls API passes & props to child components.
Component name | Purpose | Comments |
---|---|---|
EnergyMain |
Main connected parent component | Contains hook to call (mock) API on request type change (i.e. data by Date, by Energy Type, by Meter ) Handles click event from child control panel Passes props to children |
EnergyHeader |
Renders static header | Child component |
EnergyControlPanel |
Renders user inputs (i.e. buttons) | Child component Renders Date , Meter , Type buttonsuseState hooks tracks current selection (and highlights in UI render ) Calls prop onClick func (supplied by parent component) |
EnergyChart |
Renders data in plotly chart | Child component Component contains hook that fires call to buildChart on chartData change Chart data is sorted, sanitized & formatted on changes to request type (see buildChart func) buildChart accepts optional fnFormatLabel func to format x-axis labels |
/src/index.js
/src/App.js
- wraps main 'entry' component
EnergyMain
Created a dotnet core API with standard Service-Orientated-Architecture containing Service and Repository layers.
Postman collection for localhost API testing is here.
* Ingests data files and stores the raw data
-
In a 'real world' application with process would be handeld via an SSIS package (or similar) to load the flat files into staging tables within a database.
-
The repository layer's sole purpose is to read the raw flat files and returns a
List<T>
of eitherLpRow
orTouRow
objects (obviously, in the real world this dataset would come from a DB call via a stored procedure). See FlatFileDataLoaderGeneric.cs.-
I've made this class generic to handle the variances between the 'LP...' and 'TOU...' files. Using the
CsvHelper
nuget package for simple reading of the files. -
Models contain mapping helper classes (part of
CsvHelper -> ClassMap
nuget package) which assist with formatting dates (and generally make field mapping easier). LINK -
FlatFileDataLoaderGeneric.cs implements the
IDataLoaderGeneric
interface. Therefore, another concrete implementation of this interface could be created that points to a datastore but would not affect the calling Service layer.
-
* Transform the data to produce an aggregated result
-
Contains SanitizedEnergyRecords.cs that transforms raw data from
List<LpRow>
orList<TouRow>
to a unified List (seeGetMergedData()
). -
Methods to get data by request type e.g.
GetDataByMeter()
. These are called by the Web API layer and return a list ofIList<ReportingUnit>
containing the min/max/median calculations by Meter/EnergyDataType/Date.
* Distribute the aggregated result as an API
* Distribute the data via an API based on Date (no time), Meter and Data Type
EnergyRecordsController.cs
contains endpoints for each required type, endpoints call service layer and returns results as JSON.Startup.cs
configured to dependency inject repository and service layers. Link- Serialization options configured to return data in camel case and remove null values from
ReportingUnit
objects. Link.
In a real world application I would introduce Unit Testing, Security, Logging and more robust error handling. This solution is very bare bones and follows only a happy path!. If you would like me to introduce these features (or discuss how I would implement them) then please let me know.