Skip to content

Commit

Permalink
updated the pysysrev package
Browse files Browse the repository at this point in the history
  • Loading branch information
tomlue committed Apr 15, 2024
1 parent 2461b17 commit 50e2755
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 87 deletions.
31 changes: 31 additions & 0 deletions .github/workflows/pypi_publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Publish Python package to PyPI

on:
push:
branches:
- master # Set this to your default branch

jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9' # Use the Python version compatible with your project

- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install build
- name: Build package
run: python -m build

- name: Publish to PyPI
if: github.event_name == 'push' && startsWith(github.ref, 'refs/heads/master')
uses: pypa/[email protected]
with:
password: ${{ secrets.PYPI_TOKEN }}
user: sysrev
23 changes: 23 additions & 0 deletions .github/workflows/sysrev.yml.old
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Biobricks codecov

on: [push, pull_request]

env:
BIOBRICKS_TEST_TOKEN: ${{ secrets.BIOBRICKS_TEST_TOKEN }}

jobs:
build:
runs-on: ubuntu-latest
name: Test biobricks
steps:
- uses: actions/checkout@v2 # Updated to use v2
- uses: actions/setup-python@v2
with:
python-version: '3.10'
- name: Install requirements
run: pip install -r requirements.txt # Removed the working-directory override for this step
- name: Run tests and collect coverage
run: pytest --cov=./ --cov-report=xml
working-directory: ./ # Set the working directory only for the test step
- name: Upload coverage reports to Codecov with GitHub Action
uses: codecov/codecov-action@v3
2 changes: 0 additions & 2 deletions PySysrev/__init__.py

This file was deleted.

70 changes: 0 additions & 70 deletions PySysrev/funcs.py

This file was deleted.

13 changes: 4 additions & 9 deletions README
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
A Python client for sysrev.com

See Demo.ipynb for implementation of client on example project.

To install PySysrev, simply run `pip install PySysrev`

Then inside Python, run the following commands:
>>> import PySysrev
>>> df = PySysrev.getAnnotations(3144)
# Sysrev Python Client
The Sysrev package provides:
1. `SysrevClient` object for using the sysrev.com API
2. (soon) a method for synchronizing local sysrev projects with remote sysrev projects.
10 changes: 5 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from setuptools import setup

setup(name='PySysrev',
version='1.2.1',
setup(name='sysrev',
version='1.2.2',
description='Gets annotations from Sysrev API',
url='https://github.com/sysrev/PySysrev',
author='nole-lin',
author_email='nole@insilica.co',
packages=['PySysrev'],
author='Thomas Luechtefeld',
author_email='tom@insilica.co',
packages=['sysrev'],
install_requires=[
'pandas',
'requests',
Expand Down
1 change: 1 addition & 0 deletions sysrev/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .funcs import SysrevClient
103 changes: 103 additions & 0 deletions sysrev/funcs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import requests

class LabelTransformer:

def handle_boolean(self, label_value):
if isinstance(label_value, bool):
return label_value
elif str(label_value).lower() in ['yes', 'no']:
return str(label_value).lower() == 'yes'
else:
raise ValueError("Invalid boolean value")

def handle_categorical_or_string(self, label_value):
if isinstance(label_value, str):
return [label_value]
elif isinstance(label_value, list) and all(isinstance(item, str) for item in label_value):
return label_value
else:
raise ValueError("Invalid value for categorical or string type")

def transform_label(self, label_type, label_value):
if label_type == 'boolean':
return self.handle_boolean(label_value)
elif label_type in ['categorical', 'string']:
return self.handle_categorical_or_string(label_value)
else:
raise ValueError("Invalid label type")

class SysrevClient():

def __init__(self, api_key, base_url="https://www.sysrev.com"):
self.api_key = api_key
self.base_url = base_url

def get_project_info(self, project_id):
endpoint = f"{self.base_url}/api-json/project-info"
headers = {"Authorization": f"Bearer {self.api_key}"}
response = requests.get(endpoint, headers=headers, params={"project-id": project_id})
return response.json()

def set_labels(self, project_id, article_id, label_ids, label_values, label_types, confirm=False, change=False, resolve=False):
endpoint = f"{self.base_url}/api-json/set-labels"
headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}

assert len(label_ids) == len(label_values) == len(label_types), "Length of label_ids, label_values, and label_types should be the same."

# construct label_values_dict
tf = LabelTransformer()
label_values_dict = {label_ids[i]: tf.transform_label(label_types[i], label_values[i]) for i in range(len(label_ids))}

# Constructing the data payload as per the server's expectation
data = {"project-id": project_id, "article-id": article_id, "label-values": label_values_dict}
data.update({ "confirm?": confirm, "change?": change, "resolve?": resolve })

# Sending a POST request to the server
response = requests.post(endpoint, json=data, headers=headers)
return response.json()

def get_project_articles(self, project_id, offset=0, limit=10, sort_by=None, sort_dir=None):
endpoint = f"{self.base_url}/api-json/project-articles"
headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}
body = {"project-id": project_id, "n-offset": offset, "n-count": limit}

# Add optional sorting keys if provided
if sort_by: body["sort-by"] = sort_by
if sort_dir: body["sort-dir"] = sort_dir

# Make the POST request with the simplified body
response = requests.post(endpoint, headers=headers, json=body)
return response.json()

def fetch_all_articles(self, project_id, limit=10, sort_by=None, sort_dir=None):
offset = 0
while True:
result = self.get_project_articles(project_id, offset=offset, limit=limit, sort_by=sort_by, sort_dir=sort_dir)
articles = result.get('result', [])
if not articles:
break # Stop iteration if no articles are left
yield from articles # Yield each article in the current batch
offset += len(articles)

def get_article_info(self, project_id, article_id):
endpoint = f"{self.base_url}/api-json/article-info/{article_id}"
headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}
body = {"project-id": project_id,}
return requests.get(endpoint, headers=headers, json=body)

def upload_jsonlines(self, file_path, project_id):
url = f"{self.base_url}/api-json/import-files/{project_id}"
headers = {"Authorization": f"Bearer {self.api_key}"}

# Prepare the file for upload
with open(file_path, 'rb') as f:
files = {'file': (file_path.split('/')[-1], f, 'application/octet-stream')}
# Let requests handle "Content-Type"
response = requests.post(url, headers=headers, files=files)

return response

def get_article_file(self, project_id, article_id, hash):
url = f"{self.base_url}/api-json/files/{project_id}/article/{article_id}/download/{hash}"
headers = {"Authorization": f"Bearer {self.api_key}", "Content-Type": "application/json"}

1 change: 0 additions & 1 deletion test.py

This file was deleted.

0 comments on commit 50e2755

Please sign in to comment.