Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zack Mullaly's Submission #166

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
ecc94e4
Set up the frontend using vue-cli
Dec 15, 2017
b1b9ff5
Created a Home component with links to other components that do usefu…
Dec 15, 2017
c3c47e8
Built out a component to display the required information about expenses
Dec 15, 2017
f68e238
Added a ReportUpload component containing a file input that the user …
Dec 15, 2017
18f40f4
Set up Vuex and wrote out the beginning of a store with actions and m…
Dec 15, 2017
89d538e
Documented the data expected to be given to the record-expense-report…
Dec 15, 2017
6899b8b
Wired up Vuex into the components built to display data so that now i…
Dec 15, 2017
eec6e87
Removed a little bit of unnecessary code
Dec 15, 2017
f4c5eab
Starting to write a server to process CSV files
Dec 16, 2017
0f37924
Wrote a couple of models for database entities
Dec 16, 2017
111c9ec
Defined some interfaces (and mocks) to abstract functionality for dea…
Dec 16, 2017
2a36403
Started defining enough of our API surface that we can start testing
Dec 16, 2017
792cdb2
Laid out the foundation of our first set of API tests
Dec 16, 2017
262b082
Established some assertions for our CSV upload tests.
Dec 16, 2017
655d89a
Implemented a function to help us test by taking care of making reque…
Dec 16, 2017
73f6fa2
Started making tests pass; encode a response to CSV uploads that does…
Dec 16, 2017
9642de5
Added a field to the Expense model for tax names
Dec 16, 2017
018ee7f
Wrote tests for a couple of functions that will extract useful inform…
Dec 16, 2017
ca14772
Got tests to run and fail
Dec 16, 2017
f2bdfba
Progress commit; implemented extract functions to get data out of rec…
Dec 16, 2017
574ecae
Progress commit; a first stab at implementing the upload handler
Dec 16, 2017
5b89937
Fixed some bugs caused by commas in numbers, spaces around values etc.
Dec 16, 2017
915d3eb
Moving to a non-concurrent implementation to avoid annoying bugs from…
Dec 16, 2017
f88ef42
Made test code a bit more robust to fix them
Dec 16, 2017
ebbec9e
Write parsed expenses in the response to a CSV upload
Dec 17, 2017
e015554
Removing unused logging
Dec 17, 2017
8cec523
Implemented the two ledger interfaces for both employees and expense
Dec 17, 2017
3ec0402
Change the interfaces to accept pointers
Dec 17, 2017
596a53f
Corrected test code
Dec 17, 2017
84432b6
Created some constructors for ledger implementations
Dec 17, 2017
4c5c354
Set up the server with ledgers that can record employees and expenses…
Dec 17, 2017
8f28661
Create database tables at strartup
Dec 17, 2017
569e1dd
Don't upload the database
Dec 17, 2017
8e46293
add a sqlite driver for the server
Dec 17, 2017
59df642
Call into the server to do file uploads
Dec 17, 2017
fa0a077
Added an errors module to the store; switching from a file upload to …
Dec 18, 2017
90c9221
Have the server accept CSV data in the body of received requests
Dec 18, 2017
1f340fc
Lots of fiddling around with the request library and headers. Had to …
Dec 18, 2017
003a82d
Debugging to get at the data we need. Going to have to adjust to hand…
Dec 18, 2017
3022b2c
Deal with ISO-formatted dates to reduce expenses down to monthly totals
Dec 18, 2017
4cf355a
A little cleanup; order summarized data by most recent date
Dec 18, 2017
98b4481
Completed a README with instructions for setting up and running the c…
Dec 18, 2017
fa6dc2b
Pushing the build/ directory, because things don't work without it.
Jan 10, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
client/build/
client/node_modules/
client/package-lock.json

server/server
server/database.db
97 changes: 58 additions & 39 deletions README.markdown
Original file line number Diff line number Diff line change
@@ -1,59 +1,78 @@
# Wave Software Development Challenge
Applicants for the [Software developer](https://wave.bamboohr.co.uk/jobs/view.php?id=1) role at Wave must complete the following challenge, and submit a solution prior to the onsite interview.

The purpose of this exercise is to create something that we can work on together during the onsite. We do this so that you get a chance to collaborate with Wavers during the interview in a situation where you know something better than us (it's your code, after all!)
This application features a frontend (client) and a backend (server) component.

There isn't a hard deadline for this exercise; take as long as you need to complete it. However, in terms of total time spent actively working on the challenge, we ask that you not spend more than a few hours, as we value your time and are happy to leave things open to discussion in the onsite interview.
## Frontend

Please use whatever programming language and framework you feel the most comfortable with.
**Technologies**: JavaScript, Vue.js.

Feel free to email [[email protected]]([email protected]) if you have any questions.
The frontend component is responsible for:

## Project Description
Imagine that Wave has just acquired a new company. Unfortunately, the company has never stored their data in a database, and instead uses a comma separated text file. We need to create a way for the new subsidiary to import their data into a database. Your task is to create a web interface that accepts file uploads, and then stores them in a relational database.
1. Accepting CSV files containing employee expense reports from the user,
2. Uploading said files to the server for processing and storage, and
3. Displaying a summary of the data parsed by the server.

### What your web-based application must do:
## Backend

1. Your app must accept (via a form) a comma separated file with the following columns: date, category, employee name, employee address, expense description, pre-tax amount, tax name, and tax amount.
1. You can make the following assumptions:
1. Columns will always be in that order.
2. There will always be data in each column.
3. There will always be a header line.
**Technologies**: Go, SQLite

An example input file named `data_example.csv` is included in this repo.
The backend component is responsible for:

1. Your app must parse the given file, and store the information in a relational database.
1. After upload, your application should display a table of the total expenses amount per-month represented by the uploaded file.
1. Handling and parsing the contents of files uploaded containing CSV data,
2. Parsing said CSV data and extracting information about employees and expenses,
3. Storing information about employees and expenses in a relational database, and
4. Writing information about the expenses parsed from the CSV file to the client.

Your application should be easy to set up, and should run on either Linux or Mac OS X. It should not require any non open-source software.
## Setup

There are many ways that this application could be built; we ask that you build it in a way that showcases one of your strengths. If you you enjoy front-end development, do something interesting with the interface. If you like object-oriented design, feel free to dive deeper into the domain model of this problem. We're happy to tweak the requirements slightly if it helps you show off one of your strengths.
### Install the required tools

### Documentation:
1. Install the [Go compiler and developer tools](https://golang.org/dl/).
2. Install the [LTS version of Node.js](https://nodejs.org/en/).
3. Install [SQLite 3](https://www.sqlite.org/)- you may wish to use your OS' package manager here.
4. Clone this repository.

Please modify `README.md` to add:
### Preparing and running the client

1. Instructions on how to build/run your application
1. A paragraph or two about what you are particularly proud of in your implementation, and why.
Assuming you have cloned this project to `se-challenge-expenses`:

## Submission Instructions
```bash
cd se-challenge-expenses/client
npm install
npm run dev
```

1. Fork this project on github. You will need to create an account if you don't already have one.
1. Complete the project as described below within your fork.
1. Push all of your changes to your fork on github and submit a pull request.
1. You should also email [[email protected]]([email protected]) and your recruiter to let them know you have submitted a solution. Make sure to include your github username in your email (so we can match applicants with pull requests.)
If these steps succeeded, you should be able to navigate to `127.0.0.1:8080` in your web browser
and start interacting with the frontend.

## Alternate Submission Instructions (if you don't want to publicize completing the challenge)
1. Clone the repository.
1. Complete your project as described below within your local repository.
1. Email a patch file to [[email protected]]([email protected])
### Preparing and running the server

## Evaluation
Evaluation of your submission will be based on the following criteria.
Assuming you have cloned this project to `se-challenge-expenses`:

1. Did you follow the instructions for submission?
1. Did you document your build/deploy instructions and your explanation of what you did well?
1. Were models/entities and other components easily identifiable to the reviewer?
1. What design decisions did you make when designing your models/entities? Why (i.e. were they explained?)
1. Did you separate any concerns in your application? Why or why not?
1. Does your solution use appropriate datatypes for the problem as described?
```bash
cd se-challenge-expense/server
go get github.com/mattn/go-sqlite3
go build
./server
```

If these stetps succeeded, you should see some output in your terminal indicating that the server
is listening on `127.0.0.1:9001`.

## Highlights

The server component of this project has been designed in such a way that adding new functionality can
be done in a way very similar to how the existing functionality has been positioned. By abstracting
functionality related to database operations into interfaces that are not coupled to any particular
implementation strategy, it is possible to add more functionality by first defining new interfaces
followed by any number of implementations thereof. This approach also lends itself very well to
testing, as mock implementations for each interface can be produced easily, allowing us to simulate
different scenarios within our mocks and not necessarily have to deal with an actual database in our
unit tests.

While not particularly my area of expertise, I am also rather proud of the simple, functional approach
that I was able to take to building components for the frontend. Considering, for example, the
`ReportSummary` component, we can see that the functionality supported by this component is
implemented using simple, pure functions. This approach lends itself very well to code reuse and,
again, testing, as pure functions can be tested without any concern for side-effects such as database
interactions.
18 changes: 18 additions & 0 deletions client/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-vue-jsx", "istanbul"]
}
}
}
9 changes: 9 additions & 0 deletions client/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
5 changes: 5 additions & 0 deletions client/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/build/
/config/
/dist/
/*.js
/test/unit/coverage/
51 changes: 51 additions & 0 deletions client/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// https://eslint.org/docs/user-guide/configuring

module.exports = {
root: true,
parser: 'babel-eslint',
parserOptions: {
sourceType: 'module'
},
env: {
browser: true,
},
extends: 'airbnb-base',
// required to lint *.vue files
plugins: [
'html'
],
// check if imports actually resolve
settings: {
'import/resolver': {
webpack: {
config: 'build/webpack.base.conf.js'
}
}
},
// add your custom rules here
rules: {
// Don't require semi-colons.
'semi': ['error', 'never'],
// don't require .vue extension when importing
'import/extensions': ['error', 'always', {
js: 'never',
vue: 'never'
}],
// disallow reassignment of function parameters
// disallow parameter object manipulation except for specific exclusions
'no-param-reassign': ['error', {
props: true,
ignorePropertyModificationsFor: [
'state', // for vuex state
'acc', // for reduce accumulators
'e' // for e.returnvalue
]
}],
// allow optionalDependencies
'import/no-extraneous-dependencies': ['error', {
optionalDependencies: ['test/unit/index.js']
}],
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
}
}
9 changes: 9 additions & 0 deletions client/.postcssrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// https://github.com/michael-ciniawsky/postcss-load-config

module.exports = {
"plugins": {
// to edit target browsers: use "browserslist" field in package.json
"postcss-import": {},
"autoprefixer": {}
}
}
27 changes: 27 additions & 0 deletions client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# wave-challenge

> The user interface (client) component of the software required by Wave's software engineer challenge

## Build Setup

``` bash
# install dependencies
npm install

# serve with hot reload at localhost:8080
npm run dev

# build for production with minification
npm run build

# build for production and view the bundle analyzer report
npm run build --report

# run unit tests
npm run unit

# run all tests
npm test
```

For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
41 changes: 41 additions & 0 deletions client/build/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use strict'
require('./check-versions')()

process.env.NODE_ENV = 'production'

const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')

const spinner = ora('building for production...')
spinner.start()

rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false,
chunkModules: false
}) + '\n\n')

if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}

console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})
54 changes: 54 additions & 0 deletions client/build/check-versions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')

function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}

const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]

if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}

module.exports = function () {
const warnings = []

for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]

if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}

if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()

for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}

console.log()
process.exit(1)
}
}
Binary file added client/build/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading