Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
302d64a
update README
Jul 20, 2021
cc48dc6
increment version
Jul 20, 2021
537bb5c
adds pypi publish script
Jul 20, 2021
a6fedf7
moves publish
Jul 20, 2021
295b4e7
switch between stages
Jul 28, 2021
86f1eac
solves localhost bug
Jul 28, 2021
2497c03
postgres change
Aug 10, 2021
9eccb74
change to graphql and add autolf v1
Oct 3, 2021
e0932df
update setup
Oct 3, 2021
5f10667
Update README.md
jhoetter Oct 3, 2021
e091fbc
graphql to rest
Oct 5, 2021
120b476
add pandas to setup
Oct 5, 2021
fee7aba
adds autoexecution
Oct 5, 2021
2d89a2e
adds checks for existing lfs
Oct 5, 2021
9b93bbc
0.1.10
Oct 5, 2021
9da9b37
improves auto_lf
Oct 6, 2021
9ac941f
improves auto_lf
Oct 6, 2021
3e7b789
bugfix in auto_lf regex with unmatched parenthesis
Oct 7, 2021
624f693
address rest-api
Oct 13, 2021
91f8383
version 0.1.13
Oct 13, 2021
4244526
adds embedding generatioN
Nov 1, 2021
ab674bf
closed beta version
Nov 16, 2021
512929a
correct classifier
Nov 16, 2021
31aa7d1
adds documentation
Nov 17, 2021
a032ac0
Update README.md
jhoetter Nov 17, 2021
68e310c
Update README.md
jhoetter Nov 17, 2021
6932293
Update README.md
jhoetter Nov 17, 2021
76aea51
Update README.md
jhoetter Nov 17, 2021
973771c
bugfix np.union1d -> Union
Nov 22, 2021
a5cf486
Merge branch 'master' of github.com:onetask-ai/onetask-python
Nov 22, 2021
a816015
update version
Nov 22, 2021
6bb83fb
solves embedding identifier bug
Nov 26, 2021
07892a6
adds is_programmatic enrichment to fetching record data
Dec 3, 2021
f0ab02a
minor bugfix generate_embeddings
Dec 8, 2021
ea810a3
bugfix for empty records at embedding creation
Dec 8, 2021
2b28f48
update to kern
May 7, 2022
2d58982
Merge pull request #4 from code-kern-ai/kern-api
May 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.vscode/

# Jupyter
*.ipynb

Expand Down
74 changes: 54 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,66 @@
[![Python 3.8](https://img.shields.io/badge/python-3.8-blue.svg)](https://www.python.org/downloads/release/python-380/)
![kern-python](https://uploads-ssl.webflow.com/61e47fafb12bd56b40022a49/62766400bd3c57b579d289bf_kern-python%20Banner.png)
[![Python 3.9](https://img.shields.io/badge/python-3.9-blue.svg)](https://www.python.org/downloads/release/python-390/)

# onetask API for Python - WiP
# Kern AI API for Python

Official Python SDK for onetask.
This is the official Python SDK for Kern AI, your IDE for programmatic data enrichment and management.

## [](https://github.com/onetask-ai/onetask-python#installation)Installation
## Installation

You can set up this library via either running `$ pip install kern-python-client`, or via cloning this repository and running `$ pip install -r requirements.txt` in your repository.

You can clone the repository and run the setup.py script:
## Usage
Once you installed the package, you can access the application from any Python terminal as follows:

`$ python setup.py install`

## [](https://github.com/onetask-ai/onetask-python#usage)Usage
```python
from kern import Client

Before making requests to the API, you need to create an instance of the onetask client. At the moment, you will have to use the org id and the project id:
username = "your-username"
password = "your-password"
project_id = "your-project-id" # can be found in the URL of the web application

```python
from onetask import Client
# Instantiate the client using your org_id and project_id
org_id = '<YOUR ORG ID HERE>'
project_id = '<YOUR PROJECT ID HERE>'
client = Client(org_id=org_id, project_id=project_id)
client = Client(username, password, project_id)
# if you run the application locally, please the following instead:
# client = Client(username, password, project_id, uri="http://localhost:4455")
```

You can now register your custom Python function
Now, you can easily fetch the data from your project:
```python
def my_first_lf(record):
if "you" in record["headline"].lower():
return "Clickbait"
client.register_lf(my_first_lf)
df = client.fetch_export()
```

The `df` contains data of the following scheme:
- all your record attributes are stored as columns, e.g. `headline` or `running_id` if you uploaded records like `{"headline": "some text", "running_id": 1234}`
- per labeling task three columns:
- `<attribute_name|None>__<labeling_task_name>__MANUAL`: those are the manually set labels of your records
- `<attribute_name|None>__<labeling_task_name>__WEAK SUPERVISION`: those are the weakly supervised labels of your records
- `<attribute_name|None>__<labeling_task_name>__WEAK SUPERVISION_confidence`: those are the probabilities or your weakly supervised labels

With the `client`, you easily integrate your data into any kind of system; may it be a custom implementation, an AutoML system or a plain data analytics framework 🚀

## Roadmap
- [ ] Register information sources via wrappers
- [ ] Fetch project statistics


If you want to have something added, feel free to open an [issue](https://github.com/code-kern-ai/kern-python/issues).

## Contributing
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement".
Don't forget to give the project a star! Thanks again!

1. Fork the Project
2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the Branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request

And please don't forget to leave a ⭐ if you like the work!

## License
Distributed under the MIT License. See LICENSE.txt for more information.

## Contact
This library is developed and maintained by [kern.ai](https://github.com/code-kern-ai). If you want to provide us with feedback or have some questions, don't hesitate to contact us. We're super happy to help ✌️
48 changes: 48 additions & 0 deletions kern/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-

from wasabi import msg
import pandas as pd
from kern import authentication, api_calls, settings, exceptions
from typing import Optional


class Client:
"""Client object which can be used to directly address the Kern AI API.

Args:
user_name (str): Your username for the application.
password (str): The respective password. Do not share this!
project_id (str): The link to your project. This can be found in the URL in an active project.
uri (str, optional): Link to the host of the application. Defaults to "https://app.kern.ai".

Raises:
exceptions.get_api_exception_class: If your credentials are incorrect, an exception is raised.
"""

def __init__(
self, user_name: str, password: str, project_id: str, uri="https://app.kern.ai"
):
settings.set_base_uri(uri)
self.session_token = authentication.create_session_token(
user_name=user_name, password=password
)
if self.session_token is not None:
msg.good("Logged in to system.")
else:
msg.fail(f"Could not log in at {uri}. Please check username and password.")
raise exceptions.get_api_exception_class(401)
self.project_id = project_id

def fetch_export(self, num_samples: Optional[int] = None) -> pd.DataFrame:
"""Collects the export data of your project (i.e. the same data if you would export in the web app).

Args:
num_samples (Optional[int], optional): If set, only the first `num_samples` records are collected. Defaults to None.

Returns:
pd.DataFrame: DataFrame containing your record data. For more details, see https://docs.kern.ai
"""
url = settings.get_export_url(self.project_id, num_samples=num_samples)
api_response = api_calls.get_request(url, self.session_token)
df = pd.read_json(api_response)
return df
53 changes: 53 additions & 0 deletions kern/api_calls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
from json.decoder import JSONDecodeError
import pkg_resources
from kern import exceptions
import requests
from typing import Any, Dict

try:
version = pkg_resources.get_distribution("kern-python-client").version
except pkg_resources.DistributionNotFound:
version = "noversion"


def post_request(url: str, body: Dict[str, Any], session_token: str) -> str:
headers = _build_headers(session_token)
response = requests.post(url=url, json=body, headers=headers)
return _handle_response(response)


def get_request(url: str, session_token: str) -> str:
headers = _build_headers(session_token)
response = requests.get(url=url, headers=headers)
return _handle_response(response)


def _build_headers(session_token: str) -> Dict[str, str]:
return {
"Content-Type": "application/json",
"User-Agent": f"python-sdk-{version}",
"Authorization": f"Bearer {session_token}",
}


def _handle_response(response: requests.Response) -> str:
status_code = response.status_code
if status_code == 200:
json_data = response.json()
return json_data
else:
try:
json_data = response.json()
error_code = json_data.get("error_code")
error_message = json_data.get("error_message")
except JSONDecodeError:
error_code = 500
error_message = "The server was unable to process the provided data."

exception = exceptions.get_api_exception_class(
status_code=status_code,
error_code=error_code,
error_message=error_message,
)
raise exception
27 changes: 27 additions & 0 deletions kern/authentication.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
from kern import settings
import requests


def create_session_token(user_name: str, password: str) -> str:
headers = {"Accept": "application/json"}
action_url = (
requests.get(settings.get_authentication_url(), headers=headers)
.json()
.get("ui")
.get("action")
)
session_token = (
requests.post(
action_url,
headers=headers,
json={
"method": "password",
"password": password,
"password_identifier": user_name,
},
)
.json()
.get("session_token")
)
return session_token
34 changes: 15 additions & 19 deletions onetask/exceptions.py → kern/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,48 +1,44 @@
# -*- coding: utf-8 -*-
from typing import Optional


class ClientError(Exception):
def __init__(self, message: Optional[str] = None):
if message is None:
message = "Please check the documentation."
super().__init__(message)


class ParameterError(ClientError):
pass


# https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#client_error_responses
class APIError(Exception):
class SDKError(Exception):
def __init__(self, message: Optional[str] = None):
if message is None:
message = "Please check the API reference."
message = (
"Please check the SDK documentation at https://docs.kern.ai/reference."
)
super().__init__(message)


# 401 Unauthorized
class UnauthorizedError(APIError):
class UnauthorizedError(SDKError):
pass


# 404 Not Found
class UnknownIDException(APIError):
class NotFoundError(SDKError):
pass


# 500 Server Error
class InternalServerError(SDKError):
pass


RESPONSE_CODES_API_EXCEPTION_MAP = {
401: UnauthorizedError,
404: UnknownIDException,
404: NotFoundError,
500: InternalServerError,
}


def get_api_exception_class(
status_code: int,
error_code: Optional[str] = None,
error_message: Optional[str] = None,
) -> APIError:
exception_or_dict = RESPONSE_CODES_API_EXCEPTION_MAP.get(status_code, APIError)
) -> SDKError:
exception_or_dict = RESPONSE_CODES_API_EXCEPTION_MAP.get(status_code, SDKError)
if isinstance(exception_or_dict, dict):
exception_class = exception_or_dict.get(error_code, exception_or_dict["*"])
else:
Expand Down
29 changes: 29 additions & 0 deletions kern/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
BASE_URI: str


def set_base_uri(uri: str):
global BASE_URI
BASE_URI = uri


def add_query_params(url: str, **kwargs) -> str:
set_question_mark = False
for key, value in kwargs.items():
if value is not None:
if not set_question_mark:
url = f"{url}?{key}={value}"
set_question_mark = True
else:
url = f"{url}&{key}={value}"
return url


def get_authentication_url() -> str:
return f"{BASE_URI}/.ory/kratos/public/self-service/login/api"


def get_export_url(project_id: str, **kwargs) -> str:
url = f"{BASE_URI}/api/project/{project_id}/export"
url = add_query_params(url, **kwargs)
return url
57 changes: 0 additions & 57 deletions onetask/__init__.py

This file was deleted.

Loading