Introduction
Cape from the now defunct Bloomsbury AI is an open source project that provides an API that makes it easy to build software that answers questions about the contents of documents. For example, you can use Cape to:
- Build a super-powered ctrl+f that finds the answer to questions like 'Who is the CTO?', rather than just all the occurrences of a keyword;
- Build an expressive query tool for textual data - extract features with a single question;
- Build a virtual office assistant that answers routine questions through Slack; and
- Build an add-on to your private search that mimics Google's 'direct answers'.
You can communicate with Cape either directly over HTTP or using our Python client library. To install the Python client simply run:
pip3 install cape-client
Backend service - API Server
This documentation is for version 0.1 of the API.
You need to launch the webservice backend that will provide the API, more info here.
In this documentation we will refer to the location of the backend server as : http://localhost:5050/
Frontend service - Management dashboard UI
You can run the frontend service that hosts this documentation and a management and dashboard UI.
The frontend server also provides a handy mock API to tests your integrations.
In this documentation we will refer to the location of the frontend server as : http://localhost:5051/
Answer
The answer endpoint answers by reading saved replies and documents.
Answering questions
To ask the question "what is the colour of the sky ?"
curl 'http://localhost:5050/api/0.1/answer?token=08aerv08ajkdp&question=what+is+the+colour+of+the+sky+?'
from cape.client import CapeClient
cc = CapeClient()
answers = cc.answer('what is the colour of the sky ?',
user_token='08aerv08ajkdp')
print(answers)
Returns the answer
{
"success": true,
"result": {
"items": [
{
"answerText": "blue",
"answerContext": "to only the lower, more dense portions of the atmosphere.\n\nDuring daylight, the sky appears to be blue because air scatters more blue sunlight than red.[1][2][3][4]",
"confidence": 0.73,
"sourceType": "document",
"sourceId": "e6451353a0a008b4e2366691e8eff687e4ce6ef28552898d8bbe8be84f4b3ec3",
"answerTextStartOffset": 706,
"answerTextEndOffset": 710,
"answerContextStartOffset": 608,
"answerContextEndOffset": 772
}
]
}
}
[
{
"answerText": "blue",
"answerContext": "to only the lower, more dense portions of the atmosphere.\n\nDuring daylight, the sky appears to be blue because air scatters more blue sunlight than red.[1][2][3][4]",
"confidence": 0.73,
"sourceType": "document",
"sourceId": "e6451353a0a008b4e2366691e8eff687e4ce6ef28552898d8bbe8be84f4b3ec3",
"answerTextStartOffset": 706,
"answerTextEndOffset": 710,
"answerContextStartOffset": 608,
"answerContextEndOffset": 772
}
]
Definition
/api/0.1/answer
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| token | 08aerv08ajkdp | The token retrieved from /get-user-token |
| question | What colour is the sky? | The question to ask. |
| text | "The sky is blue" | Optional inline text to be treated as a temporary document with id 'Inline Text' |
| threshold | low | The minimum confidence of answers to return. Can be one of 'veryhigh', 'high', 'medium', 'low', 'verylow'. |
| documentIds | document_1, document_2 or ["document_1","document_2"] | A json list or comma separated list of document ids to search within for answers. By default searches all documents. |
| sourceType | document | What type to read to answer documents, if 'document' only read documents, if 'saved_reply' only read annotations and saved replies, if 'all' read both types. Can be one of 'document', 'saved_reply' or 'all'. Default: 'all'. |
| speedOrAccuracy | speed | Prioritise speed or accuracy in answers. Can be one of 'speed', 'accuracy' or 'balanced'. Default: 'balanced'. |
| numberOfItems | 5 | The number of answers to return. Default: 1. |
| offset | 0 | The starting index for the list of answers. If this value is '2', and 'numberOfItems' is '5', then answers 2 to 5 will be returned. Default: 0. |
Result
Returns a list of answers:
| Field | Example | Description |
|---|---|---|
| answerText | blue | The proposed answer to the question. |
| answerContext | to only the lower, more dense portions of the atmosphere.During daylight, the sky appears to be blue because air scatters more blue sunlight than red.[1][2][3][4] | The context surrounding the proposed answer (if sourceType is 'document'). |
| confidence | 0.73 | The confidence score of this response, should only be used to sort results and not as probability. |
| sourceType | document | Whether this result came from a document or a saved reply. Can be either 'document' or 'saved_reply'. |
| sourceId | document_123 | The Id of the document or saved reply this answer was found in (depending on sourceType). |
| answerTextStartOffset | 706 | The first character of the answer. As an index in the document, starting at 0. (if sourceType is 'document'). |
| answerTextEndOffset | 710 | The last character of the answer. As an index in the document, starting at 0. (if sourceType is 'document'). |
| answerContextStartOffset | 608 | The first character of the context. As an index in the document, starting at 0. (if sourceType is 'document'). |
| answerContextEndOffset | 772 | The last character of the context. As an index in the document, starting at 0. (if sourceType is 'document'). |
| matchedQuestion | What are the opening hours ? | The original question that matched the provided question. (if sourceType is 'saved_reply'). |
User - Authentication
Most API endpoints require authentication, there are three primary ways to authenticate:
- Login authentication: Proves that the user has gone through login and can build his own AI.
- Cape login : A login given by the cape team,
- Google login (can be manually enabled): Use your Google credentials to login,
- Facebook login (can be manually enabled): Use your Facebook credentials to login
- Admin token authentication: Alternative mechanism for proving that the user has been given authorisation to build their own AI.
- User token authentication: Proves that an AI builder has given the authorisation of usage to a third user.
Cape login authentication
Login authentication provides both a cookie session Id and the current admin token. Either can be used to authenticate future commands, either by providing the cookie as part of the future requests or by passing the admin token via the adminToken parameter.
To obtain a cookie session Id:
curl -v "http://localhost:5050/api/0.1/user/login?login=username&password=password"
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
This returns the session Id
GET /api/0.1/user/login?login=username&password=password HTTP/1.1
Host: localhost:5050
User-Agent: curl/7.43.0
Accept: */*
>
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: 60
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token
Set-Cookie: session=YRLGcYs3-PpBIa-PT8Q_c1OH6uKXrSYY9UvXnsbZapA; Path=/; expires=2592000; HttpOnly
Content-Length: 124
Content-Type: application/json
<
* Connection #0 to host localhost left intact
Although not needed, the session Id is returned for indicative purposes
{
"success":true,
"result":{
"message":"Valid credentials supplied",
"sessionId":"YRLGcYs3-PpBIa-PT8Q_c1OH6uKXrSYY9UvXnsbZapA"
"adminToken":"29186dba-b589-11e7-97a4-9801a7ae6c69"
}
}
When logging in, a cookie with the necessary session Id is returned. With this cookie all following requests can be authenticated.
Definition
/api/0.1/user/login
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| login | username | The login of the user. |
| password | password | The password of the user. |
Result
| Field | Example | Description |
|---|---|---|
| message | Valid credentials supplied | A message describing the success of the credential validation. |
| sessionId | ja9ev-a98ere-da | A session Id (for indicative purposes only, defined as a cookie). |
| adminToken | 29186dba-b589-11e7-97a4-9801a7ae6c69 | The current admin token which can be passed as a parameter instead of cookie-based authentication. |
Google authentication
To enable this authentication, you need to configure the backend accordingly and add a button to the frontend pointing to the necessary callback. Google authentication provides a cookie session Id after the user correctly logs in with a Google account. The user will be redirected to a supplied URL after authentication.
Definition
/api/0.1/user/google-oauth2callback
Input
Input parameters in bold are required, all other parameters are optional.
All parameters for this request must be provided via POST.
| Field | Example | Description |
|---|---|---|
| successCallback | http://localhost:5051 | URL to redirect the user to after correct authentication. |
| errorCallback | http://localhost:5051/error | URL to redirect the user to after an error in authentication. |
Result
A cookie will be created and the user will be redirected to the supplied URL.
Facebook authentication
To enable this authentication, you need to configure the backend accordingly and add a button to the frontend pointing to the necessary callback. Facebook authentication provides a cookie session Id after the user correctly logs in with a Facebook account. The user will be redirected to a supplied URL after authentication.
Definition
/api/0.1/user/facebook-oauth2callback
Input
Input parameters in bold are required, all other parameters are optional.
All parameters for this request must be provided via POST.
| Field | Example | Description |
|---|---|---|
| successCallback | http://localhost:5051 | URL to redirect the user to after correct authentication. |
| errorCallback | http://localhost:5051/error | URL to redirect the user to after an error in authentication. |
Result
A cookie will be created and the user will be redirected to the supplied URL.
Admin token authentication
In addition to being provided as part of the login endpoint, the admin token can be requested at any time via /user/get-admin-token.
To obtain the current admin token
curl 'http://localhost:5050/api/0.1/user/get-admin-token' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('username', 'password')
print(cc.get_admin_token())
Responds with the admin token
{
"success":true,
"result":{
"adminToken":"29186dba-b589-11e7-97a4-9801a7ae6c69"
}
}
This token can then be used to make further requests without doing a cookie based login
curl 'http://localhost:5050/api/0.1/user/get-profile?adminToken=29186dba-b589-11e7-97a4-9801a7ae6c69'
from cape.client import CapeClient
cc = CapeClient(admin_token="29186dba-b589-11e7-97a4-9801a7ae6c69")
print(cc.get_profile())
The admin token must be passed as a GET parameter.
Definition
/api/0.1/user/get-admin-token
Mock example
Input
None
Result
| Field | Example | Description |
|---|---|---|
| adminToken | 29186dba-b589-11e7-97a4-9801a7ae6c69 | The current admin token which can be passed as a parameter instead of cookie based authentication. |
All methods which require authentication will accept the admin token via the adminToken GET parameter instead of a cookie based authentication.
User token authentication
To obtain the user token. The user token is used for authenticating the 'answer' method.
curl 'http://localhost:5050/api/0.1/user/get-user-token' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('username', 'password')
token = cc.get_user_token()
print(token)
This returns the token
{
"success":true,
"result":{
"userToken":"08aerv08ajkdp"
}
}
'08aerv08ajkdp'
The returned user token can then be used to interact with the Cape AI's answer endpoint.
The user token must be passed as a GET parameter.
Definition
/api/0.1/user/get-user-token
Mock example
Input
None
Result
| Field | Example | Description |
|---|---|---|
| userToken | j89hvfv89s | A token that gives the holder access to only the /answer endpoint. |
Logout
To logout
curl -v "http://localhost:5050/api/0.1/user/logout" \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
print("Logged in:", cc.logged_in())
cc.logout()
print("Logged in:", cc.logged_in())
This will delete the cookie and invalidate the session in the backend
GET /mock/full/api/0.1/user/logout HTTP/1.1
Host: localhost:5050
User-Agent: curl/7.43.0
Accept: */*
Cookie: session=aijvaoerare09vjaer90jva09r
>
HTTP/1.1 200 OK
Connection: keep-alive
Keep-Alive: 60
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token
Set-Cookie: session=""; Path=/; expires=0; Max-Age=0
Content-Length: 43
Content-Type: application/json
<
* Connection #0 to host localhost left intact
Producing the following output
{
"success":true,
"result":{
"message":"User logged out"
}
}
Logged in: True
Logged in: False
This operation will log the user out.
Definition
/api/0.1/user/logout
Mock example
Input
None
Result
| Field | Example | Description |
|---|---|---|
| message | User logged out | A message confirming the operation |
Documents
Getting documents
To retrieve a list of documents
curl 'http://localhost:5050/api/0.1/documents/get-documents' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
documents = cc.get_documents()
print(documents)
Responds with a list of documents
{
"success":true,
"result":{
"totalItems":2,
"items":[
{
"id":"custom_id_2",
"title":"document2.txt",
"origin":"document2.txt",
"text":"This is another document.",
"type":"file",
"created":1508169352,
"modified":1508169352
},
{
"id":"358e1b77c9bcc353946dfe107d6b32ff",
"title":"cape_api.txt",
"origin":"cape_api.txt",
"text":"Welcome to the Cape API 0.1. Hopefully it's pretty easy to use.",
"type":"text",
"created":1508161723,
"modified":1508161723
}
]
}
}
{
'totalItems': 2,
'items': [
{
'id': 'custom_id_2',
'title': 'document2.txt',
'origin': 'document2.txt',
'text': 'This is another document.',
'type': 'file',
'created':<PHONE_NUMBER>,
'modified':<PHONE_NUMBER>
},
{
'id': '358e1b77c9bcc353946dfe107d6b32ff',
'title': 'cape_api.txt',
'origin': 'cape_api.txt',
'text': "Welcome to the Cape API 0.1. Hopefully it's pretty easy to use.",
'type': 'text',
'created':<PHONE_NUMBER>,
'modified':<PHONE_NUMBER>
}
]
}
Definition
/api/0.1/documents/get-documents
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| documentIds | 358e1b77c9bcc353946dfe107d6b32ff | A json list or comma separated list of document Ids to return. By default returns all documents. |
| numberOfItems | 5 | The number of documents to return. Default: 30. |
| offset | 0 | The start index of the list of documents. For example, if this is '2', then the returned documents omits the first two documents. (Default: 0) |
Result
Returns a list of documents in reverse chronological order (newest first):
| Field | Example | Description |
|---|---|---|
| id | 358e1b77c9bcc353946dfe107d6b32ff | The Id of this document |
| title | API Documentation | The document's title (specified at upload). |
| origin | cape_api.txt | Where this document originally came from. |
| text | Welcome to the Cape API 0.1. Hopefully it's pretty easy to use. | The contents of the document. |
| type | text | Whether this document was created by inputting text or uploading a file. Possible options 'text' or 'file'. |
| created |<PHONE_NUMBER> | Timestamp of when this document was first uploaded. |
| modified |<PHONE_NUMBER> | Timestamp of when this document was last modified. |
Uploading a document
To upload a new documents
curl 'http://localhost:5050/api/0.1/documents/add-document?title=Example%20document&text=This%20is%20an%20example.&origin=example.txt' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
document_id = cc.add_document(title="Example document",
text="This is an example.",
origin="example.txt")
print(document_id)
Responds with the Id of the new document
{
"success":true,
"result":{
"documentId":"4e2fc0c58f973ddd4ace0de85a5ebba9b88cc35df80d8c455c6079726075d3bf"
}
}
'4e2fc0c58f973ddd4ace0de85a5ebba9b88cc35df80d8c455c6079726075d3bf'
Definition
/api/0.1/documents/add-document
Mock example
Input
Input parameters in bold are required, all other parameters are optional. In the case of text and file, one or the other is required, not both.
| Field | Example | Description |
|---|---|---|
| title | Example document | The title to give the new document. |
| documentId | my_document.txt | The ID to give the new document. Default: An SHA256 hash of the document contents. |
| origin | my_document.txt | Where the document came from. Default: The document's filename when performing a file upload, otherwise empty. |
| text | Welcome to my document. | The plain text contents of the document. Either text or file must be supplied. |
| file | A file POSTed as multipart/form-data. Either text or file must be supplied. | |
| replace | true | If true and a document already exists with the same documentId it will be overwritten with the new upload. If false an error is returned when a documentId already exists. Default: false. |
| type | text | Whether this document was created by inputting text or uploading a file. Can be either 'text' or 'file'. Default: text. |
Result
| Field | Example | Description |
|---|---|---|
| documentId | my_document.txt | The Id of the new document. |
Deleting a document
To delete an existing document
curl 'http://localhost:5050/api/0.1/documents/delete-document?documentId=358e1b77c9bcc353946dfe107d6b32ff' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
document_id = cc.delete_document('358e1b77c9bcc353946dfe107d6b32ff')
print(document_id)
Responds with the Id of the deleted document
{
"success":true,
"result":{
"documentId":"358e1b77c9bcc353946dfe107d6b32ff"
}
}
'358e1b77c9bcc353946dfe107d6b32ff'
Definition
/api/0.1/documents/delete-document
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| documentId | 358e1b77c9bcc353946dfe107d6b32ff | The Id of the document to delete |
Result
| Field | Example | Description |
|---|---|---|
| documentId | 358e1b77c9bcc353946dfe107d6b32ff | The Id of the document that was deleted |
Saved replies
Saved replies are made up of a pair consisting of a canonical question and the response it should produce. In addition to the canonical question a saved reply may have many paraphrased questions associated with it which should produce the same answer (e.g. "How old are you?" vs "What is your age?").
The canonical question should be easy to understand and clear - like an FAQ. Paraphrases might be less obvious ways of phrasing the same thing.
All saved reply methods require authentication.
Getting saved replies
To retrieve a list of saved replies
curl 'http://localhost:5050/api/0.1/saved-replies/get-saved-replies' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
saved_replies = cc.get_saved_replies()
print(saved_replies)
Responds with a list of saved replies
{
"success":true,
"result":{
"totalItems":2,
"items":[
{
"id":"a0bee8c2-c3b1-11e7-a7fe-9801a7ae6c69",
"canonicalQuestion":"How old are you?",
"answers":[
{
"id": "a67678b6-c3b1-11e7-abaa-9801a7ae6c69",
"answer": "18"
}
],
"paraphraseQuestions":[
{
"id": "aa8061f6-c3b1-11e7-b59b-9801a7ae6c69",
"question": "What's your age?"
},
{
"id": "aedfbb18-c3b1-11e7-b7c0-9801a7ae6c69",
"question": "What age are you?"
}
],
"created":1508161734,
"modified":1508161734
},
{
"id":"b496121c-c3b1-11e7-a59d-9801a7ae6c69",
"canonicalQuestion":"What colour is the sky?",
"answers":[
{
"id": "b86a4afa-c3b1-11e7-9e98-9801a7ae6c69",
"answer": "Blue"
}
],
"created":1508161323,
"modified":1508161323
}
]
}
}
{
'totalItems': 2,
'items': [
{
'id': "a0bee8c2-c3b1-11e7-a7fe-9801a7ae6c69",
'canonicalQuestion': 'How old are you?',
'answers': [
{
'id': "a67678b6-c3b1-11e7-abaa-9801a7ae6c69",
'answer': '18'
}
],
'paraphraseQuestions': [
{
'id': "aa8061f6-c3b1-11e7-b59b-9801a7ae6c69",
'question': "What's your age?"
},
{
'id': "aedfbb18-c3b1-11e7-b7c0-9801a7ae6c69",
'question': 'What age are you?'
}
],
'created':<PHONE_NUMBER>,
'modified':<PHONE_NUMBER>
},
{
'id': "b496121c-c3b1-11e7-a59d-9801a7ae6c69",
'canonicalQuestion': 'What colour is the sky?',
'answers': [
{
'id': "b86a4afa-c3b1-11e7-9e98-9801a7ae6c69",
'answer': 'Blue'
}
],
'paraphraseQuestions': [],
'created':<PHONE_NUMBER>,
'modified':<PHONE_NUMBER>
}
]
}
Definition
/api/0.1/saved-replies/get-saved-replies
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| savedReplyIds | b496121c-c3b1-11e7-a59d-9801a7ae6c69 | A json list or comma separated list of saved reply Ids to return. Returns all saved replies by default. |
| searchTerm | sky | Filter saved replies based on whether they contain the search term. |
| numberOfItems | 5 | The number of saved replies to return. Default: 30. |
| offset | 0 | The starting index in the list of saved replies, used in conjunction with numberOfItems to retrieve multiple batches of saved replies. Default: 0. |
Result
Returns a list of saved replies in reverse chronological order (newest first):
| Field | Example | Description |
|---|---|---|
| id | b86a4afa-c3b1-11e7-9e98-9801a7ae6c69 | The reply Id |
| canonicalQuestion | How old are you? | The main question to identify this saved reply |
| answers | [{"id": "e953d7a8-c3b1-11e7-ba24-9801a7ae6c69", "answer": "18"}] | A list of saved answers, one of which will be selected at random as the response to the question. |
| paraphraseQuestions | [{"id": "aedfbb18-c3b1-11e7-b7c0-9801a7ae6c69", "question": "What age are you?"}] | A list of questions which paraphrase the canonical question |
| modified |<PHONE_NUMBER> | Timestamp indicating when this saved reply was last modified |
| created |<PHONE_NUMBER> | Timestamp indicating when this saved reply was created |
Creating a saved reply
To create a new saved reply
curl 'http://localhost:5050/api/0.1/saved-replies/add-saved-reply?question=When%20is%20it%20most%20likely%20to%20snow%3F&answer=In%20the%20winter' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
response = cc.add_saved_reply('When is it most likely to snow?', 'In the winter')
print(response)
Responds with the Id of the new saved reply
{
"success":true,
"result":{
"replyId":"f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69",
"answerId":"a67678b6-c3b1-11e7-abaa-9801a7ae6c69"
}
}
{
"replyId": "f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69",
"answerId": "a67678b6-c3b1-11e7-abaa-9801a7ae6c69"
}
Definition
/api/0.1/saved-replies/add-saved-reply
Mock example
Questions (whether Canonical or Paraphrase) must be unique.
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| question | What colour is the sky? | The canonical question this saved reply relates to. |
| answer | Blue | An answer to reply with when the question is asked. |
Result
| Field | Example | Description |
|---|---|---|
| replyId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the new saved reply. |
| answerId | a67678b6-c3b1-11e7-abaa-9801a7ae6c69 | The Id of the new answer in the saved reply. |
Deleting a saved reply
Deletes a saved reply, and its associated canonical and paraphrase questions.
To delete an existing saved reply
curl 'http://localhost:5050/api/0.1/saved-replies/delete-saved-reply?replyId=f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
reply_id = cc.delete_saved_reply("f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69")
print(reply_id)
Responds with the Id of the deleted reply
{
"success":true,
"result":{
"replyId":"f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69"
}
}
"f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69"
Definition
/api/0.1/saved-replies/delete-saved-reply
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| replyId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the saved reply to delete. |
Result
| Field | Example | Description |
|---|---|---|
| replyId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the saved reply that was deleted. |
Adding paraphrase questions
To add a paraphrase question to an existing saved reply
curl 'http://localhost:5050/api/0.1/saved-replies/add-paraphrase-question?replyId=f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69&question=What%20is%20your%20age?' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
question_id = cc.add_paraphrase_question("f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69", 'What is your age?')
print(question_id)
Responds with the Id of the new question
{
"success":true,
"result":{
"questionId":"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
}
}
"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
Definition
/api/0.1/saved-replies/add-paraphrase-question
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| replyId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the saved reply to add a question to |
| question | What is your age? | The question to add as a paraphase to this saved reply |
Result
| Field | Example | Description |
|---|---|---|
| questionId | 21e9689e-c3b2-11e7-8a22-9801a7ae6c69 | The Id of the paraphrased question that was created |
Deleting paraphrase questions
To delete a paraphrase question
curl 'http://localhost:5050/api/0.1/saved-replies/delete-paraphrase-question?questionId=21e9689e-c3b2-11e7-8a22-9801a7ae6c69' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
question_id = cc.delete_paraphrase_question("21e9689e-c3b2-11e7-8a22-9801a7ae6c69")
print(question_id)
Responds with the Id of the deleted question
{
"success":true,
"result":{
"questionId":"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
}
}
"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
Definition
/api/0.1/saved-replies/delete-paraphrase-question
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| questionId | 21e9689e-c3b2-11e7-8a22-9801a7ae6c69 | The Id of the paraphrase question to delete |
Result
| Field | Example | Description |
|---|---|---|
| questionId | 21e9689e-c3b2-11e7-8a22-9801a7ae6c69 | The Id of the paraphrase question that was deleted |
Modifying paraphrase questions
To edit an existing paraphrase question
curl 'http://localhost:5050/api/0.1/saved-replies/edit-paraphrase-question?questionId=21e9689e-c3b2-11e7-8a22-9801a7ae6c69&question=How%20old%20are%you?' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
question_id = cc.edit_paraphrase_question("21e9689e-c3b2-11e7-8a22-9801a7ae6c69", 'How old are you?')
print(question_id)
Responds with the Id of the edited question
{
"success":true,
"result":{
"questionId":"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
}
}
"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
Definition
/api/0.1/saved-replies/edit-paraphrase-question
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| questionId | 21e9689e-c3b2-11e7-8a22-9801a7ae6c69 | The Id of the paraphrase question to modify. |
| question | What age are you? | The updated question text to use for this paraphrase. |
Result
| Field | Example | Description |
|---|---|---|
| questionId | 21e9689e-c3b2-11e7-8a22-9801a7ae6c69 | The Id of the paraphrase question that was modified. |
Modifying canonical questions
To modify a saved reply's canonical question
curl 'http://localhost:5050/api/0.1/saved-replies/edit-canonical-question?replyId=f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69&question=What%20age%20are%20you?' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
reply_id = cc.edit_canonical_question("f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69", 'What age are you?')
print(reply_id)
Responds with the Id of the modified saved reply
{
"success":true,
"result":{
"replyId":"f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69"
}
}
"f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69"
Definition
/api/0.1/saved-replies/edit-canonical-question
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| replyId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the saved reply to modify. |
| question | What age are you? | The updated canonical question text to use for this saved reply. |
Result
| Field | Example | Description |
|---|---|---|
| replyId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the saved reply that was modified. |
Adding answers
To add an additional answer to an existing saved reply
curl 'http://localhost:5050/api/0.1/saved-replies/add-answer?replyId=68c445cc-c3b2-11e7-8a88-9801a7ae6c69&answer=Grey' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
answer_id = cc.add_answer("68c445cc-c3b2-11e7-8a88-9801a7ae6c69", 'Grey')
print(answer_id)
Responds with the Id of the new answer
{
"success":true,
"result":{
"answerId":"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
}
}
"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
Definition
/api/0.1/saved-replies/add-answer
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| replyId | 68c445cc-c3b2-11e7-8a88-9801a7ae6c69 | The Id of the saved reply to add an answer to |
| answer | Grey | The new answer to add to this saved reply |
Result
| Field | Example | Description |
|---|---|---|
| answerId | 703acab4-c3b2-11e7-b8b1-9801a7ae6c69 | The Id of the answer that was added |
Deleting answers
To delete an answer from a saved reply
curl 'http://localhost:5050/api/0.1/saved-replies/delete-answer?answerId=703acab4-c3b2-11e7-b8b1-9801a7ae6c69' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
answer_id = cc.delete_answer("703acab4-c3b2-11e7-b8b1-9801a7ae6c69")
print(answer_id)
Responds with the Id of the deleted answer
{
"success":true,
"result":{
"answerId":"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
}
}
"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
Saved replies can have more than one answer. When the saved reply is matched, an answer is chosen at random from the valid answers.
It is only possible to delete answers if more than one answer currently exists.
Attempting to delete the last remaining answer in a saved reply will result in an error.
Definition
/api/0.1/saved-replies/delete-answer
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| answerId | 703acab4-c3b2-11e7-b8b1-9801a7ae6c69 | The Id of the answer to delete. |
Result
| Field | Example | Description |
|---|---|---|
| answerId | 703acab4-c3b2-11e7-b8b1-9801a7ae6c69 | The Id of the answer that was deleted. |
Modifying answers
To edit an answer of an existing saved reply
curl 'http://localhost:5050/api/0.1/saved-replies/edit-answer?answerId=703acab4-c3b2-11e7-b8b1-9801a7ae6c69&answer=Blue' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
answer_id = cc.edit_answer("703acab4-c3b2-11e7-b8b1-9801a7ae6c69", 'Blue')
print(answer_id)
Responds with the Id of the modified answer
{
"success":true,
"result":{
"answerId":"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
}
}
"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
Definition
/api/0.1/saved-replies/edit-answer
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| answerId | 703acab4-c3b2-11e7-b8b1-9801a7ae6c69 | The Id of the answer to modify |
| answer | Blue | The updated answer text to use for this saved reply |
Result
| Field | Example | Description |
|---|---|---|
| answerId | 703acab4-c3b2-11e7-b8b1-9801a7ae6c69 | The Id of the answer that was modified |
Annotations
Annotations are made up of a pair consisting of a canonical question and the response it should produce, combined with a position within a document. In addition to the canonical question an annotation may have also have many paraphrased questions associated with it which should produce the same answer (e.g. "How old is Jim?" vs "What is Jim's age?").
The canonical question should be easy to understand and clear - like an FAQ. Paraphrases might be less obvious ways of phrasing the same thing.
All annotation methods require authentication.
Getting annotations
To retrieve a list of annotations
curl 'http://localhost:5050/api/0.1/annotations/get-annotations' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
annotations = cc.get_annotations()
print(annotations)
Responds with a list of annotations
{
"success":true,
"result":{
"totalItems":1,
"items":[
{
"id": "a0bee8c2-c3b1-11e7-a7fe-9801a7ae6c69",
"canonicalQuestion": "How old are you?",
"documentId": "f2efa8c2-e3b1-12a7-f7fb-9801a7ae6e12",
"position": {
"startOffset": 12,
"endOffset": 24
},
"answers":[
{
"id": "a67678b6-c3b1-11e7-abaa-9801a7ae6c69",
"answer": "18"
}
],
"paraphraseQuestions":[
{
"id": "aa8061f6-c3b1-11e7-b59b-9801a7ae6c69",
"question": "What's your age?"
},
{
"id": "aedfbb18-c3b1-11e7-b7c0-9801a7ae6c69",
"question": "What age are you?"
}
],
"created":1508161734,
"modified":1508161734
}
]
}
}
{
'totalItems': 1,
'items': [
{
'id': "a0bee8c2-c3b1-11e7-a7fe-9801a7ae6c69",
'canonicalQuestion': 'How old are you?',
"documentId": "f2efa8c2-e3b1-12a7-f7fb-9801a7ae6e12",
"position": {
"startOffset": 12,
"endOffset": 24
},
'answers': [
{
'id': "a67678b6-c3b1-11e7-abaa-9801a7ae6c69",
'answer': '18'
}
],
'paraphraseQuestions': [
{
'id': "aa8061f6-c3b1-11e7-b59b-9801a7ae6c69",
'question': "What's your age?"
},
{
'id': "aedfbb18-c3b1-11e7-b7c0-9801a7ae6c69",
'question': 'What age are you?'
}
],
'created':<PHONE_NUMBER>,
'modified':<PHONE_NUMBER>
}
]
}
Definition
/api/0.1/annotations/get-annotations
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| annotationIds | b496121c-c3b1-11e7-a59d-9801a7ae6c69 | A json list or comma separated list of annotation IDs to return. Returns all annotations by default. |
| documentIds | e453211f-f3b5-12b4-a59d-1143a7fb6a78 | A json list or comma separated list of document IDs to filter on. |
| searchTerm | sky | Filter annotations based on whether they contain the search term. |
| numberOfItems | 5 | The number of annotations to return. Default: 30. |
| offset | 0 | The starting index in the list of annotations, used in conjunction with numberOfItems to retrieve multiple batches of annotations. Default: 0. |
Result
Returns a list of annotations in reverse chronological order (newest first):
| Field | Example | Description |
|---|---|---|
| id | b86a4afa-c3b1-11e7-9e98-9801a7ae6c69 | The annotation Id. |
| canonicalQuestion | How old are you? | The main question to identify this annotation. |
| documentId | e453211f-f3b5-12b4-a59d-1143a7fb6a78 | The document that this annotation is associated with. |
| position | A position object specifying a start offset and end offset. | |
| answers | [{"id": "e953d7a8-c3b1-11e7-ba24-9801a7ae6c69", "answer": "18"}] | A list of saved answers, one of which will be selected at random as the response to the question. |
| paraphraseQuestions | [{"id": "aedfbb18-c3b1-11e7-b7c0-9801a7ae6c69", "question": "What age are you?"}] | A list of questions which paraphrase the canonical question. |
| modified |<PHONE_NUMBER> | Timestamp indicating when this annotation was last modified. |
| created |<PHONE_NUMBER> | Timestamp indicating when this annotation was created. |
Creating an annotation
To create a new annotation
curl 'http://localhost:5050/api/0.1/annotations/add-annotation?question=When%20is%20it%20most%20likely%20to%20snow%3F&answer=In%20the%20winter' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
response = cc.add_annotation('When is it most likely to snow?', 'In the winter', document_id='e453211f-f3b5-12b4-a59d-1143a7fb6a78', start_offset=12, end_offset=40)
print(response)
Responds with the Id of the new annotation
{
"success":true,
"result":{
"annotationId":"f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69",
"answerId":"a67678b6-c3b1-11e7-abaa-9801a7ae6c69"
}
}
{
"annotationId": "f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69",
"answerId": "a67678b6-c3b1-11e7-abaa-9801a7ae6c69"
}
Definition
/api/0.1/annotations/add-annotation
Mock example
Questions (whether Canonical or Paraphrase) must be unique for each document.
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| question | What colour is the sky? | The canonical question this annotation relates to. |
| answer | Blue | An answer to annotation with when the question is asked. |
| documentId | e453211f-f3b5-12b4-a59d-1143a7fb6a78 | The document that this annotation is associated with. |
| startOffset | 12 | The start of this annotation (for text documents). |
| endOffset | 24 | The end of this annotation (for text documents). |
| metadata | A dictionary of user defined information about this annotation. |
Result
| Field | Example | Description |
|---|---|---|
| annotationId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the new annotation. |
| answerId | a67678b6-c3b1-11e7-abaa-9801a7ae6c69 | The Id of the new answer in the annotation. |
Deleting an annotation
Deletes an annotation, and its associated canonical and paraphrase questions.
To delete an existing annotation
curl 'http://localhost:5050/api/0.1/annotations/delete-annotation?annotationId=f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
annotation_id = cc.delete_annotation("f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69")
print(annotation_id)
Responds with the Id of the deleted annotation
{
"success":true,
"result":{
"annotationId":"f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69"
}
}
"f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69"
Definition
/api/0.1/annotations/delete-annotation
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| annotationId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the annotation to delete. |
Result
| Field | Example | Description |
|---|---|---|
| annotationId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the annotation that was deleted. |
Adding paraphrase questions to annotations
To add a paraphrase question to an existing annotation
curl 'http://localhost:5050/api/0.1/annotations/add-paraphrase-question?annotationId=f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69&question=What%20is%20your%20age?' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
question_id = cc.add_annotation_paraphrase_question("f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69", 'What is your age?')
print(question_id)
Responds with the Id of the new question
{
"success":true,
"result":{
"questionId":"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
}
}
"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
Definition
/api/0.1/annotations/add-paraphrase-question
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| annotationId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the annotation to add a question to. |
| question | What is your age? | The question to add as a paraphase to this annotation. |
Result
| Field | Example | Description |
|---|---|---|
| questionId | 21e9689e-c3b2-11e7-8a22-9801a7ae6c69 | The Id of the paraphrased question that was created. |
Deleting an annotation's paraphrase questions
To delete a paraphrase question
curl 'http://localhost:5050/api/0.1/annotations/delete-paraphrase-question?questionId=21e9689e-c3b2-11e7-8a22-9801a7ae6c69' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
question_id = cc.delete_annotation_paraphrase_question("21e9689e-c3b2-11e7-8a22-9801a7ae6c69")
print(question_id)
Responds with the Id of the deleted question
{
"success":true,
"result":{
"questionId":"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
}
}
"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
Definition
/api/0.1/annotations/delete-paraphrase-question
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| questionId | 21e9689e-c3b2-11e7-8a22-9801a7ae6c69 | The Id of the paraphrase question to delete |
Result
| Field | Example | Description |
|---|---|---|
| questionId | 21e9689e-c3b2-11e7-8a22-9801a7ae6c69 | The Id of the paraphrase question that was deleted |
Modifying an annotation's paraphrase questions
To edit an existing paraphrase question
curl 'http://localhost:5050/api/0.1/annotations/edit-paraphrase-question?questionId=21e9689e-c3b2-11e7-8a22-9801a7ae6c69&question=How%20old%20are%you?' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
question_id = cc.edit_annotation_paraphrase_question("21e9689e-c3b2-11e7-8a22-9801a7ae6c69", 'How old are you?')
print(question_id)
Responds with the Id of the edited question
{
"success":true,
"result":{
"questionId":"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
}
}
"21e9689e-c3b2-11e7-8a22-9801a7ae6c69"
Definition
/api/0.1/annotations/edit-paraphrase-question
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| questionId | 21e9689e-c3b2-11e7-8a22-9801a7ae6c69 | The Id of the paraphrase question to modify. |
| question | What age are you? | The updated question text to use for this paraphrase. |
Result
| Field | Example | Description |
|---|---|---|
| questionId | 21e9689e-c3b2-11e7-8a22-9801a7ae6c69 | The Id of the paraphrase question that was modified. |
Modifying an annotation's canonical question
To modify an annotation's canonical question
curl 'http://localhost:5050/api/0.1/annotations/edit-canonical-question?annotationId=f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69&question=What%20age%20are%20you?' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
annotation_id = cc.edit_annotation_canonical_question("f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69", 'What age are you?')
print(annotation_id)
Responds with the Id of the modified annotation
{
"success":true,
"result":{
"annotationId":"f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69"
}
}
"f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69"
Definition
/api/0.1/annotations/edit-canonical-question
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| annotationId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the annotation to modify. |
| question | What age are you? | The updated canonical question text to use for this annotation. |
Result
| Field | Example | Description |
|---|---|---|
| annotationId | f9f1cf90-c3b1-11e7-91a1-9801a7ae6c69 | The Id of the annotation that was modified. |
Adding answers to annotations
To add an additional answer to an existing annotation
curl 'http://localhost:5050/api/0.1/annotations/add-answer?annotationId=68c445cc-c3b2-11e7-8a88-9801a7ae6c69&answer=Grey' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
answer_id = cc.add_annotation_answer("68c445cc-c3b2-11e7-8a88-9801a7ae6c69", 'Grey')
print(answer_id)
Responds with the Id of the new answer
{
"success":true,
"result":{
"answerId":"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
}
}
"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
Definition
/api/0.1/annotations/add-answer
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| annotationId | 68c445cc-c3b2-11e7-8a88-9801a7ae6c69 | The Id of the annotation to add an answer to |
| answer | Grey | The new answer to add to this annotation |
Result
| Field | Example | Description |
|---|---|---|
| answerId | 703acab4-c3b2-11e7-b8b1-9801a7ae6c69 | The Id of the answer that was added |
Deleting answers from annotations
To delete an answer from an annotation
curl 'http://localhost:5050/api/0.1/annotations/delete-answer?answerId=703acab4-c3b2-11e7-b8b1-9801a7ae6c69' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
answer_id = cc.delete_annotation_answer("703acab4-c3b2-11e7-b8b1-9801a7ae6c69")
print(answer_id)
Responds with the Id of the deleted answer
{
"success":true,
"result":{
"answerId":"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
}
}
"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
Saved replies can have more than one answer. When the annotation is matched, an answer is chosen at random from the valid answers.
It is only possible to delete answers if more than one answer currently exists.
Attempting to delete the last remaining answer in an annotation will result in an error.
Definition
/api/0.1/annotations/delete-answer
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| answerId | 703acab4-c3b2-11e7-b8b1-9801a7ae6c69 | The Id of the answer to delete. |
Result
| Field | Example | Description |
|---|---|---|
| answerId | 703acab4-c3b2-11e7-b8b1-9801a7ae6c69 | The Id of the answer that was deleted. |
Modifying an annotation's answers
To edit an answer of an existing annotation
curl 'http://localhost:5050/api/0.1/annotations/edit-answer?answerId=703acab4-c3b2-11e7-b8b1-9801a7ae6c69&answer=Blue' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
answer_id = cc.edit_answer("703acab4-c3b2-11e7-b8b1-9801a7ae6c69", 'Blue')
print(answer_id)
Responds with the Id of the modified answer
{
"success":true,
"result":{
"answerId":"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
}
}
"703acab4-c3b2-11e7-b8b1-9801a7ae6c69"
Definition
/api/0.1/annotations/edit-answer
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| answerId | 703acab4-c3b2-11e7-b8b1-9801a7ae6c69 | The Id of the answer to modify |
| answer | Blue | The updated answer text to use for this annotation |
Result
| Field | Example | Description |
|---|---|---|
| answerId | 703acab4-c3b2-11e7-b8b1-9801a7ae6c69 | The Id of the answer that was modified |
Inbox
Fetching a user's inbox
To retrieve the currently logged in user's inbox
curl 'http://localhost:5050/api/0.1/inbox/get-inbox' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
inbox = cc.get_inbox()
print(inbox)
Returns the following list of inbox items
{
"success":true,
"result":{
"totalItems":2,
"items":[
{
"id":"4124",
"answered":false,
"read":false,
"question":"Who are you?",
"questionSource":"API",
"created":1508162032,
"answers":[]
},
{
"id":"4123",
"answered":true,
"read":false,
"question":"How easy is the API to use?",
"questionSource":"API",
"created":1508161834,
"answers":[
{
"answerText":"Hopefully it's pretty easy",
"answerContext":"in the instructions. Hopefully it's pretty easy. We think that then you can ",
"confidence":0.75,
"sourceType":"document",
"sourceId":"358e1b77c9bcc353946dfe107d6b32ff",
"answerTextStartOffset":30,
"answerTextEndOffset":56,
"answerContextStartOffset":17,
"answerContextEndOffset":98
}
]
}
]
}
}
{
'totalItems': 2,
'items': [
{
'id': '4124',
'answered': False,
'read': False,
'question': 'Who are you?',
'questionSource': 'API',
'created':<PHONE_NUMBER>,
'answers': []
},
{
'id': '4123',
'answered': True,
'read': False,
'question': 'How easy is the API to use?',
'questionSource': 'API',
'created':<PHONE_NUMBER>,
'answers': [
{
"answerText":"Hopefully it's pretty easy",
"answerContext":"in the instructions. Hopefully it's pretty easy. We think that then you can ",
"confidence":0.75,
"sourceType":"document",
"sourceId":"358e1b77c9bcc353946dfe107d6b32ff",
"answerTextStartOffset":30,
"answerTextEndOffset":56,
"answerContextStartOffset":17,
"answerContextEndOffset":98
}
]
}
]
}
Definition
/api/0.1/inbox/get-inbox
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| read | false | Filter messages based on whether they have been read. Can be 'true', 'false' or 'both'. Default: 'both'. |
| answered | true | Filter messages based on whether they have been answered. Can be 'true', 'false' or 'both'. Default: 'both'. |
| searchTerm | sky | Filter messages based on whether they contain the search term. By default there is no filter. |
| numberOfItems | 5 | The number of inbox items to return. Default: 30. |
| offset | 0 | The starting index in the list of inbox items, so if the starting index is 1 and 'numberOfItems' is 5, items 1-6 will be returned. Default: 0. |
Result
Returns a list of inbox items in reverse chronological order (newest first):
| Field | Example | Description |
|---|---|---|
| id | 5a3f6a28-c3b1-11e7-8087-9801a7ae6c69 | Unique Id for this inbox item |
| question | What colour is the sky? | The question that a user asked |
| read | false | Whether this item has been read |
| answered | true | Whether an answer could be found for this question |
| answers | A list of answers as returned by /answers endpoint | |
| created |<PHONE_NUMBER> | Timestamp indicating when this question was asked |
Mark an inbox item read
To mark an inbox item as having been read
curl 'http://localhost:5050/api/0.1/inbox/mark-inbox-read?inboxId=123' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
inbox_id = cc.mark_inbox_read("7e94dfae-c3b1-11e7-a15b-9801a7ae6c69")
print(inbox_id)
Responds with the inboxId that was marked as being read
{
"success":true,
"result":{
"inboxId":"7e94dfae-c3b1-11e7-a15b-9801a7ae6c69"
}
}
"7e94dfae-c3b1-11e7-a15b-9801a7ae6c69"
Definition
/api/0.1/inbox/mark-inbox-read
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| inboxId | 7e94dfae-c3b1-11e7-a15b-9801a7ae6c69 | The inbox item to mark as being read. |
Result
| Field | Example | Description |
|---|---|---|
| inboxId | 7e94dfae-c3b1-11e7-a15b-9801a7ae6c69 | The inbox item that was marked as read. |
Archiving an inbox item
To move an inbox item to the archive
curl 'http://localhost:5050/api/0.1/inbox/archive-inbox?inboxId=7e94dfae-c3b1-11e7-a15b-9801a7ae6c69' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
inbox_id = cc.archive_inbox("7e94dfae-c3b1-11e7-a15b-9801a7ae6c69")
print(inbox_id)
Responds with the inboxId that was archived
{
"success":true,
"result":{
"inboxId":"7e94dfae-c3b1-11e7-a15b-9801a7ae6c69"
}
}
"7e94dfae-c3b1-11e7-a15b-9801a7ae6c69"
Definition
/api/0.1/inbox/archive-inbox
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| inboxId | 7e94dfae-c3b1-11e7-a15b-9801a7ae6c69 | The inbox item to archive. |
Result
| Field | Example | Description |
|---|---|---|
| inboxId | 7e94dfae-c3b1-11e7-a15b-9801a7ae6c69 | The inbox item that was archived. |
User - Configuration
Getting the default threshold
If a threshold parameter isn't specified when calling the answer endpoint a default threshold is used.
To get the current default threshold
curl 'http://localhost:5050/api/0.1/user/get-default-threshold' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
default_threshold = cc.get_default_threshold()
print(default_threshold)
Responds with the default threshold
{
"success":true,
"result":{
"threshold":"medium"
}
}
"medium"
Definition
/api/0.1/user/get-default-threshold
Mock example
Input
None
Result
| Field | Example | Description |
|---|---|---|
| threshold | medium | The current default threshold, either 'verylow', low', 'medium', 'high' or 'veryhigh'. |
Setting the default threshold
If a threshold parameter isn't specified when calling the answer endpoint a default threshold is used.
To set a new default threshold
curl 'http://localhost:5050/api/0.1/user/set-default-threshold?threshold=high' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
default_threshold = cc.set_default_threshold("high")
print(default_threshold)
Responds with the new default threshold
{
"success":true,
"result":{
"threshold":"high"
}
}
"high"
Definition
/api/0.1/user/set-default-threshold
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| threshold | high | The new default threshold to set, must be either 'verylow', 'low', 'medium', 'high' or 'veryhigh'. |
Result
| Field | Example | Description |
|---|---|---|
| threshold | high | The new default threshold that's just been set. |
Setting the forward email address
If a forwarding email is set users can send emails to<EMAIL_ADDRESS>(where 'token' is the user token) and receive a response by email.
When an answer isn't found an email is sent to the forward email address with the top three suggestions from Cape to assist a human expert in providing the correct response, when the experts replies the answer is stored as a saved reply to answer similar future questions.
To set a new forward email address
curl<EMAIL_ADDRESS>\
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
forward_email =<EMAIL_ADDRESS>print(forward_email)
Responds with the new forward email address
{
"success":true,
"result":{
<EMAIL_ADDRESS>}
}
<EMAIL_ADDRESS>Definition
/api/0.1/user/set-forward-email
Mock example
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
|<EMAIL_ADDRESS>| The new forward email address to set. |
Result
| Field | Example | Description |
|---|---|---|
|<EMAIL_ADDRESS>| The new forward email address that has just been set. |
Getting the user profile
To get the current user's profile
curl 'http://localhost:5050/api/0.1/user/get-profile' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
print(cc.get_profile())
Responds with the user profile
{
"success":true,
"result":{
<EMAIL_ADDRESS>"plan":"free",
"termsAgreed":false,
"onboardingCompleted":false,
<EMAIL_ADDRESS>"forwardEmailVerified": false
}
}
{
<EMAIL_ADDRESS>"plan":"free",
"termsAgreed":False,
"onboardingCompleted":True,
<EMAIL_ADDRESS>"forwardEmailVerified": False
}
Definition
/api/0.1/user/get-profile
Mock example
Input
None
Result
| Field | Example | Description |
|---|---|---|
| username |<EMAIL_ADDRESS>| The currently logged in user's username. |
| plan | pro | The plan selected by the user, can be "free","basic" and "pro". |
| termsAgreed | false | Whether or not we need to display the "Terms and Conditions" banner. |
| onboardingCompleted | false | Whether or not the onboarding process was completed. |
| forwardEmail |<EMAIL_ADDRESS>| When using emails, the address to forward emails which Cape was unable to answer automatically. For more info click here. |
| forwardEmailVerified | false | Whether or not the forward email has been verified. For more info click here. |
User - Creation and Deletion
When the Google and Facebook account logins are disabled (default behaviour).
The only way to add new users is by using the SUPER ADMIN endpoints and providing the SUPER ADMIN token.
This token has to configured when launching the backend, it is by default REPLACEME
Cape SUPER ADMIN - Creating a new account
To create a new account provide, the super admin token, the login, password and optionally the user token for the new user. Please note that the login and user token need to be unique or you will get an error.
To create a new user:
curl -v "http://localhost:5050/api/0.1/user/create-user?userId=login&password=password&token=token&superAdminToken=REPLACEME"
This returns the username on success
> GET /api/0.1/user/create-user?userId=login&password=password&token=token&superAdminToken=REPLACEME HTTP/1.1
> Host: localhost:5050
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Connection: keep-alive
< Keep-Alive: 600
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS
< Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token
< Content-Length: 46
< Content-Type: application/json
<
* Connection #0 to host localhost left intact
{"success":true,"result":{"username":"login"}}
Definition
/api/0.1/user/create-user
No Mock example for this endpoint.
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| userId | mylogin | The login of the user. |
| password | mypassword | The password of the user. |
| superAdminToken | REPLACEME | The super admin token of the backend. |
| token | mytoken | The user token of the user. |
Result
| Field | Example | Description |
|---|---|---|
| username | mylogin | For indicative purposes the login of the new user. |
Cape SUPER ADMIN - Deleting an existing account
To delete a user account provide, the super admin token, the existing user login. Please note that the login must exist or you will get an error. Please note that all the user data, including documents, annotations and saved replies will be permanently deleted.
To delete an existing user:
curl -v "http://localhost:5050/api/0.1/user/delete-user?userId=login&superAdminToken=REPLACEME"
This returns the username on success
> GET /api/0.1/user/delete-user?userId=login&superAdminToken=REPLACEME HTTP/1.1
> Host: localhost:5050
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Connection: keep-alive
< Keep-Alive: 600
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS
< Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token
< Content-Length: 46
< Content-Type: application/json
<
* Connection #0 to host localhost left intact
{"success":true,"result":{"username":"login"}}
Definition
/api/0.1/user/delete-user
No Mock example for this endpoint.
Input
Input parameters in bold are required, all other parameters are optional.
| Field | Example | Description |
|---|---|---|
| userId | mylogin | The login of the user to delete. |
| superAdminToken | REPLACEME | The super admin token of the backend. |
Result
| Field | Example | Description |
|---|---|---|
| username | mylogin | For indicative purposes the login of the successfully deleted user. |
Handling results with lists
Many methods return lists of items. Where possible we've provided a consistent interface to all lists. Any method that returns a list will accept the parameters numberOfItems and offset. These can be used to retrieve items from a list in batches.
When a method returns a list it will typically also provide a key called totalItems which specifies the complete number of items available (not just the specific number requested in this call).
Using these pieces of information, you can choose which items are returned. For example, if there are 100 responses, you can get the last 5 items by setting numberOfItems to 5, and offset to 95.
To fetch just the first item from the inbox
curl 'http://localhost:5050/api/0.1/inbox/get-inbox?numberOfItems=1' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
inbox = cc.get_inbox(number_of_items=1)
print(inbox)
This returns the first item, but also informs us that there are two items in total
{
"success":true,
"result":{
"totalItems":6,
"items":[
{
"id":"4124",
"answered":false,
"read":false,
"question":"Who are you?",
"questionSource":"API",
"created":1508162032,
"answers":[]
}
]
}
}
{
'totalItems': 6,
'items':[
{
'id': '4124',
'answered': False,
'read': False,
'question': 'Who are you?',
'questionSource': 'API',
'created':<PHONE_NUMBER>,
'answers': []
}
]
}
We can try to fetch the next 5 items
curl 'http://localhost:5050/api/0.1/inbox/get-inbox?numberOfItems=5&offset=1' \
-b 'session=<your session id>'
from cape.client import CapeClient
cc = CapeClient()
cc.login('blo', 'bla')
inbox = cc.get_inbox(number_of_items=5, offset=1)
print(inbox)
Because there are only a total of two items available this just returns the second item
{
"success":true,
"result":{
"totalItems":2,
"items":[
{
"id":"4123",
"answered":true,
"read":false,
"question":"How easy is the API to use?",
"questionSource":"API",
"created":1508161834,
"answers":[
{
"text":"Hopefully it's pretty easy",
"confidence":0.75,
"sourceType":"document",
"sourceId":"358e1b77c9bcc353946dfe107d6b32ff",
"startOffset":30,
"endOffset":56
}
]
}
]
}
}
{
'totalItems': 2,
'items': [
{
'id': '4123',
'answered': True,
'read': False,
'question': 'How easy is the API to use?',
'questionSource': 'API',
'created':<PHONE_NUMBER>,
'answers': [
{
'text': "Hopefully it's pretty easy",
'confidence': 0.75,
'sourceType': 'document',
'sourceId': '358e1b77c9bcc353946dfe107d6b32ff',
'startOffset': 30,
'endOffset': 56
}
]
}
]
}
Mock services
In additional to the genuine API service the frontend service provides four mock services which can be used to assist with testing. None of these mock services has any permanent effect and will always return the same responses, making them suitable for inclusion in your own unit tests.
Each endpoint in the mock services corresponds to what an operation would return in a real setting. Each mock method returns an example of the output you could expect to see from the real API.
Full mock service
The full mock service returns complete output for a call:
{
"success":true,
"result":{
"totalItems":2,
"items":[
{
"id":"custom_id_2",
"title":"document2.txt",
"origin":"document2.txt",
"text":"This is another document.",
"type":"file",
"created":1508169352,
"modified":1508169352
},
{
"id":"358e1b77c9bcc353946dfe107d6b32ff",
"title":"cape_api.txt",
"origin":"cape_api.txt",
"text":"Welcome to the Cape API 0.1. Hopefully it's pretty easy to use.",
"type":"text",
"created":1508161723,
"modified":1508161723
}
]
}
}
The full mock service is available at http://localhost:5051/mock/full/api/0.1/. It aims at giving responses describing "full" cases, this might include :
- For a
login
endpoint : The login credentials are valid. - For a
search
endpoint : A full page of results is returned. - For an
answer question
endpoint : An answer is returned .
Unlucky mock service
The unlucky mock service returns a different type of failure for each method:
{
"success":false,
"result": {
"message":"A reply for the question 'Test?' already exists."
}
}
The unlucky mock service is available at http://localhost:5051/mock/unlucky/api/0.1/. It aims at giving responses describing "unlucky" cases, this might include :
- for a
login
endpoint : The login credentials are invalid, - for a
search
endpoint : No search results, - for an
answer question
endpoint : No answer found, - for a
document upload
endpoint : A document already exists with that Id, or - for a
delete
endpoint : The item being deleted does not exist.
These may or may not produce a "success": false
response, depending on the call. (For example a failed upload would
return false, but an empty search response would be successful just without any results in the list.)
These are useful for checking how your code handles errors.
Error mock service
The error mock service returns the same error for all methods:
{
"success":false,
"result":{
"message":"Something went wrong while processing the request."
}
}
The error mock service is available at http://localhost:5051/mock/error/api/0.1/. It aims at giving responses describing "server error" cases, this always returns the same response from all endpoints.
Timeout mock service
The timeout mock service never returns a response
The timeout mock service is available at http://localhost:5051/mock/timeout/api/0.1/. It aims at simulating a server hanging and never returning a response, this always has the same behaviour from all endpoints.


An easy way to insert pictures into FreeMind is by using Node / Set Image in the right-click popup menu (or Alt+K).
However, pictures in FreeMind are a preliminary feature:
If a node contains a link to an image, you can replace the contents of the node with the image by using the Set Image function. You can drag and drop several image files into FreeMind, select them as multiple nodes, and them turn them into images in one step by choosing Set Image.
A more technical and not-so-user-friendly way to put pictures in the nodes is by including the HTML. You have to start the node content with the tag <html>.
E.g.:


Specify S2Dao.NET assembly(Seasar.Dao) in application configuration file (app.config).
<configuration><configSections><sectionname="seasar"type="Seasar.Framework.Xml.S2SectionHandler, Seasar"/></configSections><seasar><!-- set in SingletonS2ContainerFactory#Init the path of dicon filebelow as the initial value of the root dicon file. --><configPath>App.dicon</configPath><!-- During creation of S2Container, read in assembly below into AppDomain(S2ContainerFactory#Create) --><assemblys><assembly>assembly name 1</assembly><assembly>assembly name 2</assembly><assembly>Seasar.Dao</assembly></assemblys></seasar></configuration>


asm960/gas960[c|e]Assembler
The assembler can be invoked with four invocation commands, which determine
the output file format (b.out, COFF, or ELF). The invocation syntax is:
asm960
| gas960 [ c
| e ] -option...
file [...]
- asm960
and gas960c
- invoke the assembler to generate COFF output.
- gas960
- invokes the assembler to generate b.out output.
- gas960e
- invokes the assembler to generate ELF output.
Assembler options affect assembler input, operation and output. Each
option must be preceded by a -
character. (In Windows, you can also use a /
character.) The options are listed below.
Assembler input files can contain assembly language instructions and
assembler directives.
The assembler invocation options are listed below.
- A arch
(Architecture)
Assemble instructions for the specified i960 architecture. arch
can be: SA, SB, KA, KB, CA,
CF, JA, JF, JD, HA, HD, HT, RP, CORE0, CORE1, CORE2, CORE3,
and ANY.
The default architecture is KB.
The CORE0-3 options let you create code that is compatible
with a group of processors. Here are the types of i960 processors that
are supported by each CORE switch:
CORE0 = Jx, Hx, RP
CORE1 = All 80960 architectures
CORE2 = Jx, Hx, RP
CORE3 = Cx, Jx, Hx
Use the G960ARCH
or I960ARCH
environment variable to override the default. ANY
means allow any valid i960 instruction.
Opcode mnemonics and special function register operands that are illegal
in the specified architecture are flagged as errors. Exceptions:
- With AANY,
all architecture checking is disabled.
- With x,
architecture mismatches are warnings, not errors.
- Branch-prediction bits are always assembled, since they are ignored
by architectures that don't support them.
- D symbolname
[=expression] (Define symbol)
Define the symbol symbolname
and set its value to expression.
If =expression
is not specified, the value of symbolname
is 1.
- d (Debug
symbols)
Retain symbol information about local labels beginning with L
or .
(dot). Such labels are typically compiler-generated for local symbols,
and are normally discarded to save space.
- G (Big-endian)
Assemble the file to run in a big-endian memory region. Not valid when
assembler is invoked as gas960.
- h (Help)
Display help information about the assembler and exit without doing
anything else.
- I pathname
(Include-file search path)
Add pathname
to the list of pathnames to search for source files included with the .include
directive. The default is to search only the current directory for include
files. You can specify any number of I
pathname
pairs.
- i (Interactive)
Read source from standard input instead of from a file. For keyboard
input, terminate interactive mode with a CTRL-D. (On Windows hosts, use CTRL-Z.)
- L
arg (Listing)
Generate a listing and print it on standard output. Accept all listing
defaults. arg
is:
a
List all the code, ignoring .nolist
directives.
e Print
all machine code in the listing with target-endian byte order.
f listfilename
Create a listing file with the specified filename.
n
Do not expand include files in the listing.
t title Use
title as listing title; must be quoted if it
contains embedded spaces.
z
Do not print the listing header.
- n (No
compare-and-branch-replacements)
(No relax) Normally, the assembler replaces each such instruction with
two instructions: an equivalent compare instruction and an equivalent branch
instruction. Note that this option has no effect on the processing of the
Intel Compare-and-Jump pseudo-instructions (e.g., cmpijne).
- o objfilename
(Object filename)
Use the specified filename for the output file.
- p { c |
d | b } (Position independence)
Set a flag in the output file header to mark the file as containing
position-independent code or data or both, respectively. The default is
to not mark the file header in this way. The argument must be exactly one
of the letters shown. Not valid when assembler is invoked as gas960.
- V
(Version)
Display version information about the assembler and continue with the
assembly.
- v960
(Version)
Display version information about the assembler and exit without doing
anything else.
- W (Warnings)
Suppress warning messages. Normally, both warnings and errors print
their messages on standard error. With this option, only error messages
are printed.
- x
(Allow mixed architecture)
Treat architecture mismatches (e.g., opcode not in target architecture)
as warnings, not errors.
- z (Time
stamp)
(COFF format only) Suppress the time/date stamp in the output COFF
file header. Normally, the time and date of the assembly is written into
the file header. With this option, the time/date field is set to "Date
Zero."
Return
to the top of this page.
Go
to the ic960 Interface Index.
Go
to the gcc960 Interface Index.


Details for app "{{name}}"
General information
{% if documentation_url %}Documentation: Go to app documentation
{% else %}
Documentation: Documentation not provided by the app author
{% endif %}
Detailed information
{% if metainfo %}Author(s): {{ metainfo.author }}
Short description: {{ metainfo.description }}
Package name (for pip): aiidalab-{{ name }}
Most recent version: {{ metainfo.version }}
Unable to retrieve meta information for this app.
Ask the app developers to provide a meta.json
file
in the app source code.


I recently performed a sodium dichromate
oxidation of 2-butanol to 2-butanone
.
For reasons soon to be mentioned, I thought my attempt at this reaction had failed miserably, and so I decided to accept and forget. But a few days later I examined my experimental data - where I expected an alcohol proton in my NMR, there was none to be found!
H: The Experiment
The oxidation was performed via fractional distillation, with 2-butanone expected to distill in the 77-85°C range. I used 2.5mL butanol as my limiting reagent, and anticipated a modest ~2mL crude distillate.
I collected less than 0.5mL. Perhaps my setup was just very low-yielding? I ran an IR and submitted an NMR sample. The IR suggested the collected "product" was almost entirely 2-butanol. I decided to run the experiment again, double-checking each step of the procedure.
Again, less than 0.5mL product was collected.  Gas was evolving from my apparatus! Turns out, the distillation head had a small hole at the bottom of its thermometer jointLikely from someone attaching a thermometer too violently.. This explained the lack of product, but this time I could not run the reaction again - less than an hour remained in the lab period, there were no spare distillation heads, and I would still have to purify my product and collecting a melting point.
I cleaned up and headed out, forgetting to retract my bad NMR sample.
He: The Data
A couple days later, I received the NMR spectra for my submitted product, which I knew was actually 2-butanol. And that's when it got interesting.
Do you see it? Almost every hydrogen is accounted for - the signals at `2.14`ppm and `1.06`ppm correspond to the molecule's methyl groups, at `1.70`ppm is the hydrogen adjacent the alcoholThis proton should actually be further downfield.
, and at `2.45`ppm are the protons of the third carbonThe minute rise at
7.27
is the solvent, CDCl3. -
but the alcohol proton is missing!
So where did it go? An argument could be made that the NMR sample was
impure, producing a bad spectra. While this may be true, even in a poor
spectrum one can expect the protons of the sample to have at least a small
presence
For completeness, here is a spectrum of pure 2-butanol..
In fact, there is a far more interesting solution this puzzle.
Li: Hydrogen Bonding and -OH Shifts
The alcohol group of 2-butanol is susceptible to hydrogen bonding, but hydrogen bonds are dynamic and constantly changing. This produces a fairly large variability in electron density around the alcohol proton, leading to a broad range of potential chemical shiftsThis type of behavior extends to most functional groups exhibiting hydrogen or other dynamic bonding, including amines. (roughly between 2 and 5 ppm).
It may seems crazy, but this suggests that the alcohol proton is really just
hidden amongst the doublet or triplet at 2.45
ppm and 2.14
ppm, respectively.
At least, that's my best theoryIf you know of a better
one, please let me know.,
and it blows my mind.


Market Update
Bitcoin Price ‘Ranging’ Continues as Trader Says $8,400 Could Be Next
by William SubergBitcoin (BTC) was struggling to recapture $8,800 on Nov. 12 after selling pressure sent the largest cryptocurrency broadly lower late on Monday.
Cryptocurrency market daily overview. Source: Coin360
Bitcoin fails to gain momentum to break $9K
Data from Coin360 showed BTC/USD trading between $8,700 and $8,800 on Tuesday morning, having fallen to local lows of $8,630 in the past 24 hours.
Despite volatility waning in recent days, Bitcoin remained broadly lower as the week began, losing support at $9,000.
Bitcoin seven-day price chart. Source: Coin360
Having exited its previous trading range, Bitcoin was now laying the foundations for further downside, analysts said.
In his most recent Twitter update, regular Cointelegraph contributor Michaël van de Poppe eyed the potential for BTC/USD to fall to around $8,400.
“Could continue the ranging here. For bulls; break back above $9,050 required,” he summarized.
Van de Poppe added that current price behavior signified a tussle between bull and bear forces over Bitcoin, something which nonetheless was not repeating on altcoin markets.
Despite the current slide, Bitcoin remains higher than many models forecast for this year. As Cointelegraph reported, the most accurate models call for an average BTC/USD price of $8,300 until next May’s block reward halving event.
Altcoins diverge from sluggish BTC
Altcoins indeed showed mixed behavior over the past 24 hours. While Bitcoin stayed mostly static, shifts of up to 5% characterized the top twenty cryptocurrencies by market cap.
Ether (ETH), the largest altcoin, nonetheless copied Bitcoin’s behavior, shedding just 0.25% to remain at $186.
Ether seven-day price chart. Source: Coin360
The leader was NEO (NEO), however, which rose almost 6% and extended a Chinese token run which began in late October.
The overall cryptocurrency market cap was $240.3 billion at press time, with Bitcoin’s share making up 66% of the total.
Keep track of top crypto markets in real time here


Telenor offloads last Veon chunk
by Yanitsa BoyadzhievaTelenor Group sold its last stake in multinational operator Veon for $362 million, after a prolonged contest about the future ownership of the company, reported Reuters.
The transaction compiled of Telenor’s remaining 157 million shares in Veon and “is in line with the previously communicated strategy and concludes the final sell-down in Veon”, the company said, adding its counterpart for the sale was US-based investment banking company Citi.
Telenor first announced plans to offload its stake in Veon (formerly VimpelCom) in 2015, as it couldn’t get hold of a majority to fully control the operator.
In July the group hailed its results for Q2 2019 as validation for a strategy commenced in 2017 to offload non-core assets.


SAML integration with Keycloak
Edit (pull request)Alien4Cloud supports SAML authentication, this section describes the configuration for enabling SAML and how to integrate Keycloak.
How does it work
Alien4Cloud serves as a SP (Service Provider) and the authentication of users could be delegated to an existing IdP (Identity Provider). Keycloak is an open-source tool managing the SP and IdP and providing the SSO (Single-Sign-On). The diagram below shows a high-level intuition of how A4C interacts with Keycloak when an user tries to login A4C.
How to configure
Prerequistes
Before configuring the IdP in A4C, just for testing, please create a new realm firstly and then create a new IdP as well as some users in this realm.
To remind that Alien4Cloud serves as SP and Keycloak as IdP. So firstly, for A4C, we need to set up the configuration file of IdP which has been created in Keycloak. And then, we go to configure A4C to generate a configuration file of SP and then download it from http(s)://alien4cloud.host:alien4cloud.port/saml/metadata and import it on Keycloak.
Here is the diagram showing the configuration process:
SAML configuration can be done by updating the A4C configuration file located in config/alien4cloud-config.yml.
Here is an example:
saml:
enabled: true
maxAuthenticationAge: 7200
maxAssertionTime: 3000
logoutUrl: http://alien4cloud.org
ssl:
keystore: samlKeystore.jks
defaultKey: apollo
keystorepassword: nalle123
metadata:
idp:
url: "https://idp.ssocircle.com/idp-meta.xml"
sp:
entityId: "org:alien4cloud:sp"
Table: definition of parameters
| Parameters | Description | Mandatory | Default value | Example |
|---|---|---|---|---|
| saml:enabled | Flag for enabling SAML | yes | true | |
| saml:maxAuthenticationAge | Maximum life of an authentication in seconds | yes | 7200 | |
| saml:maxAssertionTime | The duration fo validity of assertions processed during the signle sign-on process in seconds | yes | 3000 | |
| saml:logoutUrl | Allows to specify an url on which the user will be redirected after performing a logout from A4C when SAML is enabled | no | http://alien4cloud.org | |
| saml:proxy:host | TODO | no | <IP_ADDRESS> | |
| saml:proxy:port | TODO | no | 8080 | |
| saml:ssl:keystore | Certificate configured in Keycloak server | yes | samlKeystore.jks | |
| saml:ssl:defaultKey | Alias of keystore | yes | apollo | |
| saml:ssl:keystorepassword | Password of keystore | yes | nalle123 | |
| saml:ssl:metadata:idp:url | URL pointing to the IdP metadata file | yes if not setting file path to IdP metadata file; no otherwise | “https://idp.ssocircle.com/idp-meta.xml” | |
| saml:ssl:metadata:idp:file | Local file path to the IdP metadata file | yes if not setting URL to IdP metadata file; no otherwise | “/path/to/file.xml” | |
| saml:ssl:metadata:sp:entityId | Unique identifier of the service provider. | yes | “org:alien4cloud:sp” | |
| saml:ssl:metadata:sp:entityBaseURL | Base URL to construct SAML endpoints from, needs to be a URL with protocol, server, port and context path. | no | “http://localhost:8088” | |
| saml:ssl:metadata:sp:requestSigned | Flag indicating whether this service signs authentication requests. | no | false | |
| saml:ssl:mapping:email | Mapping of user email inside SAML attribute assertion | no | false | |
| saml:ssl:mapping:firstname | Mapping of user firstname inside SAML attribute assertion | no | false | |
| saml:ssl:mapping:lastname | Mapping of user lastname inside SAML attribute assertion | no | false |
How to download SP metadata file
Once alien4cloud is started you can retrieve A4C’s SP metadata from http(s)://alien4cloud.host:alien4cloud.port/saml/metadata.


DetNet
Notifications
Search
Account
Logout
FirstName LastName
Home
Events
3
Members
Plugins
Admin
Groups
Group A
Group B
Group C


--- layout: post title: M's First Soccer Practice categories: [] tags: - emma - family - soccer - sports status: publish type: post published: true meta: _original_post_id: '' _original_import_author: John Duprey author: ---
Emma had her first soccer practice today. It was a beautiful morning for it - well into the 70's by the time we left, I believe. I was happy to see how friendly the coaches were and patient with the children. I think this will be a good first introduction to soccer. More importantly, it will keep her active and healthy. Its great for my health too. With the exception of having to be on the field by 8:30, I get a lot of enjoyment out of watching the kids.


| 1 | package fr.sii.ogham.core.builder.retry; | |
| 2 | ||
| 3 | import java.util.Arrays; | |
| 4 | import java.util.List; | |
| 5 | ||
| 6 | import fr.sii.ogham.core.builder.Builder; | |
| 7 | import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilder; | |
| 8 | import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilderHelper; | |
| 9 | import fr.sii.ogham.core.builder.configurer.Configurer; | |
| 10 | import fr.sii.ogham.core.builder.context.BuildContext; | |
| 11 | import fr.sii.ogham.core.builder.env.EnvironmentBuilder; | |
| 12 | import fr.sii.ogham.core.fluent.AbstractParent; | |
| 13 | import fr.sii.ogham.core.retry.PerExecutionDelayRetry; | |
| 14 | import fr.sii.ogham.core.retry.RetryStrategy; | |
| 15 | ||
| 16 | /** | |
| 17 | * Configures retry handling based on a specific delay for each execution. | |
| 18 | * | |
| 19 | * Retry several times with a fixed delay to wait after the last execution | |
| 20 | * failure until the maximum attempts is reached. A specific delay is used for | |
| 21 | * each execution. If there are more attempts than the configured delays, the | |
| 22 | * last delays is used for remaining attempts. | |
| 23 | * | |
| 24 | * | |
| 25 | * For example: | |
| 26 | * | |
| 27 | *
 | |
| 28 | * .delays(500, 750, 1800) | |
| 29 | * .maxRetries(5) | |
| 30 | *
 | |
| 31 | * | |
| 32 | * Means that a retry will be attempted with specified delays until 5 attempts | |
| 33 | * are reached (inclusive). For example, you want to connect to an external | |
| 34 | * system at t1=0 and the connection timeout (100ms) is triggered at t2=100ms. | |
| 35 | * Using this retry will provide the following behavior: | |
| 36 | * | |
| 37 | *
 | |
| 38 | *
0: connect
 | |
| 39 | *
100: timeout
 | |
| 40 | *
600: connect
 | |
| 41 | *
700: timeout
 | |
| 42 | *
1450: connect
 | |
| 43 | *
1550: timeout
 | |
| 44 | *
3350: connect
 | |
| 45 | *
3450: timeout
 | |
| 46 | *
5250: connect
 | |
| 47 | *
5350: timeout
 | |
| 48 | *
7150: connect
 | |
| 49 | *
7250: timeout
 | |
| 50 | *
fail
 | |
| 51 | *
 | |
| 52 | * | |
| 53 | * @author Aurélien Baudet | |
| 54 | * | |
| 55 | * @param
 | |
| 56 | * the type of the parent builder (when calling {@link #and()} | |
| 57 | * method) | |
| 58 | */ | |
| 59 | public class PerExecutionDelayBuilder
 extends AbstractParent
 implements Builder<RetryStrategy> { | |
| 60 | private final BuildContext buildContext; | |
| 61 | private final ConfigurationValueBuilderHelper<PerExecutionDelayBuilder
, Integer> maxRetriesValueBuilder; | |
| 62 | private final ConfigurationValueBuilderHelper<PerExecutionDelayBuilder
, Long[]> delaysValueBuilder; | |
| 63 | ||
| 64 | /** | |
| 65 | * Initializes the builder with a parent builder. The parent builder is used | |
| 66 | * when calling {@link #and()} method. The {@link EnvironmentBuilder} is | |
| 67 | * used to evaluate properties when {@link #build()} method is called. | |
| 68 | * | |
| 69 | * @param parent | |
| 70 | * the parent builder | |
| 71 | * @param buildContext | |
| 72 | * for registering instances and property evaluation | |
| 73 | */ | |
| 74 | public PerExecutionDelayBuilder(P parent, BuildContext buildContext) { | |
| 75 | super(parent); | |
| 76 | this.buildContext = buildContext; | |
| 77 | maxRetriesValueBuilder = buildContext.newConfigurationValueBuilder(this, Integer.class); | |
| 78 | delaysValueBuilder = buildContext.newConfigurationValueBuilder(this, Long[].class); | |
| 79 | } | |
| 80 | ||
| 81 | /** | |
| 82 | * Set the maximum number of attempts. | |
| 83 | * | |
| 84 | *
 | |
| 85 | * The value set using this method takes precedence over any property and | |
| 86 | * default value configured using {@link #maxRetries()}. | |
| 87 | * | |
| 88 | *
 | |
| 89 | * .maxRetries(10) | |
| 90 | * .maxRetries() | |
| 91 | * .properties("${custom.property.high-priority}", "${custom.property.low-priority}") | |
| 92 | * .defaultValue(5) | |
| 93 | *
 | |
| 94 | * | |
| 95 | *
 | |
| 96 | * .maxRetries(10) | |
| 97 | * .maxRetries() | |
| 98 | * .properties("${custom.property.high-priority}", "${custom.property.low-priority}") | |
| 99 | * .defaultValue(5) | |
| 100 | *
 | |
| 101 | * | |
| 102 | * In both cases, {@code maxRetries(10)} is used. | |
| 103 | * | |
| 104 | *
 | |
| 105 | * If this method is called several times, only the last value is used. | |
| 106 | * | |
| 107 | *
 | |
| 108 | * If {@code null} value is set, it is like not setting a value at all. The | |
| 109 | * property/default value configuration is applied. | |
| 110 | * | |
| 111 | * @param maxRetries | |
| 112 | * the maximum number of attempts | |
| 113 | * @return this instance for fluent chaining | |
| 114 | */ | |
| 115 | public PerExecutionDelayBuilder
 maxRetries(Integer maxRetries) { | |
| 116 |
1
1. maxRetries : removed call to fr/sii/ogham/core/builder/configuration/ConfigurationValueBuilderHelper::setValue → RUN_ERROR |
this.maxRetriesValueBuilder.setValue(maxRetries); |
| 117 |
1
1. maxRetries : replaced return value with null for fr/sii/ogham/core/builder/retry/PerExecutionDelayBuilder::maxRetries → RUN_ERROR |
return this; |
| 118 | } | |
| 119 | ||
| 120 | /** | |
| 121 | * Set the maximum number of attempts. | |
| 122 | * | |
| 123 | *
 | |
| 124 | * This method is mainly used by {@link Configurer}s to register some | |
| 125 | * property keys and/or a default value. The aim is to let developer be able | |
| 126 | * to externalize its configuration (using system properties, configuration | |
| 127 | * file or anything else). If the developer doesn't configure any value for | |
| 128 | * the registered properties, the default value is used (if set). | |
| 129 | * | |
| 130 | *
 | |
| 131 | * .maxRetries() | |
| 132 | * .properties("${custom.property.high-priority}", "${custom.property.low-priority}") | |
| 133 | * .defaultValue(5) | |
| 134 | *
 | |
| 135 | * | |
| 136 | *
 | |
| 137 | * Non-null value set using {@link #maxRetries(Integer)} takes precedence | |
| 138 | * over property values and default value. | |
| 139 | * | |
| 140 | *
 | |
| 141 | * .maxRetries(10) | |
| 142 | * .maxRetries() | |
| 143 | * .properties("${custom.property.high-priority}", "${custom.property.low-priority}") | |
| 144 | * .defaultValue(5) | |
| 145 | *
 | |
| 146 | * | |
| 147 | * The value {@code 10} is used regardless of the value of the properties | |
| 148 | * and default value. | |
| 149 | * | |
| 150 | *
 | |
| 151 | * See {@link ConfigurationValueBuilder} for more information. | |
| 152 | * | |
| 153 | * | |
| 154 | * @return the builder to configure property keys/default value | |
| 155 | */ | |
| 156 | public ConfigurationValueBuilder<PerExecutionDelayBuilder
, Integer> maxRetries() { | |
| 157 |
1
1. maxRetries : replaced return value with null for fr/sii/ogham/core/builder/retry/PerExecutionDelayBuilder::maxRetries → RUN_ERROR |
return maxRetriesValueBuilder; |
| 158 | } | |
| 159 | ||
| 160 | /** | |
| 161 | * Set specific delays (in milliseconds) used for each execution. If there | |
| 162 | * are more attempts than the configured delays, the last delays is used for | |
| 163 | * remaining attempts. | |
| 164 | * | |
| 165 | *
 | |
| 166 | * The value set using this method takes precedence over any property and | |
| 167 | * default value configured using {@link #delays()}. | |
| 168 | * | |
| 169 | *
 | |
| 170 | * .delays(5000L) | |
| 171 | * .delays() | |
| 172 | * .properties("${custom.property.high-priority}", "${custom.property.low-priority}") | |
| 173 | * .defaultValue(10000L) | |
| 174 | *
 | |
| 175 | * | |
| 176 | *
 | |
| 177 | * .delays(5000L) | |
| 178 | * .delays() | |
| 179 | * .properties("${custom.property.high-priority}", "${custom.property.low-priority}") | |
| 180 | * .defaultValue(10000L) | |
| 181 | *
 | |
| 182 | * | |
| 183 | * In both cases, {@code delays(5000L)} is used. | |
| 184 | * | |
| 185 | *
 | |
| 186 | * If this method is called several times, only the last value is used. | |
| 187 | * | |
| 188 | *
 | |
| 189 | * If {@code null} value is set, it is like not setting a value at all. The | |
| 190 | * property/default value configuration is applied. | |
| 191 | * | |
| 192 | * @param delays | |
| 193 | * the delays for each execution | |
| 194 | * @return this instance for fluent chaining | |
| 195 | */ | |
| 196 | public PerExecutionDelayBuilder
 delays(List<Long> delays) { | |
| 197 |
2
1. delays : removed call to fr/sii/ogham/core/builder/configuration/ConfigurationValueBuilderHelper::setValue → RUN_ERROR 2. delays : negated conditional → RUN_ERROR |
delaysValueBuilder.setValue(delays == null ? null : delays.toArray(new Long[delays.size()])); |
| 198 |
1
1. delays : replaced return value with null for fr/sii/ogham/core/builder/retry/PerExecutionDelayBuilder::delays → RUN_ERROR |
return this; |
| 199 | } | |
| 200 | ||
| 201 | /** | |
| 202 | * Set specific delays (in milliseconds) used for each execution. If there | |
| 203 | * are more attempts than the configured delays, the last delays is used for | |
| 204 | * remaining attempts. | |
| 205 | * | |
| 206 | *
 | |
| 207 | * The value set using this method takes precedence over any property and | |
| 208 | * default value configured using {@link #delays()}. | |
| 209 | * | |
| 210 | *
 | |
| 211 | * .delays(5000L) | |
| 212 | * .delays() | |
| 213 | * .properties("${custom.property.high-priority}", "${custom.property.low-priority}") | |
| 214 | * .defaultValue(10000L) | |
| 215 | *
 | |
| 216 | * | |
| 217 | *
 | |
| 218 | * .delays(5000L) | |
| 219 | * .delays() | |
| 220 | * .properties("${custom.property.high-priority}", "${custom.property.low-priority}") | |
| 221 | * .defaultValue(10000L) | |
| 222 | *
 | |
| 223 | * | |
| 224 | * In both cases, {@code delays(5000L)} is used. | |
| 225 | * | |
| 226 | *
 | |
| 227 | * If this method is called several times, only the last value is used. | |
| 228 | * | |
| 229 | *
 | |
| 230 | * If {@code null} value is set, it is like not setting a value at all. The | |
| 231 | * property/default value configuration is applied. | |
| 232 | * | |
| 233 | * @param delays | |
| 234 | * the delays for each execution | |
| 235 | * @return this instance for fluent chaining | |
| 236 | */ | |
| 237 | public PerExecutionDelayBuilder
 delays(Long... delays) { | |
| 238 |
1
1. delays : removed call to fr/sii/ogham/core/builder/configuration/ConfigurationValueBuilderHelper::setValue → NO_COVERAGE |
delaysValueBuilder.setValue(delays); |
| 239 |
1
1. delays : replaced return value with null for fr/sii/ogham/core/builder/retry/PerExecutionDelayBuilder::delays → NO_COVERAGE |
return this; |
| 240 | } | |
| 241 | ||
| 242 | /** | |
| 243 | * Set specific delays (in milliseconds) used for each new execution. If | |
| 244 | * there are more attempts than the configured delays, the last delay is | |
| 245 | * used for remaining attempts. | |
| 246 | * | |
| 247 | *
 | |
| 248 | * This method is mainly used by {@link Configurer}s to register some | |
| 249 | * property keys and/or a default value. The aim is to let developer be able | |
| 250 | * to externalize its configuration (using system properties, configuration | |
| 251 | * file or anything else). If the developer doesn't configure any value for | |
| 252 | * the registered properties, the default value is used (if set). | |
| 253 | * | |
| 254 | *
 | |
| 255 | * .delays() | |
| 256 | * .properties("${custom.property.high-priority}", "${custom.property.low-priority}") | |
| 257 | * .defaultValue(10000L) | |
| 258 | *
 | |
| 259 | * | |
| 260 | *
 | |
| 261 | * Non-null value set using {@link #delays(Long...)} takes precedence over | |
| 262 | * property values and default value. | |
| 263 | * | |
| 264 | *
 | |
| 265 | * .delays(5000L) | |
| 266 | * .delays() | |
| 267 | * .properties("${custom.property.high-priority}", "${custom.property.low-priority}") | |
| 268 | * .defaultValue(10000L) | |
| 269 | *
 | |
| 270 | * | |
| 271 | * The value {@code 5000L} is used regardless of the value of the properties | |
| 272 | * and default value. | |
| 273 | * | |
| 274 | *
 | |
| 275 | * See {@link ConfigurationValueBuilder} for more information. | |
| 276 | * | |
| 277 | * | |
| 278 | * @return the builder to configure property keys/default value | |
| 279 | */ | |
| 280 | public ConfigurationValueBuilder<PerExecutionDelayBuilder
, Long[]> delays() { | |
| 281 |
1
1. delays : replaced return value with null for fr/sii/ogham/core/builder/retry/PerExecutionDelayBuilder::delays → RUN_ERROR |
return delaysValueBuilder; |
| 282 | } | |
| 283 | ||
| 284 | @Override | |
| 285 | public RetryStrategy build() { | |
| 286 | int evaluatedMaxRetries = buildMaxRetries(); | |
| 287 | List<Long> evaluatedDelays = buildDelays(); | |
| 288 |
2
1. build : negated conditional → RUN_ERROR 2. build : negated conditional → RUN_ERROR |
if (evaluatedMaxRetries == 0 || evaluatedDelays.isEmpty()) { |
| 289 | return null; | |
| 290 | } | |
| 291 |
1
1. build : replaced return value with null for fr/sii/ogham/core/builder/retry/PerExecutionDelayBuilder::build → RUN_ERROR |
return buildContext.register(new PerExecutionDelayRetry(evaluatedMaxRetries, evaluatedDelays)); |
| 292 | } | |
| 293 | ||
| 294 | private int buildMaxRetries() { | |
| 295 |
1
1. buildMaxRetries : replaced int return with 0 for fr/sii/ogham/core/builder/retry/PerExecutionDelayBuilder::buildMaxRetries → RUN_ERROR |
return maxRetriesValueBuilder.getValue(0); |
| 296 | } | |
| 297 | ||
| 298 | private List<Long> buildDelays() { | |
| 299 | Long[] delays = delaysValueBuilder.getValue(new Long[0]); | |
| 300 |
1
1. buildDelays : replaced return value with Collections.emptyList for fr/sii/ogham/core/builder/retry/PerExecutionDelayBuilder::buildDelays → RUN_ERROR |
return Arrays.asList(delays); |
| 301 | } | |
| 302 | } | |
Mutations | ||
| 116 |
1.1 |
|
| 117 |
1.1 |
|
| 157 |
1.1 |
|
| 197 |
1.1 2.2 |
|
| 198 |
1.1 |
|
| 238 |
1.1 |
|
| 239 |
1.1 |
|
| 281 |
1.1 |
|
| 288 |
1.1 2.2 |
|
| 291 |
1.1 |
|
| 295 |
1.1 |
|
| 300 |
1.1 |


Studied at the LSE over the summer of 2015 in a specialized course on Game Theory and Competitive Strategy
Final Grade: A+
Web Development: HTML5, CSS, JavaScript[+ JQuery, D3]
Programming: Python, Java, Ruby, Xamarin, C++, Racket, ACL2, Assembly
Data: Microsoft Excel, VBA, SQL, MongoDB, Tableau, R
Certifications: Google Analytics, Programmatic 101


Yellow weather warnings issued ahead of wet weekend
The Met Office has issued four weather warnings across the UK for Saturday, including parts of north-east England where heavy flooding has caused significant disruption in previous weeks.
Heavy and persistent rain is due to start in south-west England from 12pm on Friday and last until 6pm on Saturday – meaning a deluge for roughly 30 hours.
About 20-30mm of rain is expected widely in the region, though the up-slopes of Dartmoor in south Devon could see 40mm-50mm of rain.
The fresh warnings of heavy rain come just over a week after much of the Midlands and Yorkshire was deluged forcing hundreds to abandon their homes.
Sorry, this content isn't available on your device.
The average rainfall for Devon and Cornwall during November is roughly 35mm-45mm.
Yellow warnings have also been issued in north-eastern England and parts of Scotland, which will also be hit with continuing wet weather throughout Saturday.
A spokesman for the Met Office added that areas such as Derbyshire and Nottinghamshire will see persistent rain arriving from the south early on Saturday morning, which will last through most of the day.
According to the Met Office, rain will be heavy at times, especially on hills where the rainfall could also reach up to 40mm-50mm.
The region is still recovering from heavy flooding that caused devastation in many communities in the last two weeks.
Met Office data up to November 17 shows that Nottinghamshire has seen the most rain of all UK counties compared with the average, having seen 189% of its monthly average already.
There are currently 45 flood alerts and six flood warnings in place that have been issued by the Environment Agency.
Four of the flood warnings are in the North East.
Wet weather is also expected to develop across north-eastern Scotland from Saturday afternoon.
The Met said that general rainfall of 20mm-40mm was likely through Aberdeenshire, and areas of high ground could reach 60-80 mm in addition to thawing snow.


| Movie Title | Genre | Year | Gross |
|---|---|---|---|
| Star Wars | Adventure, Sci-fi | 1977 | $460,935,665 |
| Howard The Duck | "Comedy" | 1986 | $16,295,774 |
| American Graffiti | Comedy, Drama | 1973 | $115,000,000 |
← Drag window (in editor or full page view) to see the effect. →
| Movie Title | Genre | Year | Gross |
|---|---|---|---|
| Star Wars | Adventure, Sci-fi | 1977 | $460,935,665 |
| Howard The Duck | "Comedy" | 1986 | $16,295,774 |
| American Graffiti | Comedy, Drama | 1973 | $115,000,000 |
← Drag window (in editor or full page view) to see the effect. →


@(user: models.User)(form: play.data.Form[views.forms.SchoolForm])(id: Long) @* Display a form, that allows editing a school SchoolForm with a single field - name of the school *@ @import bootstrap3._ @stylesheets = { } @javascripts = { } @main("Admin Dashboard: School Management - Careers From Here")(stylesheets)(javascripts)(user) {