educelab-hercdb 0.1.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.
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.1
2
+ Name: educelab-hercdb
3
+ Version: 0.1.0
4
+ Summary: Graph database API for Herculaneum data
5
+ Author-email: Mami Hayashida <mami.hayashida@uky.edu>, Seth Parker <c.seth.parker@uky.edu>
6
+ Project-URL: Repository, https://gitlab.com/educelab/educelab-hercdb
7
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: neo4j>=5.20
16
+ Requires-Dist: numpy>=2.0
17
+ Requires-Dist: pandas>=2.2
18
+ Requires-Dist: prompt-toolkit
19
+
20
+ # EduceLab Herculaneum Graph Database API
21
+
22
+ This API is considered a work in progress and can change at any moment.
23
+
24
+ ## Installation
25
+
26
+ The latest release is available on PyPI:
27
+
28
+ ```shell
29
+ python3 -m pip install educelab-hercdb
30
+ ```
31
+
32
+ ## Connect to a server
33
+
34
+ ```python
35
+ from educelab import hercdb
36
+
37
+ uri = "neo4j://localhost:7687"
38
+ user = "foo"
39
+ password = "bar"
40
+ db = hercdb.connect(uri, user, password)
41
+ if db.verify_connection():
42
+ print("Connected!")
43
+ ```
44
+
45
+ ### Server configuration
46
+
47
+ If not provided when calling `hercdb.connect()`, this package will attempt to
48
+ read the URI, username, and password from the configuration file at `~/.hercdb`.
49
+ This file is expected to be in the [TOML](https://toml.io/) format:
50
+ ```toml
51
+ [database]
52
+ uri = "neo4j://localhost:7687"
53
+ username = "foo"
54
+ password = "bar"
55
+ ```
56
+ The section header is optional, and only the information from the first section
57
+ will be read. In the future, sections may be used to differentiate multiple
58
+ database servers. **Note: In Python 3.10, the configuration file is loaded
59
+ using `configparser`, which does not support the full TOML syntax.**
60
+
61
+ Alternatively, the server information can be provided by exporting the following
62
+ environment variables:
63
+ ```shell
64
+ export EDUCEDB_URI='neo4j://localhost:7687'
65
+ export EDUCEDB_USER=foo
66
+ export EDUCEDB_PASSWORD=bar
67
+ ```
68
+ Environment variables take priority over the configuration file.
69
+
70
+ As a convenience, this package provides the `hercdb.config.request_required()`
71
+ method, which will check for configuration values in the environment and
72
+ the configuration file and prompt for any which have not been provided:
73
+ ```
74
+ >>> hercdb.config.request_required()
75
+
76
+ Enter URI: neo4j://localhost:7687
77
+ Enter username: foo
78
+ Enter password:
79
+ ```
@@ -0,0 +1,60 @@
1
+ # EduceLab Herculaneum Graph Database API
2
+
3
+ This API is considered a work in progress and can change at any moment.
4
+
5
+ ## Installation
6
+
7
+ The latest release is available on PyPI:
8
+
9
+ ```shell
10
+ python3 -m pip install educelab-hercdb
11
+ ```
12
+
13
+ ## Connect to a server
14
+
15
+ ```python
16
+ from educelab import hercdb
17
+
18
+ uri = "neo4j://localhost:7687"
19
+ user = "foo"
20
+ password = "bar"
21
+ db = hercdb.connect(uri, user, password)
22
+ if db.verify_connection():
23
+ print("Connected!")
24
+ ```
25
+
26
+ ### Server configuration
27
+
28
+ If not provided when calling `hercdb.connect()`, this package will attempt to
29
+ read the URI, username, and password from the configuration file at `~/.hercdb`.
30
+ This file is expected to be in the [TOML](https://toml.io/) format:
31
+ ```toml
32
+ [database]
33
+ uri = "neo4j://localhost:7687"
34
+ username = "foo"
35
+ password = "bar"
36
+ ```
37
+ The section header is optional, and only the information from the first section
38
+ will be read. In the future, sections may be used to differentiate multiple
39
+ database servers. **Note: In Python 3.10, the configuration file is loaded
40
+ using `configparser`, which does not support the full TOML syntax.**
41
+
42
+ Alternatively, the server information can be provided by exporting the following
43
+ environment variables:
44
+ ```shell
45
+ export EDUCEDB_URI='neo4j://localhost:7687'
46
+ export EDUCEDB_USER=foo
47
+ export EDUCEDB_PASSWORD=bar
48
+ ```
49
+ Environment variables take priority over the configuration file.
50
+
51
+ As a convenience, this package provides the `hercdb.config.request_required()`
52
+ method, which will check for configuration values in the environment and
53
+ the configuration file and prompt for any which have not been provided:
54
+ ```
55
+ >>> hercdb.config.request_required()
56
+
57
+ Enter URI: neo4j://localhost:7687
58
+ Enter username: foo
59
+ Enter password:
60
+ ```
@@ -0,0 +1,39 @@
1
+ [build-system]
2
+ requires = ["setuptools >= 62"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.setuptools.packages.find]
6
+ where = ["src/"]
7
+ include = ["educelab.hercdb"]
8
+
9
+ [project]
10
+ name = "educelab-hercdb"
11
+ version = "0.1.0"
12
+ dependencies = [
13
+ "neo4j>=5.20",
14
+ "numpy>=2.0",
15
+ "pandas>=2.2",
16
+ "prompt-toolkit"
17
+ ]
18
+ requires-python = ">= 3.10"
19
+ authors = [
20
+ {name = "Mami Hayashida", email = "mami.hayashida@uky.edu"},
21
+ {name = "Seth Parker", email = "c.seth.parker@uky.edu"}
22
+ ]
23
+ description = "Graph database API for Herculaneum data"
24
+ readme = "README.md"
25
+ classifiers = [
26
+ "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
27
+ "Operating System :: OS Independent",
28
+
29
+ "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3.10",
31
+ "Programming Language :: Python :: 3.11",
32
+ "Programming Language :: Python :: 3.12",
33
+ ]
34
+
35
+ [project.urls]
36
+ Repository = "https://gitlab.com/educelab/educelab-hercdb"
37
+
38
+ [project.scripts]
39
+ el-hercdb-search = "educelab.hercdb.apps.search:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from educelab.hercdb import config
2
+ from educelab.hercdb.api import (connect, GraphDBConnection, FlatbedScanType,
3
+ PGSRawType, SpectralRawType)
@@ -0,0 +1,186 @@
1
+ import logging
2
+ from enum import Enum
3
+
4
+ from neo4j import GraphDatabase
5
+
6
+
7
+ class DatasetType(Enum):
8
+ FlatbedScan = 'FlatbedScanDataset'
9
+ PGSRaw = 'PGSRaw'
10
+ SpectralRaw = 'SpectralRaw'
11
+
12
+ def __str__(self):
13
+ return f'{self.value}'
14
+
15
+
16
+ FlatbedScanType = DatasetType.FlatbedScan
17
+ PGSRawType = DatasetType.PGSRaw
18
+ SpectralRawType = DatasetType.SpectralRaw
19
+
20
+
21
+ class GraphDBConnection:
22
+ logger = logging.getLogger('educelab.hercdb')
23
+ uri: str = None
24
+ user: str = None
25
+
26
+ def __init__(self, uri, user, password):
27
+ self.uri = uri
28
+ self.user = user
29
+ self.driver = GraphDatabase.driver(uri, auth=(user, password))
30
+
31
+ def __del__(self):
32
+ self.close()
33
+
34
+ def close(self):
35
+ self.driver.close()
36
+
37
+ def verify_connection(self):
38
+ try:
39
+ self.driver.verify_connectivity()
40
+ return True
41
+ except Exception as e:
42
+ self.logger.debug('failed to connect', exc_info=e)
43
+ return False
44
+
45
+ def _delete_all(self):
46
+ records, summary, keys = self.driver.execute_query(
47
+ """
48
+ MATCH (n)
49
+ DETACH DELETE n
50
+ """,
51
+ database_="neo4j",
52
+ )
53
+
54
+ def _return_all(self):
55
+ records, summary, keys = self.driver.execute_query(
56
+ """
57
+ MATCH (n)
58
+ RETURN n
59
+ """,
60
+ database_="neo4j",
61
+ )
62
+ self.logger.debug(records)
63
+ self.logger.debug(summary)
64
+ self.logger.debug(keys)
65
+
66
+ def _node_count(self) -> int:
67
+ record, keys, summary = self.driver.execute_query(
68
+ """
69
+ MATCH (n)
70
+ RETURN count(n)
71
+ """,
72
+ database_="neo4j",
73
+ )
74
+ count = record[0]
75
+ assert isinstance(count, int)
76
+ return count
77
+
78
+ def get_human_readable_name(self, pherc, cornice=None, pezzo=None):
79
+
80
+ pherc_n = None
81
+ corn_n = None
82
+ pezzo_n = None
83
+
84
+ if cornice:
85
+ records, summary, keys = self.driver.execute_query(
86
+ """
87
+ MATCH (ph:PHerc {name: $ph})-[:HAS]->(cr:Cornice {name: $cor})
88
+ RETURN ph.human_name, cr.human_name
89
+ """, ph=pherc, cor=cornice,
90
+ database_="neo4j",
91
+ )
92
+ if records:
93
+ pherc_n = records[0]["ph.human_name"]
94
+ corn_n = records[0]["c.human_name"]
95
+
96
+ if pezzo is not None:
97
+ # Currently this is irrelevant since there are no pezzo with "names"
98
+ records, summary, keys = self.driver.execute_query(
99
+ """
100
+ MATCH (ph:PHerc {name: $ph})-[:HAS]->(:Cornice)
101
+ -[:HAS]->(pz:Pezzo {name: $pz})
102
+ RETURN ph.human_name, pz.human_name
103
+ """, ph=pherc, pz=pezzo,
104
+ database_="neo4j",
105
+ )
106
+ if records:
107
+ pherc_n = records[0]["ph.human_name"]
108
+ pezzo_n = records[0]["pz.human_name"]
109
+
110
+ if not pherc_n:
111
+ # If there was neither cornice nor pezzo names given
112
+ records, summary, keys = self.driver.execute_query(
113
+ """
114
+ MATCH (ph:PHerc {name: $ph})
115
+ RETURN ph.human_name
116
+ """, ph=pherc,
117
+ database_="neo4j",
118
+ )
119
+ if records:
120
+ pherc_n = records[0]["ph.human_name"]
121
+
122
+ return pherc_n, corn_n, pezzo_n
123
+
124
+ def list_cornici_pezzi(self, pherc):
125
+ # Use display names
126
+ records, summary, keys = self.driver.execute_query(
127
+ """
128
+ MATCH (ph:PHerc {human_name: $ph})
129
+ OPTIONAL MATCH (ph)-[:HAS]-(cr:Cornice)
130
+ OPTIONAL MATCH (cr)-[:HAS]-(pz:Pezzo)
131
+ RETURN ph, cr, pz
132
+ """, ph=pherc,
133
+ database_="neo4j",
134
+ )
135
+ return records
136
+
137
+ def find_datasets(self, ds_type: DatasetType, pherc, cornice=None,
138
+ pezzo=None):
139
+ # Use display names
140
+ if cornice:
141
+ records, summary, keys = self.driver.execute_query(
142
+ """
143
+ MATCH (ph:PHerc {name: $ph})-[:HAS]-(cr:Cornice {name: $cor})
144
+ MATCH (cr)<-[:ASSIGNED_TO]-(e:EduceLabID)
145
+ MATCH (e)<-[:BELONGS_TO]-(n)
146
+ WHERE $data_t IN LABELS(n)
147
+ RETURN n
148
+ """, data_t=str(ds_type), ph=pherc, cor=cornice,
149
+ database_="neo4j",
150
+ )
151
+
152
+ else:
153
+ # Pezzo
154
+ records, summary, keys = self.driver.execute_query(
155
+ """
156
+ MATCH (ph:PHerc {name: $ph})-[:HAS]->(:Cornice)
157
+ -[:HAS]->(pz:Pezzo {human_name: $pz})
158
+ MATCH (pz)<-[:ASSIGNED_TO]-(e:EduceLabID)
159
+ MATCH (e)<-[:BELONGS_TO]-(n)
160
+ WHERE $data_t IN LABELS(n)
161
+ RETURN n
162
+ """, data_t=str(ds_type), ph=pherc, pz=pezzo,
163
+ database_="neo4j",
164
+ )
165
+
166
+ properties = []
167
+ for record in records:
168
+ dataset = record[0]
169
+ properties.append(dict(dataset))
170
+
171
+ return properties
172
+
173
+
174
+ def connect(uri=None, user=None, password=None) -> GraphDBConnection:
175
+ # use system config values if not provided
176
+ from educelab.hercdb import config
177
+ if uri is None:
178
+ uri = config.uri
179
+ if user is None:
180
+ user = config.username
181
+ if password is None:
182
+ password = config.password
183
+
184
+ # open new connection
185
+ db = GraphDBConnection(uri, user, password)
186
+ return db
@@ -0,0 +1,80 @@
1
+ import logging
2
+ import os
3
+ import sys
4
+ from functools import lru_cache
5
+ from getpass import getpass, GetPassWarning
6
+ from pathlib import Path
7
+
8
+ from prompt_toolkit import prompt
9
+
10
+
11
+ @lru_cache(maxsize=2)
12
+ def _load_config():
13
+ """Load ~/.educedb as a dictionary."""
14
+ logger = logging.getLogger('educelab.hercdb')
15
+ cfg_path = Path.home() / '.educedb'
16
+ if not cfg_path.exists():
17
+ return None
18
+
19
+ if sys.version_info < (3, 11):
20
+ logger.debug('config backend: configparser')
21
+ import configparser
22
+ with cfg_path.open('r') as f:
23
+ cfg = configparser.ConfigParser()
24
+ cfg.read_file(f)
25
+ cfg = {s: dict(cfg.items(s)) for s in cfg.sections()}
26
+
27
+ else:
28
+ logger.debug('config backend: tomllib')
29
+ import tomllib
30
+ with cfg_path.open('rb') as f:
31
+ cfg = tomllib.load(f)
32
+
33
+ # return first section if present
34
+ if isinstance(next(iter(cfg.values())), dict):
35
+ cfg = next(iter(cfg.values()))
36
+
37
+ return cfg
38
+
39
+
40
+ def _get_cfg_val(env_key, config_key):
41
+ """Return a config value from the environment or config file."""
42
+ # Prefer env val
43
+ val = os.getenv(env_key, None)
44
+ if val is not None:
45
+ return val
46
+
47
+ # Load config
48
+ cfg = _load_config()
49
+ if cfg is None:
50
+ _load_config.cache_clear()
51
+ return None
52
+
53
+ return cfg.get(config_key, None)
54
+
55
+
56
+ def __getattr__(name):
57
+ """Stub to avoid attribute errors on this module"""
58
+ pass
59
+
60
+
61
+ # Default properties #
62
+ uri = _get_cfg_val('EDUCEDB_URI', 'uri')
63
+ username = _get_cfg_val('EDUCEDB_USER', 'username')
64
+ password = _get_cfg_val('EDUCEDB_PASSWORD', 'password')
65
+
66
+
67
+ def request_required():
68
+ """Prompt the user to provide required configuration information."""
69
+ global uri, username, password
70
+ if uri is None:
71
+ uri = prompt('Enter URI: ')
72
+
73
+ if username is None:
74
+ username = prompt('Enter username: ')
75
+
76
+ if password is None:
77
+ try:
78
+ password = getpass('Enter password: ')
79
+ except GetPassWarning:
80
+ pass
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.1
2
+ Name: educelab-hercdb
3
+ Version: 0.1.0
4
+ Summary: Graph database API for Herculaneum data
5
+ Author-email: Mami Hayashida <mami.hayashida@uky.edu>, Seth Parker <c.seth.parker@uky.edu>
6
+ Project-URL: Repository, https://gitlab.com/educelab/educelab-hercdb
7
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Python: >=3.10
14
+ Description-Content-Type: text/markdown
15
+ Requires-Dist: neo4j>=5.20
16
+ Requires-Dist: numpy>=2.0
17
+ Requires-Dist: pandas>=2.2
18
+ Requires-Dist: prompt-toolkit
19
+
20
+ # EduceLab Herculaneum Graph Database API
21
+
22
+ This API is considered a work in progress and can change at any moment.
23
+
24
+ ## Installation
25
+
26
+ The latest release is available on PyPI:
27
+
28
+ ```shell
29
+ python3 -m pip install educelab-hercdb
30
+ ```
31
+
32
+ ## Connect to a server
33
+
34
+ ```python
35
+ from educelab import hercdb
36
+
37
+ uri = "neo4j://localhost:7687"
38
+ user = "foo"
39
+ password = "bar"
40
+ db = hercdb.connect(uri, user, password)
41
+ if db.verify_connection():
42
+ print("Connected!")
43
+ ```
44
+
45
+ ### Server configuration
46
+
47
+ If not provided when calling `hercdb.connect()`, this package will attempt to
48
+ read the URI, username, and password from the configuration file at `~/.hercdb`.
49
+ This file is expected to be in the [TOML](https://toml.io/) format:
50
+ ```toml
51
+ [database]
52
+ uri = "neo4j://localhost:7687"
53
+ username = "foo"
54
+ password = "bar"
55
+ ```
56
+ The section header is optional, and only the information from the first section
57
+ will be read. In the future, sections may be used to differentiate multiple
58
+ database servers. **Note: In Python 3.10, the configuration file is loaded
59
+ using `configparser`, which does not support the full TOML syntax.**
60
+
61
+ Alternatively, the server information can be provided by exporting the following
62
+ environment variables:
63
+ ```shell
64
+ export EDUCEDB_URI='neo4j://localhost:7687'
65
+ export EDUCEDB_USER=foo
66
+ export EDUCEDB_PASSWORD=bar
67
+ ```
68
+ Environment variables take priority over the configuration file.
69
+
70
+ As a convenience, this package provides the `hercdb.config.request_required()`
71
+ method, which will check for configuration values in the environment and
72
+ the configuration file and prompt for any which have not been provided:
73
+ ```
74
+ >>> hercdb.config.request_required()
75
+
76
+ Enter URI: neo4j://localhost:7687
77
+ Enter username: foo
78
+ Enter password:
79
+ ```
@@ -0,0 +1,11 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/educelab/hercdb/__init__.py
4
+ src/educelab/hercdb/api.py
5
+ src/educelab/hercdb/config.py
6
+ src/educelab_hercdb.egg-info/PKG-INFO
7
+ src/educelab_hercdb.egg-info/SOURCES.txt
8
+ src/educelab_hercdb.egg-info/dependency_links.txt
9
+ src/educelab_hercdb.egg-info/entry_points.txt
10
+ src/educelab_hercdb.egg-info/requires.txt
11
+ src/educelab_hercdb.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ el-hercdb-search = educelab.hercdb.apps.search:main
@@ -0,0 +1,4 @@
1
+ neo4j>=5.20
2
+ numpy>=2.0
3
+ pandas>=2.2
4
+ prompt-toolkit