-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit is way too large, but there isn't a huge amount of value in breaking it up at this point... I always say that. Anyway, this contains a very basic interface to show off the talking point tracker functionality as it stands. It even includes a VERY IMPRESSIVE logo which is safe to consider 100% final. This is written in react, and leverages graphQL to load in the data itself. Issue #13
- Loading branch information
Showing
18 changed files
with
949 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import ApolloClient from 'apollo-boost' | ||
|
||
const client = new ApolloClient({ | ||
uri: 'http://localhost:3000/graphql', | ||
}) | ||
|
||
export default client |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Imports | ||
import React from 'react' | ||
import { ApolloProvider } from 'react-apollo' | ||
import { Route, BrowserRouter as Router } from 'react-router-dom' | ||
|
||
// App Imports | ||
import Dashboard from './Dashboard' | ||
import EntityDetail from './EntityDetail' | ||
import Header from './Header' | ||
import client from '../ApolloClient' | ||
|
||
// Component | ||
class App extends React.Component { | ||
state = {} | ||
|
||
setStartTime = (startTime) => { | ||
this.setState({ startTime }) | ||
} | ||
|
||
setEndTime = (endTime) => { | ||
this.setState({ endTime }) | ||
} | ||
|
||
render() { | ||
const { startTime, endTime } = this.state | ||
return ( | ||
<> | ||
<div className="container"> | ||
<Router> | ||
<ApolloProvider client={client}> | ||
<Header | ||
startTime={startTime} | ||
setStartTime={this.setStartTime} | ||
endTime={endTime} | ||
setEndTime={this.setEndTime} | ||
/> | ||
<Route | ||
exact | ||
path="/" | ||
render={props => ( | ||
<Dashboard | ||
{...props} | ||
startTime={startTime} | ||
endTime={endTime} | ||
/> | ||
) | ||
} | ||
/> | ||
<Route | ||
path="/detail" | ||
render={props => ( | ||
<EntityDetail | ||
{...props} | ||
startTime={startTime} | ||
endTime={endTime} | ||
/> | ||
) | ||
} | ||
/> | ||
</ApolloProvider> | ||
</Router> | ||
</div> | ||
</> | ||
) | ||
} | ||
} | ||
|
||
export default App |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import React from 'react' | ||
import PropTypes from 'prop-types' | ||
|
||
import { Query } from 'react-apollo' | ||
import gql from 'graphql-tag' | ||
|
||
import FrequencyChart from './FrequencyChart' | ||
import SentenceViewer from './SentenceViewer' | ||
|
||
const ALL_ENTITIES_QUERY = gql` | ||
query ALL_ENTITIES_QUERY( | ||
$after: String, | ||
$before: String) { | ||
namedEntities ( | ||
after: $after, | ||
before: $before | ||
) { | ||
entity | ||
type | ||
createdAt | ||
} | ||
} | ||
` | ||
|
||
const RECENT_SENTENCES_QUERY = gql` | ||
query RECENT_SENTENCES_QUERY($after: String!) { | ||
sentences ( | ||
after: $after | ||
) { | ||
content | ||
createdAt | ||
} | ||
} | ||
` | ||
|
||
class Dashboard extends React.Component { | ||
static propTypes = { | ||
startTime: PropTypes.number, | ||
endTime: PropTypes.number, | ||
} | ||
|
||
static defaultProps = { | ||
startTime: null, | ||
endTime: null, | ||
} | ||
|
||
render() { | ||
const { startTime, endTime } = this.props | ||
const currentDate = new Date() | ||
return ( | ||
<> | ||
<div className="row no-gutters"> | ||
<div className="col-sm-7 col-12"> | ||
<div id="dashboardEntityChart" className="elementContainer"> | ||
<h2>Keyword Counts</h2> | ||
<Query | ||
query={ALL_ENTITIES_QUERY} | ||
variables={{ | ||
after: startTime, | ||
before: endTime, | ||
}} | ||
> | ||
{({ data, error, loading }) => { | ||
if (loading) { | ||
return <p>Loading...</p> | ||
} | ||
if (error) { | ||
return ( | ||
<p> | ||
Error: | ||
{error.message} | ||
</p> | ||
) | ||
} | ||
|
||
const aggregatedData = data.namedEntities.reduce((accumulator, currentValue) => { | ||
if (!(currentValue.entity in accumulator)) { | ||
accumulator[currentValue.entity] = 0 | ||
} | ||
accumulator[currentValue.entity] += 1 | ||
return accumulator | ||
}, {}) | ||
|
||
const masterList = Object.keys(aggregatedData).map(key => ({ | ||
key, | ||
val: aggregatedData[key], | ||
})) | ||
|
||
const sortedList = masterList.sort((a, b) => a.val < b.val) | ||
|
||
const labels = sortedList.map(x => x.key) | ||
const values = sortedList.map(x => x.val) | ||
|
||
return ( | ||
<FrequencyChart | ||
data={values} | ||
labels={labels} | ||
/> | ||
) | ||
}} | ||
</Query> | ||
</div> | ||
</div> | ||
<div className="col-sm-5 col-12"> | ||
<div id="dashboardSentences" className="elementContainer"> | ||
<h2>Recent Transcript</h2> | ||
<Query | ||
query={RECENT_SENTENCES_QUERY} | ||
variables={{ after: currentDate.toISOString() }} | ||
> | ||
{({ data, error, loading }) => { | ||
if (loading) { | ||
return <p>Loading...</p> | ||
} | ||
if (error) { | ||
return ( | ||
<p> | ||
Error: | ||
{error.message} | ||
</p> | ||
) | ||
} | ||
return ( | ||
<SentenceViewer | ||
sentences={data.sentences} | ||
/> | ||
) | ||
}} | ||
</Query> | ||
</div> | ||
<div id="dashboardRecentEntities" className="elementContainer"> | ||
<h2>Recent Entities</h2> | ||
</div> | ||
</div> | ||
</div> | ||
</> | ||
) | ||
} | ||
} | ||
|
||
export default Dashboard |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
import React from 'react' | ||
import PropTypes from 'prop-types' | ||
import qs from 'query-string' | ||
|
||
import { Query } from 'react-apollo' | ||
import gql from 'graphql-tag' | ||
|
||
import FrequencyChart from './FrequencyChart' | ||
import SentenceViewer from './SentenceViewer' | ||
|
||
const RELATED_ENTITIES_QUERY = gql` | ||
query RELATED_ENTITIES_QUERY( | ||
$relatedTo: String!, | ||
$after: String, | ||
$before: String) { | ||
namedEntities ( | ||
relatedTo: $relatedTo, | ||
after: $after, | ||
before: $before | ||
) { | ||
entity | ||
type | ||
createdAt | ||
} | ||
} | ||
` | ||
|
||
const RELATED_SENTENCES_QUERY = gql` | ||
query RELATED_SENTENCES_QUERY( | ||
$relatedTo: String!, | ||
$after: String, | ||
$before: String) { | ||
sentences ( | ||
relatedTo: $relatedTo, | ||
after: $after, | ||
before: $before | ||
) { | ||
id | ||
content | ||
createdAt | ||
} | ||
} | ||
` | ||
|
||
class EntityDetail extends React.Component { | ||
static propTypes = { | ||
location: PropTypes.shape({ | ||
search: PropTypes.string, | ||
}).isRequired, | ||
startTime: PropTypes.number, | ||
endTime: PropTypes.number, | ||
} | ||
|
||
static defaultProps = { | ||
startTime: null, | ||
endTime: null, | ||
} | ||
|
||
render() { | ||
const { location, startTime, endTime } = this.props | ||
const relatedTo = qs.parse(location.search, { ignoreQueryPrefix: true }).entity | ||
|
||
return ( | ||
<> | ||
<div className="row no-gutters"> | ||
<div className="col-sm-7"> | ||
<div className="elementContainer"> | ||
<h2> | ||
Content referencing | ||
" | ||
{relatedTo} | ||
" | ||
</h2> | ||
<Query | ||
query={RELATED_SENTENCES_QUERY} | ||
variables={{ | ||
relatedTo, | ||
after: startTime, | ||
before: endTime, | ||
}} | ||
> | ||
{({ data, error, loading }) => { | ||
if (loading) { | ||
return <p>Loading...</p> | ||
} | ||
if (error) { | ||
return ( | ||
<p> | ||
Error: | ||
{error.message} | ||
</p> | ||
) | ||
} | ||
return ( | ||
<SentenceViewer | ||
sentences={data.sentences} | ||
/> | ||
) | ||
}} | ||
</Query> | ||
</div> | ||
</div> | ||
<div className="col-sm-5"> | ||
<div className="elementContainer"> | ||
<h2>Count over time</h2> | ||
</div> | ||
<div className="elementContainer"> | ||
<h2>Related Keywords</h2> | ||
<Query | ||
query={RELATED_ENTITIES_QUERY} | ||
variables={{ | ||
relatedTo, | ||
after: startTime, | ||
before: endTime, | ||
}} | ||
> | ||
{({ data, error, loading }) => { | ||
if (loading) { | ||
return <p>Loading...</p> | ||
} | ||
if (error) { | ||
return ( | ||
<p> | ||
Error: | ||
{error.message} | ||
</p> | ||
) | ||
} | ||
|
||
const aggregatedData = data.namedEntities.reduce((accumulator, currentValue) => { | ||
if (!(currentValue.entity in accumulator)) { | ||
accumulator[currentValue.entity] = 0 | ||
} | ||
accumulator[currentValue.entity] += 1 | ||
return accumulator | ||
}, {}) | ||
|
||
const masterList = Object.keys(aggregatedData).map(key => ({ | ||
key, | ||
val: aggregatedData[key], | ||
})) | ||
|
||
const sortedList = masterList.sort((a, b) => a.val < b.val) | ||
|
||
const labels = sortedList.map(x => x.key) | ||
const values = sortedList.map(x => x.val) | ||
|
||
return ( | ||
<FrequencyChart | ||
data={values} | ||
labels={labels} | ||
size={[500, 500]} | ||
/> | ||
) | ||
}} | ||
</Query> | ||
</div> | ||
</div> | ||
</div> | ||
</> | ||
) | ||
} | ||
} | ||
|
||
export default EntityDetail |
Oops, something went wrong.