Commit 1ddbf2fd authored by gijs's avatar gijs

Files for the annotator

parent 59b88bbe
from flask import Flask, jsonify, request
import random
baseurl = '/the-annotator/api'
app = Flask(__name__)
# def pick_sentence ():
# return [1, '''Logée modestement dans une aile du Capitole, où il fut possible
# d'introduire péniblement 350,000 volumes environ, elle envahit les
# cryptes du vaste édifice législatif, mais pour y enterrer, sans pouvoir
# les en extraire utilement, plusieurs centaines de mille documents de
# toute nature.''']
CLASSES = [
"0 - Science and Knowledge. Organization. Computer Science. Information Science. Documentation. Librarianship. Institutions. Publications",
"1 - Philosophy. Psychology",
"2 - Religion. Theology",
"3 - Social Sciences",
"4 - vacant",
"5 - Mathematics. Natural Sciences",
"6 - Applied Sciences. Medicine, Technology",
"7 - The Arts. Entertainment. Sport",
"8 - Linguistics. Literature",
"9 - Geography. History"
]
DATA = []
def load_data():
pass
def store_data():
pass
def classify (sentence, sentence_class):
DATA[sentence][2] = sentence_class
def pick_sentence ():
return DATA[random.randint(0,len(DATA))][0:2]
@app.route("{}/sentence".format(baseurl), methods=['GET'])
def get_sentence ():
id, sentence = pick_sentence()
return jsonify({
'id': id,
'sentence': sentence
})
@app.route("{}/classify".format(baseurl), methods=['POST'])
def post_sentence ():
sentence_id = int(request.form.id)
classify(sentence_id, min(len(CLASSES), max(0, request.form.sentenceClass)))
store_data()
return jsonify("DONE")
@app.route("{}/classes".format(baseurl), methods=['GET'])
def get_classes ():
return jsonify(CLASSES)
@app.route("{}/mark_dirty".format(baseurl), methods=['POST'])
def mark_dirty ():
return jsonify()
@app.after_request
def add_headers(response):
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add('Access-Control-Allow-Headers',
'Content-Type,Authorization')
return response
\ No newline at end of file
import glob
import os.path
import settings
import random
import re
import csv
documents = glob.glob(os.path.join(settings.datadir, '*.txt'))
def pick_sentence_from_tree (tree):
sentence = ''
while not sentence:
sentence = random.choice(tree).string
return sentence
def pick_sentence():
path = random.choice(documents)
with open(path, 'r') as h:
text = re.sub(r'\n(\S)', ' \\1', re.sub(r'-\n(\S)', '\\1', h.read(), flags=re.M), flags=re.M)
# print(text)
tree = parsetree(text, tokenize = False, tags = False, chunks = False)
return (0, pick_sentence_from_tree(tree))
def transform ():
\ No newline at end of file
datadir = "/home/gijs/Documents/Bedrijf/Projecten/Algolit/mons/git/data/re-ocred"
\ No newline at end of file
{
"name": "cleaning-interface",
"version": "0.1.0",
"private": true,
"dependencies": {
"connected-react-router": "^6.2.1",
"history": "^4.7.2",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-redux": "^6.0.0",
"react-router-dom": "^4.3.1",
"react-scripts": "2.1.3",
"redux": "^4.0.1",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 40vmin;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.sentenceClassList {
display: grid;
grid-template-columns: 25% 25% 25% 25%;
grid-auto-rows: 150px;
}
.sentenceClassList--class {
padding: 1em;
margin: .5em;
background: black;
color: white;
display: flex;
text-align: center;
align-items: center;
justify-content: space-around;
border: none;
}
.sentenceClassList--class:hover {
background: red;
}
.sentenceClassList--class:active {
background: darkorange;
box-shadow: inset 0px 0px 5px rgba(0,0,0,.5);
}
.sentenceClassList--class[data-selected="true"] {
background: orange;
}
.sentence {
font-size: 200%;
line-height: 1.75;
font-family: serif;
margin: 1em 3em;
}
\ No newline at end of file
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import Annotator from './containers/Annotator';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<Router>
<Route exact path="/" component={ Annotator } />
</Router>
</div>
);
}
}
export default App;
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App />, div);
ReactDOM.unmountComponentAtNode(div);
});
export const MESSAGE_CLOSE = 'MESSAGE_CLOSE';
export const messageClose = () => ({ type: MESSAGE_CLOSE });
\ No newline at end of file
import { classesGet } from '../services/api';
export const CLASSES_LOAD = 'CLASSES_LOAD';
export const CLASSES_LOAD_FAIL = "CLASSES_LOAD_FAIL";
export const CLASSES_LOAD_SUCCESS = "CLASSES_LOAD_SUCCESS";
export const classesLoad = () => (dispatch, getState) => {
dispatch({ type: CLASSES_LOAD })
classesGet()
.then(classes => dispatch(classesLoadSucces(classes)))
.catch(() => dispatch(classesLoadFail()))
}
export const classesLoadFail = () => ({
type: CLASSES_LOAD_FAIL,
});
export const classesLoadSucces = (classes) => ({
type: CLASSES_LOAD_SUCCESS,
classes: classes
})
\ No newline at end of file
export const ACTION_NAME = 'ACTION_NAME';
export const GREETING_CHANGE = 'GREETING_CHANGE';
export const actionName = () => ({
type: ACTION_NAME
});
export const greetingChange = (greeting) => ({
type: GREETING_CHANGE,
greeting // short for: greeting: greeting
})
\ No newline at end of file
import {
sentenceGet as apiSentenceGet,
sentencePost as apiSentencePost,
sentenceMarkDirty as apiSentenceMarkDirty
} from '../services/api';
export const SENTENCE_LOAD = 'SENTENCE_LOAD';
export const SENTENCE_LOAD_SUCCESS = 'SENTENCE_LOAD_SUCCESS';
export const SENTENCE_LOAD_FAIL = 'SENTENCE_LOAD_FAIL';
export const SENTENCE_POST = 'SENTENCE_POST';
export const SENTENCE_POST_SUCCESS = 'SENTENCE_POST_SUCCESS';
export const SENTENCE_POST_FAIL = 'SENTENCE_POST_FAIL';
export const SENTENCE_SUBMIT = 'SENTENCE_SUBMIT';
export const SENTENCE_CLASS_SELECT = 'SENTENCE_CLASSIFICATION_PICK';
export const SENTENCE_MARK_DIRTY = "SENTENCE_MARK_DIRTY";
export const SENTENCE_MARK_DIRTY_SUCCESS = "SENTENCE_MARK_DIRTY_SUCCESS";
export const SENTENCE_MARK_DIRTY_FAIL = "SENTENCE_MARK_DIRTY_FAIL";
export const sentenceLoad = () => (dispatch, _) => {
dispatch({ type: SENTENCE_LOAD })
apiSentenceGet()
.then((data) => dispatch(sentenceLoadSuccess(data)))
.catch(() => dispatch(sentenceLoadFail()));
}
export const sentencePost = () => (dispatch, getState) => {
const state = getState().sentence;
dispatch({ type: SENTENCE_POST })
apiSentencePost(state.id, state.selectedClass)
.then(() => {
dispatch({ type: SENTENCE_POST_SUCCESS });
dispatch(sentenceLoad())
})
.catch(() => dispatch({ type: SENTENCE_POST_FAIL }));
}
export const sentenceMarkDirty = () => (dispatch, getState) => {
const state = getState();
dispatch({
type: SENTENCE_MARK_DIRTY
});
apiSentenceMarkDirty(state.id)
.then(() => {
dispatch({ type: SENTENCE_MARK_DIRTY_SUCCESS });
dispatch(sentenceLoad())
})
.catch(() => dispatch({ type: SENTENCE_MARK_DIRTY_FAIL }))
}
export const sentenceClassSelect = (selectedClass) => ({
type: SENTENCE_CLASS_SELECT,
selectedClass: selectedClass
})
export const sentenceLoadSuccess = ({ id, sentence, predictions }) => ({
type: SENTENCE_LOAD_SUCCESS,
id, sentence, predictions })
export const sentenceLoadFail = () => ({ type: SENTENCE_LOAD_FAIL })
\ No newline at end of file
import React, { Component } from 'react';
import Sentence from './Sentence';
import SentenceActions from './SentenceActions';
import SentenceClassList from './SentenceClassList';
export const Annotator = class extends Component {
componentDidMount = () => {
this.props.classesLoad();
this.props.sentenceLoad();
}
render = () => {
const {
classesLoading,
message,
sentence,
sentenceClasses,
sentenceLoading,
selectedClass,
sentenceClassSelect,
sentenceLoad,
sentencePost,
sentenceMarkDirty
} = this.props;
return <section id="annotator">
<section id="application-message">{ message }</section>
<SentenceActions
sentenceLoading={ sentenceLoading }
sentenceClassSelected={ selectedClass }
sentenceLoad={ sentenceLoad }
sentencePost={ sentencePost }
sentenceMarkDirty={ sentenceMarkDirty } />
<Sentence sentence={ sentence } />
<SentenceClassList
classesLoading={ classesLoading }
sentenceClasses={ sentenceClasses }
sentenceClassSelected={ selectedClass }
sentenceClassSelect={ sentenceClassSelect } />
</section>
}
}
export default Annotator;
\ No newline at end of file
import React from 'react';
export const Example = ({ greeting, greetingChange }) => (
<section className="example-component">
<h1>Hello, { greeting }!</h1>
<input value={ greeting } onChange={ (e) => greetingChange(e.target.value) } />
</section>
);
export default Example;
\ No newline at end of file
import React from 'react';
export const Sentence = ({
sentence
}) => (
<section className="sentence">
<p>{ sentence }</p>
</section>
);
export default Sentence;
\ No newline at end of file
import React from 'react';
import { t } from '../utils'
export const SentenceActions = ({
sentenceClassSelected,
sentenceLoading,
sentenceLoad,
sentencePost,
sentenceMarkDirty
}) => (
<section id="sentenceActions">
<button onClick={ sentenceMarkDirty } disabled={ sentenceLoading }>{ t('mark_dirty_button') }</button>
<button onClick={ sentenceLoad } disabled={ sentenceLoading }>{ t('sentence_load_new_button') }</button>
{ (sentenceClassSelected) ? <button onClick={ sentencePost } disabled={ sentenceLoading }>{ t('sentence_submit_button') }</button> : '' }
</section>
)
export default SentenceActions;
\ No newline at end of file
import React from 'react';
export const SentenceClassList = ({
sentenceClasses,
sentenceClassSelected,
sentenceClassSelect
}) => (
<section className="sentenceClassList">
{ sentenceClasses.map((sentenceClass, k) => (
<button className="sentenceClassList--class" onClick={ () => sentenceClassSelect(k) } data-selected={ (k === sentenceClassSelected) }>
{ sentenceClass}
</button>
))}
</section>
);
export default SentenceClassList;
\ No newline at end of file
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { sentenceClassSelect, sentenceLoad, sentencePost, sentenceMarkDirty } from '../actions/sentence';
import { classesLoad } from '../actions/classes';
import Annotator from '../components/Annotator'
function mapStateToProps ({ sentence, classes, application }) {
// Make a selection of the state
return {
...sentence,
sentenceClasses: classes,
...application }
}
function mapDispatchToProps (dispatch) {
// Select action creators to be bound with dispatch
return bindActionCreators({
classesLoad,
sentenceClassSelect,
sentenceLoad,
sentenceMarkDirty,
sentencePost
}, dispatch);
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Annotator);
\ No newline at end of file
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as exampleActions from '../actions/example';
import Example from '../components/Example'
// Import main component
// Import actions
function mapStateToProps ({ example }) {
// Make a selection of the state
return { ...example }
}
// alternative for more complex mapper
// function mapStateToProps (state) {
// // Make a selection of the state
// return {
// greeting: state.greeting
// }
// }
function mapDispatchToProps (dispatch) {
// Select action creators to be bound with dispatch
return bindActionCreators(exampleActions, dispatch);
}
// export default connect(
// mapStateToProps,
// mapDispatchToProps
// )(Component);
export default connect(
mapStateToProps,
mapDispatchToProps
)(Example);
\ No newline at end of file
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux'
import { ConnectedRouter } from 'connected-react-router';
import './index.css';
import { configureStore, history } from './store';
import App from './App';
import * as serviceWorker from './serviceWorker';
const store = configureStore({});
ReactDOM.render(
<Provider store={store}>
<ConnectedRouter history={history}>
<App />
</ConnectedRouter>
</Provider>, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();
export const messages = {
'classes_load': 'Loading annotation classes',
'classes_load_fail': 'Could not retreive classes',
'sentence_load': 'Loading a new sentence to annotate',
'sentence_load_fail': 'Could not load a new sentence',
'sentence_load_new_button': 'Load another sentence',
'sentence_submit_button': 'Submit Annotation',
'mark_dirty_button': 'This sentence should be cleaned',
'sentence_mark_dirty': 'Scheduling sentence to be cleaned',
'sentence_mark_dirty_fail': 'Unable to send feedback to the server',
'sentence_mark_dirty_success': 'Feedback sent to the server',
'sentence_post': 'Sending annotation',
'sentence_post_fail': 'Unable to send annotation',
'sentence_post_success': 'Sent annotation to the server',
}
export default messages;
\ No newline at end of file
import { messages as en } from './en';
export { en };
\ No newline at end of file
import { MESSAGE_CLOSE } from '../actions/application';
import {
CLASSES_LOAD,
CLASSES_LOAD_FAIL,
CLASSES_LOAD_SUCCESS
} from '../actions/classes';
import {
SENTENCE_LOAD,
SENTENCE_LOAD_FAIL,
SENTENCE_LOAD_SUCCESS,
SENTENCE_POST,
SENTENCE_POST_FAIL,
SENTENCE_POST_SUCCESS,
SENTENCE_MARK_DIRTY,
SENTENCE_MARK_DIRTY_FAIL,
SENTENCE_MARK_DIRTY_SUCCESS
} from '../actions/sentence';
import { simpleUpdate, t } from '../utils';
const initialState = {
sentenceLoading: false,
classesLoading: false,
message: null
}
export default function example (state = initialState, action) {
switch (action.type) {
case MESSAGE_CLOSE:
return simpleUpdate(state, { message: null })
case CLASSES_LOAD:
return simpleUpdate(state, { classesLoading: true, message: t('classes_load') })
case CLASSES_LOAD_FAIL:
return simpleUpdate(state, { sentenceLoading: false, message: t('classes_load_fail') })
case CLASSES_LOAD_SUCCESS:
return simpleUpdate(state, { classesLoading: false, message: '' })
case SENTENCE_LOAD:
return simpleUpdate(state, { sentenceLoading: true, message: t('sentence_load') })
case SENTENCE_LOAD_FAIL:
return simpleUpdate(state, { sentenceLoading: false, message: t('sentence_load_fail')})
case SENTENCE_LOAD_SUCCESS:
return simpleUpdate(state, { sentenceLoading: false, message: '' })