Skip to content
Snippets Groups Projects
Commit 701d2a59 authored by Pierre Chanial's avatar Pierre Chanial
Browse files

Add describe methods.

parent 9cb7c902
No related branches found
No related tags found
No related merge requests found
......@@ -60,6 +60,8 @@ repos:
- psycopg2==2.9.1
- sqlalchemy2-stubs==0.0.2a4
- types-requests==2.25.0
- types-tabulate==0.8.0
- types-termcolor==1.1.0
exclude: ^migrations/
#- repo: https://github.com/pre-commit/mirrors-pylint
......
"""This module provides a client to ESAP-DB."""
from ..config import settings
from ..helpers import raise_for_status
from ..helpers import print_table, raise_for_status
from ..models import Project, ResourceCollection
from ..sessions import BasedSession
......@@ -34,3 +34,8 @@ def create_project(
response = self.session.post('/projects', project)
raise_for_status(response)
return Project.deserialize(response.json())
def describe(self) -> None:
"""Prints the projects available to the user through the client."""
projects = ', '.join(_.name.split('.')[-1] for _ in self.projects) or '[]'
print_table(projects=projects)
......@@ -2,6 +2,8 @@
from typing import Type
from requests import Response
from tabulate import tabulate
from termcolor import colored
from .exceptions import ESAPClientError, ESAPError, ESAPServerError
......@@ -20,3 +22,9 @@ def raise_for_status(response: Response) -> None:
if isinstance(error, dict) and 'detail' in error:
error = error['detail']
raise cls(f'HTTP Error {response.status_code}: {error}')
def print_table(**keywords: str) -> None:
"""Prints a colorful table."""
table = [(colored(k.capitalize(), 'red'), v) for k, v in keywords.items()]
print(tabulate(table, tablefmt='plain'))
......@@ -7,7 +7,7 @@
import pandas as pd
from ..helpers import raise_for_status
from ..helpers import print_table, raise_for_status
from ..sessions import BasedSession
from .collections import ResourceCollection
from .tables import Table
......@@ -52,7 +52,11 @@ def tables(self) -> ResourceCollection[Table]:
)
def create_table_from(
self, source: Union[str, pd.DataFrame], name: Optional[str] = None, description: str = '', **keywords
self,
source: Union[str, pd.DataFrame],
name: Optional[str] = None,
description: str = '',
**keywords: Any,
) -> Table:
"""Creates a table from a Pandas DataFrame."""
if not isinstance(source, (str, pd.DataFrame)):
......@@ -119,3 +123,13 @@ def create_table_from_esap_gateway_query(
response = session.post('/esap-gateway-operations', payload)
raise_for_status(response)
return Table.deserialize(response.json())
def describe(self) -> None:
"""Prints information associated with the dataset."""
infos = {
'name': self.name,
'description': self.description,
}
tables = ', '.join(_.name.split('.')[-1] for _ in self.tables)
infos['tables'] = tables or '[]'
print_table(**infos)
......@@ -4,7 +4,7 @@
from typing import Any
from ..config import settings
from ..helpers import raise_for_status
from ..helpers import print_table, raise_for_status
from ..sessions import BasedSession
from .collections import ResourceCollection
from .datasets import Dataset
......@@ -13,12 +13,13 @@
class Project:
"""The project defines a scope, in which are stored collections of database tables.""" # noqa
def __init__(self, name: str, description: str, max_size: int) -> None:
def __init__(self, name: str, description: str, type: str, max_size: int) -> None:
"""The `Project` constructor."""
if '.' in name:
raise ValueError("Project names cannot contain the character '.'")
self.name = name
self.description = description
self.type = type
self.max_size = max_size
self.session = BasedSession(f'/projects/{name}')
......@@ -46,8 +47,9 @@ def deserialize(cls, project: dict) -> Project:
"""Deserializes a dict-like project."""
name = project['name']
description = project['description']
type = project['type']
max_size = project.get('max_size', settings.DEFAULT_PROJECT_MAX_SIZE)
return Project(name, description, max_size)
return Project(name, description, type, max_size)
def create_dataset(self, name: str, description: str = '') -> Dataset:
"""Creates a dataset inside this project."""
......@@ -63,3 +65,16 @@ def create_dataset(self, name: str, description: str = '') -> Dataset:
def datasets(self) -> ResourceCollection[Dataset]:
"""The member datasets of this project."""
return ResourceCollection[Dataset](f'/projects/{self.name}/datasets', Dataset)
def describe(self) -> None:
"""Prints information associated with the project."""
infos = {
'name': self.name,
'description': self.description,
'type': self.type,
}
if self.type == 'user':
infos['max size'] = f'{self.max_size / 2 ** 30} GiB'
datasets = ', '.join(_.name.split('.')[-1] for _ in self.datasets)
infos['datasets'] = datasets or '[]'
print_table(**infos)
......@@ -5,6 +5,7 @@
import pandas as pd
from ..helpers import print_table, raise_for_status
from ..sessions import BasedSession
......@@ -26,6 +27,12 @@ def __eq__(self, other: Any) -> bool:
return NotImplemented
return self.name == other.name and self.description == other.description
def __len__(self) -> int:
"""Returns the number of entries in the table."""
response = self.session.post(':getLength')
raise_for_status(response)
return response.json()
def __repr__(self) -> str:
"""The string representation of a `Table`."""
return f'<Table {self.name}>'
......@@ -33,13 +40,14 @@ def __repr__(self) -> str:
def delete(self) -> None:
"""Deletes this table."""
response = self.session.delete('')
response.raise_for_status()
raise_for_status(response)
@classmethod
def deserialize(cls, table: dict) -> Table:
"""Deserializes a dict-like table."""
return Table(table['name'], table['description'])
@property
def column_names(self) -> List[str]:
"""Returns the names of the table columns."""
return self.session.get('/column-names').json()
......@@ -51,3 +59,16 @@ def _content(self) -> list[dict]:
def aspandas(self) -> pd.DataFrame:
"""Returns the content of the table as a Pandas `DataFrame`."""
return pd.read_json(f'{self.session.base_url}/content')
def describe(self) -> None:
"""Prints information associated with the table."""
nrow = len(self)
row_str = 'row' if nrow < 2 else 'rows'
ncol = len(self.column_names)
col_str = 'column' if ncol < 2 else 'columns'
infos = {
'name': self.name,
'description': self.description,
'shape': f'{nrow} {row_str} x {ncol} {col_str}',
}
print_table(**infos)
......@@ -578,6 +578,25 @@ category = "main"
optional = false
python-versions = "*"
[[package]]
name = "tabulate"
version = "0.8.9"
description = "Pretty-print tabular data"
category = "main"
optional = false
python-versions = "*"
[package.extras]
widechars = ["wcwidth"]
[[package]]
name = "termcolor"
version = "1.1.0"
description = "ANSII Color formatting for output in terminal."
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "toml"
version = "0.10.2"
......@@ -692,7 +711,7 @@ h11 = ">=0.9.0,<1"
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
content-hash = "54b6ee91552dc096f0511b64ba5c9c4571a9905b82dcb6af7c93ea5e3078bc79"
content-hash = "c5a6013ec5017853114ad3f12bdcfd8b5c732ce206d08431a7886ac39372b42e"
[metadata.files]
anyio = [
......@@ -1039,6 +1058,13 @@ sortedcontainers = [
{file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
{file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
]
tabulate = [
{file = "tabulate-0.8.9-py3-none-any.whl", hash = "sha256:d7c013fe7abbc5e491394e10fa845f8f32fe54f8dc60c6622c6cf482d25d47e4"},
{file = "tabulate-0.8.9.tar.gz", hash = "sha256:eb1d13f25760052e8931f2ef80aaf6045a6cceb47514db8beab24cded16f13a7"},
]
termcolor = [
{file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"},
]
toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
......
......@@ -12,6 +12,8 @@ asks = "^2.4.12"
requests = "^2.25.1"
pandas = "^1.2.5"
pydantic = "^1.8.2"
tabulate = "^0.8.9"
termcolor = "^1.1.0"
[tool.poetry.dev-dependencies]
pytest = "^6.2.4"
......@@ -28,7 +30,7 @@ skip-string-normalization = true
[tool.isort]
profile = "black"
known_third_party = ["pandas", "pydantic", "pytest", "requests"]
known_third_party = ["pandas", "pydantic", "pytest", "requests", "tabulate", "termcolor"]
[tool.mypy]
python_version = "3.9"
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment