rowan-python 0.0.5__tar.gz → 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rowan_python-1.0.0/.github/workflows/python-publish.yml +39 -0
- rowan_python-1.0.0/.gitignore +17 -0
- {rowan_python-0.0.5 → rowan_python-1.0.0}/PKG-INFO +6 -9
- {rowan_python-0.0.5 → rowan_python-1.0.0}/README.md +2 -1
- rowan_python-1.0.0/examples/run.py +18 -0
- rowan_python-1.0.0/pyproject.toml +37 -0
- rowan_python-1.0.0/rowan/__init__.py +10 -0
- rowan_python-1.0.0/rowan/calculation.py +11 -0
- rowan_python-1.0.0/rowan/client.py +50 -0
- rowan_python-1.0.0/rowan/constants.py +1 -0
- rowan_python-1.0.0/rowan/folder.py +97 -0
- {rowan_python-0.0.5 → rowan_python-1.0.0}/rowan/utils.py +22 -1
- rowan_python-1.0.0/rowan/workflow.py +139 -0
- rowan_python-0.0.5/pyproject.toml +0 -30
- rowan_python-0.0.5/rowan/__init__.py +0 -4
- rowan_python-0.0.5/rowan/client.py +0 -155
- rowan_python-0.0.5/rowan_python.egg-info/PKG-INFO +0 -32
- rowan_python-0.0.5/rowan_python.egg-info/SOURCES.txt +0 -11
- rowan_python-0.0.5/rowan_python.egg-info/dependency_links.txt +0 -1
- rowan_python-0.0.5/rowan_python.egg-info/requires.txt +0 -4
- rowan_python-0.0.5/rowan_python.egg-info/top_level.txt +0 -1
- rowan_python-0.0.5/setup.cfg +0 -4
- {rowan_python-0.0.5 → rowan_python-1.0.0}/LICENSE +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# This workflow will upload a Python Package using Twine when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
|
3
|
+
|
|
4
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
5
|
+
# They are provided by a third-party and are governed by
|
|
6
|
+
# separate terms of service, privacy policy, and support
|
|
7
|
+
# documentation.
|
|
8
|
+
|
|
9
|
+
name: Upload Python Package
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
release:
|
|
13
|
+
types: [published]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
deploy:
|
|
20
|
+
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v3
|
|
25
|
+
- name: Set up Python
|
|
26
|
+
uses: actions/setup-python@v3
|
|
27
|
+
with:
|
|
28
|
+
python-version: '3.x'
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: |
|
|
31
|
+
python -m pip install --upgrade pip
|
|
32
|
+
pip install build
|
|
33
|
+
- name: Build package
|
|
34
|
+
run: python -m build
|
|
35
|
+
- name: Publish package
|
|
36
|
+
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
|
|
37
|
+
with:
|
|
38
|
+
user: __token__
|
|
39
|
+
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: rowan-python
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 1.0.0
|
|
4
4
|
Summary: Rowan Python Library
|
|
5
|
-
Author-email: Corin Wagen <corin@rowansci.com>
|
|
6
5
|
Project-URL: Homepage, https://github.com/rowansci/rowan-client
|
|
7
6
|
Project-URL: Bug Tracker, https://github.com/rowansci/rowan-client/issues
|
|
7
|
+
Author-email: Corin Wagen <corin@rowansci.com>
|
|
8
|
+
License-File: LICENSE
|
|
8
9
|
Requires-Python: >=3.8
|
|
9
10
|
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Requires-Dist: cctk>=0.2.18
|
|
12
|
-
Requires-Dist: httpx>=0.25
|
|
13
|
-
Requires-Dist: numpy>=1.24
|
|
14
|
-
Requires-Dist: stjames>=0.0.23
|
|
15
11
|
|
|
16
12
|
# Rowan Python Library
|
|
17
13
|
|
|
18
14
|
[](https://pypi.python.org/pypi/rowan-python)
|
|
19
|
-
[](https://pixi.sh)
|
|
16
|
+
[](https://github.com/charliermarsh/ruff)
|
|
20
17
|
|
|
21
18
|
The Rowan Python library provides convenient access to the Rowan API from applications written in the Python language.
|
|
22
19
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Rowan Python Library
|
|
2
2
|
|
|
3
3
|
[](https://pypi.python.org/pypi/rowan-python)
|
|
4
|
-
[](https://pixi.sh)
|
|
5
|
+
[](https://github.com/charliermarsh/ruff)
|
|
5
6
|
|
|
6
7
|
The Rowan Python library provides convenient access to the Rowan API from applications written in the Python language.
|
|
7
8
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import cctk
|
|
2
|
+
import rowan
|
|
3
|
+
|
|
4
|
+
rowan.api_key = "rowan-sk..."
|
|
5
|
+
client = rowan.Client()
|
|
6
|
+
|
|
7
|
+
# load molecule by name (cctk can also load in a variety of file formats)
|
|
8
|
+
molecule = cctk.Molecule.new_from_name("cyclobutane")
|
|
9
|
+
|
|
10
|
+
# run calculation remotely and return result
|
|
11
|
+
result = client.compute(
|
|
12
|
+
molecule,
|
|
13
|
+
name="opt cyclobutane",
|
|
14
|
+
method="b97-3c",
|
|
15
|
+
tasks=["optimize", "charge", "dipole"]
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
print(result)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "rowan-python"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Rowan Python Library"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.8"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "Corin Wagen", email = "corin@rowansci.com" },
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
[project.urls]
|
|
12
|
+
"Homepage" = "https://github.com/rowansci/rowan-client"
|
|
13
|
+
"Bug Tracker" = "https://github.com/rowansci/rowan-client/issues"
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
build-backend = "hatchling.build"
|
|
17
|
+
requires = ["hatchling"]
|
|
18
|
+
|
|
19
|
+
[tool.pixi.project]
|
|
20
|
+
channels = ["conda-forge"]
|
|
21
|
+
platforms = ["linux-64", "osx-arm64"]
|
|
22
|
+
|
|
23
|
+
[tool.pixi.pypi-dependencies]
|
|
24
|
+
rowan-python = { path = ".", editable = true }
|
|
25
|
+
stjames = ">=0.0.42"
|
|
26
|
+
|
|
27
|
+
[tool.pixi.tasks]
|
|
28
|
+
|
|
29
|
+
[tool.pixi.dependencies]
|
|
30
|
+
cctk = ">=0.2.25,<0.3"
|
|
31
|
+
httpx = ">=0.27.2,<0.28"
|
|
32
|
+
setuptools = ">=73.0.1,<74"
|
|
33
|
+
rdkit = ">=2024.03.1"
|
|
34
|
+
|
|
35
|
+
[tool.hatch.build.targets.wheel]
|
|
36
|
+
packages = ["rowan"]
|
|
37
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import stjames
|
|
2
|
+
|
|
3
|
+
from .utils import api_client
|
|
4
|
+
|
|
5
|
+
class Calculation:
|
|
6
|
+
@classmethod
|
|
7
|
+
def retrieve(cls, uuid: stjames.UUID) -> dict:
|
|
8
|
+
with api_client() as client:
|
|
9
|
+
response = client.get(f"/calculation/{uuid}/stjames")
|
|
10
|
+
response.raise_for_status()
|
|
11
|
+
return response.json()
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import cctk
|
|
2
|
+
import stjames
|
|
3
|
+
import time
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from .utils import cctk_to_stjames, smiles_to_stjames
|
|
7
|
+
from .workflow import Workflow
|
|
8
|
+
|
|
9
|
+
""" A high-level interface to submitting a calculation. """
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def compute(
|
|
13
|
+
molecule: str | cctk.Molecule | stjames.Molecule,
|
|
14
|
+
workflow_type: str,
|
|
15
|
+
name: str = "",
|
|
16
|
+
folder_uuid: Optional[stjames.UUID] = None,
|
|
17
|
+
blocking: bool = True,
|
|
18
|
+
ping_interval: int = 5,
|
|
19
|
+
**workflow_data,
|
|
20
|
+
) -> dict:
|
|
21
|
+
"""High-level function to compute and return workflows."""
|
|
22
|
+
|
|
23
|
+
if isinstance(molecule, str):
|
|
24
|
+
stjmol = smiles_to_stjames(molecule)
|
|
25
|
+
elif isinstance(molecule, cctk.Molecule):
|
|
26
|
+
stjmol = cctk_to_stjames(molecule)
|
|
27
|
+
elif isinstance(molecule, stjames.Molecule):
|
|
28
|
+
stjmol = molecule
|
|
29
|
+
else:
|
|
30
|
+
raise ValueError("Invalid type for `molecule`!")
|
|
31
|
+
|
|
32
|
+
result = Workflow.submit(
|
|
33
|
+
initial_molecule=stjmol,
|
|
34
|
+
workflow_type=workflow_type,
|
|
35
|
+
name=name,
|
|
36
|
+
folder_uuid=folder_uuid,
|
|
37
|
+
workflow_data=workflow_data,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if blocking:
|
|
41
|
+
uuid = result["uuid"]
|
|
42
|
+
|
|
43
|
+
while not Workflow.is_finished(uuid):
|
|
44
|
+
time.sleep(ping_interval)
|
|
45
|
+
|
|
46
|
+
completed_result = Workflow.retrieve(uuid)
|
|
47
|
+
return completed_result
|
|
48
|
+
|
|
49
|
+
else:
|
|
50
|
+
return result
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
API_URL = "https://api.rowansci.com"
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import stjames
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from .utils import api_client
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Folder:
|
|
8
|
+
@classmethod
|
|
9
|
+
def create(
|
|
10
|
+
cls,
|
|
11
|
+
name: str,
|
|
12
|
+
parent_uuid: Optional[stjames.UUID] = None,
|
|
13
|
+
notes: str = "",
|
|
14
|
+
starred: bool = False,
|
|
15
|
+
public: bool = False,
|
|
16
|
+
) -> dict:
|
|
17
|
+
with api_client() as client:
|
|
18
|
+
response = client.post(
|
|
19
|
+
"/folder",
|
|
20
|
+
json={
|
|
21
|
+
"name": name,
|
|
22
|
+
"parent_uuid": parent_uuid,
|
|
23
|
+
"notes": notes,
|
|
24
|
+
"starred": starred,
|
|
25
|
+
"public": public,
|
|
26
|
+
},
|
|
27
|
+
)
|
|
28
|
+
response.raise_for_status()
|
|
29
|
+
return response.json()
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def retrieve(cls, uuid: stjames.UUID) -> dict:
|
|
33
|
+
with api_client() as client:
|
|
34
|
+
response = client.get(f"/folder/{uuid}")
|
|
35
|
+
response.raise_for_status()
|
|
36
|
+
return response.json()
|
|
37
|
+
|
|
38
|
+
@classmethod
|
|
39
|
+
def update(
|
|
40
|
+
cls,
|
|
41
|
+
uuid: stjames.UUID,
|
|
42
|
+
name: Optional[str] = None,
|
|
43
|
+
parent_uuid: Optional[stjames.UUID] = None,
|
|
44
|
+
notes: Optional[str] = None,
|
|
45
|
+
starred: Optional[bool] = None,
|
|
46
|
+
public: Optional[bool] = None,
|
|
47
|
+
) -> None:
|
|
48
|
+
old_data = cls.retrieve(uuid)
|
|
49
|
+
|
|
50
|
+
new_data = {}
|
|
51
|
+
new_data["name"] = name if name is not None else old_data["name"]
|
|
52
|
+
new_data["parent_uuid"] = (
|
|
53
|
+
parent_uuid if parent_uuid is not None else old_data["parent_uuid"]
|
|
54
|
+
)
|
|
55
|
+
new_data["notes"] = notes if notes is not None else old_data["notes"]
|
|
56
|
+
new_data["starred"] = starred if starred is not None else old_data["starred"]
|
|
57
|
+
new_data["public"] = public if public is not None else old_data["public"]
|
|
58
|
+
|
|
59
|
+
with api_client() as client:
|
|
60
|
+
response = client.post(f"/folder/{uuid}", json=new_data)
|
|
61
|
+
response.raise_for_status()
|
|
62
|
+
return response.json()
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def delete(cls, uuid: stjames.UUID) -> None:
|
|
66
|
+
with api_client() as client:
|
|
67
|
+
response = client.delete(f"/folder/{uuid}")
|
|
68
|
+
response.raise_for_status()
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def list(
|
|
72
|
+
cls,
|
|
73
|
+
parent_uuid: Optional[stjames.UUID] = None,
|
|
74
|
+
name_contains: Optional[str] = None,
|
|
75
|
+
public: Optional[bool] = None,
|
|
76
|
+
starred: Optional[bool] = None,
|
|
77
|
+
page: int = 0,
|
|
78
|
+
size: int = 10,
|
|
79
|
+
):
|
|
80
|
+
params = {"page": page, "size": size}
|
|
81
|
+
|
|
82
|
+
if parent_uuid is not None:
|
|
83
|
+
params["parent_uuid"] = parent_uuid
|
|
84
|
+
|
|
85
|
+
if name_contains is not None:
|
|
86
|
+
params["name_contains"] = name_contains
|
|
87
|
+
|
|
88
|
+
if public is not None:
|
|
89
|
+
params["public"] = public
|
|
90
|
+
|
|
91
|
+
if starred is not None:
|
|
92
|
+
params["starred"] = starred
|
|
93
|
+
|
|
94
|
+
with api_client() as client:
|
|
95
|
+
response = client.get("/folder", params=params)
|
|
96
|
+
response.raise_for_status()
|
|
97
|
+
return response.json()
|
|
@@ -2,9 +2,13 @@ import os
|
|
|
2
2
|
import cctk
|
|
3
3
|
import stjames
|
|
4
4
|
import numpy as np
|
|
5
|
+
from contextlib import contextmanager
|
|
6
|
+
import httpx
|
|
5
7
|
|
|
6
8
|
import rowan
|
|
7
9
|
|
|
10
|
+
from .constants import API_URL
|
|
11
|
+
|
|
8
12
|
|
|
9
13
|
def get_api_key() -> str:
|
|
10
14
|
api_key = os.environ.get("ROWAN_API_KEY")
|
|
@@ -24,10 +28,27 @@ def cctk_to_stjames(molecule: cctk.Molecule) -> stjames.Molecule:
|
|
|
24
28
|
|
|
25
29
|
atoms = list()
|
|
26
30
|
for i in range(molecule.num_atoms()):
|
|
27
|
-
atoms.append(
|
|
31
|
+
atoms.append(
|
|
32
|
+
stjames.Atom(atomic_number=atomic_numbers[i], position=geometry[i])
|
|
33
|
+
)
|
|
28
34
|
|
|
29
35
|
return stjames.Molecule(
|
|
30
36
|
atoms=atoms,
|
|
31
37
|
charge=molecule.charge,
|
|
32
38
|
multiplicity=molecule.multiplicity,
|
|
33
39
|
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def smiles_to_stjames(smiles: str) -> stjames.Molecule:
|
|
43
|
+
cmol = cctk.Molecule.new_from_smiles(smiles)
|
|
44
|
+
return cctk_to_stjames(cmol)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@contextmanager
|
|
48
|
+
def api_client():
|
|
49
|
+
"""Wraps `httpx.Client` with Rowan-specific kwargs."""
|
|
50
|
+
with httpx.Client(
|
|
51
|
+
base_url=API_URL,
|
|
52
|
+
headers={"X-API-Key": get_api_key()},
|
|
53
|
+
) as client:
|
|
54
|
+
yield client
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import stjames
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from .utils import api_client
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Workflow:
|
|
9
|
+
@classmethod
|
|
10
|
+
def submit(
|
|
11
|
+
cls,
|
|
12
|
+
workflow_type: str,
|
|
13
|
+
initial_molecule: dict | stjames.Molecule,
|
|
14
|
+
workflow_data: dict,
|
|
15
|
+
name: Optional[str] = None,
|
|
16
|
+
folder_uuid: Optional[stjames.UUID] = None,
|
|
17
|
+
) -> dict:
|
|
18
|
+
if isinstance(initial_molecule, stjames.Molecule):
|
|
19
|
+
molecule_dict = initial_molecule.model_dump()
|
|
20
|
+
elif isinstance(initial_molecule, dict):
|
|
21
|
+
molecule_dict = initial_molecule
|
|
22
|
+
else:
|
|
23
|
+
raise ValueError("Invalid type for `initial_molecule`")
|
|
24
|
+
|
|
25
|
+
with api_client() as client:
|
|
26
|
+
response = client.post(
|
|
27
|
+
"/workflow",
|
|
28
|
+
json={
|
|
29
|
+
"name": name,
|
|
30
|
+
"folder_uuid": folder_uuid,
|
|
31
|
+
"initial_molecule": molecule_dict,
|
|
32
|
+
"workflow_type": workflow_type,
|
|
33
|
+
"workflow_data": workflow_data,
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
response.raise_for_status()
|
|
37
|
+
return response.json()
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def retrieve(cls, uuid: stjames.UUID) -> dict:
|
|
41
|
+
with api_client() as client:
|
|
42
|
+
response = client.get(f"/workflow/{uuid}")
|
|
43
|
+
response.raise_for_status()
|
|
44
|
+
return response.json()
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def update(
|
|
48
|
+
cls,
|
|
49
|
+
uuid: stjames.UUID,
|
|
50
|
+
name: Optional[str] = None,
|
|
51
|
+
parent_uuid: Optional[stjames.UUID] = None,
|
|
52
|
+
notes: Optional[str] = None,
|
|
53
|
+
starred: Optional[bool] = None,
|
|
54
|
+
email_when_complete: Optional[bool] = None,
|
|
55
|
+
public: Optional[bool] = None,
|
|
56
|
+
) -> None:
|
|
57
|
+
old_data = cls.retrieve(uuid)
|
|
58
|
+
|
|
59
|
+
new_data = {}
|
|
60
|
+
new_data["name"] = name if name is not None else old_data["name"]
|
|
61
|
+
new_data["parent_uuid"] = (
|
|
62
|
+
parent_uuid if parent_uuid is not None else old_data["parent_uuid"]
|
|
63
|
+
)
|
|
64
|
+
new_data["notes"] = notes if notes is not None else old_data["notes"]
|
|
65
|
+
new_data["starred"] = starred if starred is not None else old_data["starred"]
|
|
66
|
+
new_data["email_when_complete"] = (
|
|
67
|
+
email_when_complete
|
|
68
|
+
if email_when_complete is not None
|
|
69
|
+
else old_data["email_when_complete"]
|
|
70
|
+
)
|
|
71
|
+
new_data["public"] = public if public is not None else old_data["public"]
|
|
72
|
+
|
|
73
|
+
with api_client() as client:
|
|
74
|
+
response = client.post(f"/workflow/{uuid}", json=new_data)
|
|
75
|
+
response.raise_for_status()
|
|
76
|
+
return response.json()
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def status(cls, uuid: stjames.UUID) -> int:
|
|
80
|
+
data = cls.retrieve(uuid)
|
|
81
|
+
return data["object_status"]
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def is_finished(cls, uuid: stjames.UUID) -> bool:
|
|
85
|
+
status = cls.status(uuid)
|
|
86
|
+
return status in {
|
|
87
|
+
stjames.Status.COMPLETED_OK.value,
|
|
88
|
+
stjames.Status.FAILED.value,
|
|
89
|
+
stjames.Status.STOPPED.value,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@classmethod
|
|
93
|
+
def stop(cls, uuid: stjames.UUID) -> None:
|
|
94
|
+
with api_client() as client:
|
|
95
|
+
response = client.post(f"/workflow/{uuid}/stop")
|
|
96
|
+
response.raise_for_status()
|
|
97
|
+
|
|
98
|
+
@classmethod
|
|
99
|
+
def delete(cls, uuid: stjames.UUID) -> None:
|
|
100
|
+
with api_client() as client:
|
|
101
|
+
response = client.delete(f"/workflow/{uuid}")
|
|
102
|
+
response.raise_for_status()
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def list(
|
|
106
|
+
cls,
|
|
107
|
+
parent_uuid: Optional[stjames.UUID] = None,
|
|
108
|
+
name_contains: Optional[str] = None,
|
|
109
|
+
public: Optional[bool] = None,
|
|
110
|
+
starred: Optional[bool] = None,
|
|
111
|
+
object_status: Optional[int] = None,
|
|
112
|
+
object_type: Optional[str] = None,
|
|
113
|
+
page: int = 0,
|
|
114
|
+
size: int = 10,
|
|
115
|
+
):
|
|
116
|
+
params = {"page": page, "size": size}
|
|
117
|
+
|
|
118
|
+
if parent_uuid is not None:
|
|
119
|
+
params["parent_uuid"] = parent_uuid
|
|
120
|
+
|
|
121
|
+
if name_contains is not None:
|
|
122
|
+
params["name_contains"] = name_contains
|
|
123
|
+
|
|
124
|
+
if public is not None:
|
|
125
|
+
params["public"] = public
|
|
126
|
+
|
|
127
|
+
if starred is not None:
|
|
128
|
+
params["starred"] = starred
|
|
129
|
+
|
|
130
|
+
if object_status is not None:
|
|
131
|
+
params["object_status"] = object_status
|
|
132
|
+
|
|
133
|
+
if object_type is not None:
|
|
134
|
+
params["object_type"] = object_type
|
|
135
|
+
|
|
136
|
+
with api_client() as client:
|
|
137
|
+
response = client.get("/workflow", params=params)
|
|
138
|
+
response.raise_for_status()
|
|
139
|
+
return response.json()
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "rowan-python"
|
|
3
|
-
version = "0.0.5"
|
|
4
|
-
description = "Rowan Python Library"
|
|
5
|
-
readme = "README.md"
|
|
6
|
-
requires-python = ">=3.8"
|
|
7
|
-
authors = [
|
|
8
|
-
{ name = "Corin Wagen", email = "corin@rowansci.com" },
|
|
9
|
-
]
|
|
10
|
-
|
|
11
|
-
dependencies = [
|
|
12
|
-
"cctk>=0.2.18",
|
|
13
|
-
"httpx>=0.25",
|
|
14
|
-
"numpy>=1.24",
|
|
15
|
-
"stjames>=0.0.23",
|
|
16
|
-
]
|
|
17
|
-
|
|
18
|
-
[build-system]
|
|
19
|
-
# maybe will want to move away from setuptools eventually
|
|
20
|
-
requires = ["setuptools>=61.0"]
|
|
21
|
-
build-backend = "setuptools.build_meta"
|
|
22
|
-
|
|
23
|
-
[project.urls]
|
|
24
|
-
"Homepage" = "https://github.com/rowansci/rowan-client"
|
|
25
|
-
"Bug Tracker" = "https://github.com/rowansci/rowan-client/issues"
|
|
26
|
-
|
|
27
|
-
[tool.ruff]
|
|
28
|
-
line-length = 160
|
|
29
|
-
select = ["E", "F"]
|
|
30
|
-
ignore = ["E741"]
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import cctk
|
|
4
|
-
import httpx
|
|
5
|
-
from typing import Optional
|
|
6
|
-
import stjames
|
|
7
|
-
from dataclasses import dataclass, field
|
|
8
|
-
import time
|
|
9
|
-
|
|
10
|
-
import rowan
|
|
11
|
-
|
|
12
|
-
API_URL = "https://api.rowansci.com"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@dataclass
|
|
16
|
-
class Client:
|
|
17
|
-
blocking: bool = True
|
|
18
|
-
print: bool = True
|
|
19
|
-
ping_interval: int = 5
|
|
20
|
-
delete_when_finished: bool = False
|
|
21
|
-
|
|
22
|
-
headers: dict = field(init=False)
|
|
23
|
-
|
|
24
|
-
def __post_init__(self):
|
|
25
|
-
self.headers = {"X-API-Key": rowan.utils.get_api_key()}
|
|
26
|
-
|
|
27
|
-
if not self.blocking and self.delete_when_finished:
|
|
28
|
-
print("Warning: ``delete_when_finished`` has no effect when ``blocking`` is False. To delete calculations, call ``Client.delete()`` manually.")
|
|
29
|
-
|
|
30
|
-
def compute(
|
|
31
|
-
self,
|
|
32
|
-
type: Optional[str] = "calculation",
|
|
33
|
-
input_mol: Optional[cctk.Molecule] = None,
|
|
34
|
-
input_smiles: Optional[str] = None,
|
|
35
|
-
name: Optional[str] = None,
|
|
36
|
-
folder_uuid: Optional[str] = None,
|
|
37
|
-
engine: str = "peregrine",
|
|
38
|
-
**options,
|
|
39
|
-
) -> dict | str:
|
|
40
|
-
if (input_mol is None) == (input_smiles is None):
|
|
41
|
-
raise ValueError("Must specify exactly one of ``input_smiles`` and ``input_mol``!")
|
|
42
|
-
|
|
43
|
-
if input_mol is None:
|
|
44
|
-
input_mol = cctk.Molecule.new_from_smiles(input_smiles)
|
|
45
|
-
|
|
46
|
-
molecule = rowan.utils.cctk_to_stjames(input_mol)
|
|
47
|
-
|
|
48
|
-
with httpx.Client() as client:
|
|
49
|
-
if type == "calculation":
|
|
50
|
-
settings = stjames.Settings(**options)
|
|
51
|
-
calc = stjames.Calculation(molecules=[molecule], name=name, engine=engine, settings=settings)
|
|
52
|
-
|
|
53
|
-
response = client.post(
|
|
54
|
-
f"{API_URL}/calculation",
|
|
55
|
-
headers=self.headers,
|
|
56
|
-
json={
|
|
57
|
-
"json_data": calc.model_dump(mode="json"),
|
|
58
|
-
"folder_uuid": folder_uuid,
|
|
59
|
-
},
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
else:
|
|
63
|
-
response = client.post(
|
|
64
|
-
f"{API_URL}/workflow",
|
|
65
|
-
headers=self.headers,
|
|
66
|
-
json={
|
|
67
|
-
"initial_molecule": molecule.model_dump(mode="json"),
|
|
68
|
-
"workflow_type": type,
|
|
69
|
-
"name": name,
|
|
70
|
-
"folder_uuid": folder_uuid,
|
|
71
|
-
"workflow_data": options,
|
|
72
|
-
},
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
response.raise_for_status()
|
|
76
|
-
response_dict = response.json()
|
|
77
|
-
calc_uuid = response_dict["uuid"]
|
|
78
|
-
|
|
79
|
-
if self.blocking:
|
|
80
|
-
while not self.is_finished(calc_uuid, type):
|
|
81
|
-
time.sleep(self.ping_interval)
|
|
82
|
-
result = self.get(calc_uuid, type)
|
|
83
|
-
|
|
84
|
-
if self.delete_when_finished:
|
|
85
|
-
self.delete(calc_uuid, type)
|
|
86
|
-
|
|
87
|
-
return result
|
|
88
|
-
|
|
89
|
-
else:
|
|
90
|
-
return calc_uuid
|
|
91
|
-
|
|
92
|
-
def is_finished(self, calc_uuid: str, type: str = "calculation") -> bool:
|
|
93
|
-
with httpx.Client() as client:
|
|
94
|
-
if type == "calculation":
|
|
95
|
-
response = client.get(f"{API_URL}/calculation/{calc_uuid}", headers=self.headers)
|
|
96
|
-
response.raise_for_status()
|
|
97
|
-
response_dict = response.json()
|
|
98
|
-
status = response_dict["status"]
|
|
99
|
-
|
|
100
|
-
else:
|
|
101
|
-
response = client.get(f"{API_URL}/workflow/{calc_uuid}", headers=self.headers)
|
|
102
|
-
response.raise_for_status()
|
|
103
|
-
response_dict = response.json()
|
|
104
|
-
status = response_dict["object_status"]
|
|
105
|
-
|
|
106
|
-
return status in [2, 3, 4]
|
|
107
|
-
|
|
108
|
-
def get(self, calc_uuid: str, type: str = "calculation") -> dict:
|
|
109
|
-
with httpx.Client() as client:
|
|
110
|
-
if type == "calculation":
|
|
111
|
-
stj_response = client.get(f"{API_URL}/calculation/{calc_uuid}/stjames", headers=self.headers)
|
|
112
|
-
stj_response.raise_for_status()
|
|
113
|
-
stj_dict = stj_response.json()
|
|
114
|
-
|
|
115
|
-
response = client.get(f"{API_URL}/calculation/{calc_uuid}", headers=self.headers)
|
|
116
|
-
response.raise_for_status()
|
|
117
|
-
response_dict = response.json()
|
|
118
|
-
|
|
119
|
-
# reformat
|
|
120
|
-
del response_dict["settings"]
|
|
121
|
-
response_dict["data"] = stj_dict
|
|
122
|
-
|
|
123
|
-
return response_dict
|
|
124
|
-
|
|
125
|
-
else:
|
|
126
|
-
response = client.get(f"{API_URL}/workflow/{calc_uuid}", headers=self.headers)
|
|
127
|
-
response.raise_for_status()
|
|
128
|
-
response_dict = response.json()
|
|
129
|
-
|
|
130
|
-
# reformat
|
|
131
|
-
response_dict["data"] = response_dict["object_data"]
|
|
132
|
-
del response_dict["object_data"]
|
|
133
|
-
del response_dict["status"]
|
|
134
|
-
|
|
135
|
-
return response_dict
|
|
136
|
-
|
|
137
|
-
def stop(self, calc_uuid: str, type: str = "calculation") -> None:
|
|
138
|
-
with httpx.Client() as client:
|
|
139
|
-
if type == "calculation":
|
|
140
|
-
response = client.post(f"{API_URL}/calculation/{calc_uuid}/stop", headers=self.headers)
|
|
141
|
-
response.raise_for_status()
|
|
142
|
-
|
|
143
|
-
else:
|
|
144
|
-
response = client.post(f"{API_URL}/workflow/{calc_uuid}/stop", headers=self.headers)
|
|
145
|
-
response.raise_for_status()
|
|
146
|
-
|
|
147
|
-
def delete(self, calc_uuid: str, type: str = "calculation") -> None:
|
|
148
|
-
with httpx.Client() as client:
|
|
149
|
-
if type == "calculation":
|
|
150
|
-
response = client.delete(f"{API_URL}/calculation/{calc_uuid}", headers=self.headers)
|
|
151
|
-
response.raise_for_status()
|
|
152
|
-
|
|
153
|
-
else:
|
|
154
|
-
response = client.delete(f"{API_URL}/folder/{calc_uuid}", headers=self.headers)
|
|
155
|
-
response.raise_for_status()
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: rowan-python
|
|
3
|
-
Version: 0.0.5
|
|
4
|
-
Summary: Rowan Python Library
|
|
5
|
-
Author-email: Corin Wagen <corin@rowansci.com>
|
|
6
|
-
Project-URL: Homepage, https://github.com/rowansci/rowan-client
|
|
7
|
-
Project-URL: Bug Tracker, https://github.com/rowansci/rowan-client/issues
|
|
8
|
-
Requires-Python: >=3.8
|
|
9
|
-
Description-Content-Type: text/markdown
|
|
10
|
-
License-File: LICENSE
|
|
11
|
-
Requires-Dist: cctk>=0.2.18
|
|
12
|
-
Requires-Dist: httpx>=0.25
|
|
13
|
-
Requires-Dist: numpy>=1.24
|
|
14
|
-
Requires-Dist: stjames>=0.0.23
|
|
15
|
-
|
|
16
|
-
# Rowan Python Library
|
|
17
|
-
|
|
18
|
-
[](https://pypi.python.org/pypi/rowan-python)
|
|
19
|
-
[](https://github.com/charliermarsh/ruff)
|
|
20
|
-
|
|
21
|
-
The Rowan Python library provides convenient access to the Rowan API from applications written in the Python language.
|
|
22
|
-
|
|
23
|
-
## Documentation
|
|
24
|
-
|
|
25
|
-
The documentation is available [here](https://docs.rowansci.com/python-api).
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
## Issues
|
|
29
|
-
|
|
30
|
-
To report issues, please use the "Issues" tab above.
|
|
31
|
-
|
|
32
|
-
*Corin Wagen, 2023*
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
LICENSE
|
|
2
|
-
README.md
|
|
3
|
-
pyproject.toml
|
|
4
|
-
rowan/__init__.py
|
|
5
|
-
rowan/client.py
|
|
6
|
-
rowan/utils.py
|
|
7
|
-
rowan_python.egg-info/PKG-INFO
|
|
8
|
-
rowan_python.egg-info/SOURCES.txt
|
|
9
|
-
rowan_python.egg-info/dependency_links.txt
|
|
10
|
-
rowan_python.egg-info/requires.txt
|
|
11
|
-
rowan_python.egg-info/top_level.txt
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
rowan
|
rowan_python-0.0.5/setup.cfg
DELETED
|
File without changes
|