diff --git a/.github/workflows/python_actions.yaml b/.github/workflows/python_actions.yaml index 754c2db..f088f7e 100644 --- a/.github/workflows/python_actions.yaml +++ b/.github/workflows/python_actions.yaml @@ -32,17 +32,15 @@ jobs: --health-timeout 5s --health-retries 5 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v7 + - uses: actions/setup-python@v5 with: - python-version: '3.8' + python-version: '3.12' - name: Install dependencies run: | - python -m pip install --upgrade wheel setuptools==57 pip - pip install -r requirements.txt - pip install -r dev-requirements.txt - + pip install ".[dev]" + - name: Run unittests run: | py.test \ No newline at end of file diff --git a/config.py b/config.py index db242f5..b65559f 100644 --- a/config.py +++ b/config.py @@ -14,7 +14,7 @@ IMAGE_PROXY_RETRIES = 1 # Number of retries for failed upstream image requests (Cantaloupe cold-cache) IMAGE_PROXY_RETRY_DELAY = 2 # Seconds to wait between retries -SQLALCHEMY_DATABASE_URI = 'postgres://scan_explorer:scan_explorer@postgres_service/scan_explorer_service' +SQLALCHEMY_DATABASE_URI = 'postgresql+psycopg2://scan_explorer:scan_explorer@postgres_service/scan_explorer_service' SQLALCHEMY_TRACK_MODIFICATIONS = False OPEN_SEARCH_URL = 'http://opensearch-node1:9200' diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index eac0dd2..0000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -Flask-Testing==0.8.1 -coverage==5.2.1 -testing.postgresql==1.3.0 -pytest==7.1.2 -pytest-cov==3.0.0 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..dbf3eae --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,54 @@ +[project] +name = "scan_explorer_service" +version = "0.0.1" +description = "ADS Scan Explorer Service" +authors = [{ name = "ADS Team", email = "adshelp@cfa.harvard.edu" }] +license = { text = "MIT" } +readme = "README.md" +dependencies = [ + "adsmutils @ git+https://github.com/adsabs/ADSMicroserviceUtils.git@v2.0.0", + "Flask-Limiter==3.8.0", + "Flask-Compress==1.24", + "iiif-prezi==0.3.0", + "SQLAlchemy-Utils==0.42.1", + "Jinja2==3.1.6", + "markupsafe==3.0.3", + "itsdangerous==2.2.0", + "werkzeug==2.3.8", + "psycopg2-binary==2.9.12", + "opensearch-py==3.2.0", + "alembic==1.19.1", + "img2pdf==0.6.3", + "appmap==3.0.1", + "boto3==1.43.67", + "redis==5.0.8" +] + +[project.optional-dependencies] +dev = [ + "Flask-Testing==0.8.1", + "testing.postgresql==1.3.0", + "pytest==9.1.1", + "pytest-cov==7.1.0", + "coverage==7.15.4" +] + +[build-system] +requires = ["setuptools>=62.0.0", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.setuptools.packages.find] +include = ["scan_explorer_service*"] +exclude = ["scan_explorer_service.tests*"] + +[tool.pytest.ini_options] +addopts = "--cov=scan_explorer_service --cov-report=term-missing" +testpaths = ["scan_explorer_service/tests"] +pythonpath = ["."] + +[tool.black] +line-length = 88 +target-version = ['py312'] + +[tool.isort] +profile = "black" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 82529c0..0000000 --- a/requirements.txt +++ /dev/null @@ -1,17 +0,0 @@ -git+https://github.com/adsabs/ADSMicroserviceUtils.git@v1.2.1 -Flask-Limiter==1.4 -iiif-prezi==0.3.0 -SQLAlchemy-Utils==0.38.2 -Flask-Compress==1.12 -Jinja2==2.11.3 -MarkupSafe==2.0.1 -itsdangerous==2.0.1 -Werkzeug==2.0.3 -psycopg2-binary==2.9.3 -opensearch-py==2.0.0 -setuptools<58 -alembic==1.8.0 -img2pdf==0.4.4 -appmap>=1.1.0.dev0 -boto3==1.34.75 -redis==4.6.0 diff --git a/scan_explorer_service/models.py b/scan_explorer_service/models.py index 91c1864..00cf751 100644 --- a/scan_explorer_service/models.py +++ b/scan_explorer_service/models.py @@ -1,7 +1,6 @@ from flask import current_app -from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column, ForeignKey, Integer, String, Table, UniqueConstraint, Enum, Index, or_ -from sqlalchemy.orm import relationship +from sqlalchemy.orm import declarative_base, relationship from sqlalchemy_utils.models import Timestamp from scan_explorer_service.utils.utils import url_for_proxy import enum diff --git a/scan_explorer_service/tests/test_metadata.py b/scan_explorer_service/tests/test_metadata.py index dc96c6b..26e7a9e 100644 --- a/scan_explorer_service/tests/test_metadata.py +++ b/scan_explorer_service/tests/test_metadata.py @@ -317,7 +317,7 @@ def setUp(self): def test_collection_serialized_no_pages(self): """S3: Collection.serialized returns thumbnail=None when no pages exist.""" with self.app.app_context(): - col = self.app.db.session.query(Collection).get(self.collection_id) + col = self.app.db.session.get(Collection, self.collection_id) data = col.serialized self.assertIsNone(data['thumbnail']) self.assertEqual(data['pages'], 0) @@ -325,7 +325,7 @@ def test_collection_serialized_no_pages(self): def test_article_serialized_no_pages(self): """S3: Article.serialized returns thumbnail=None when no pages exist.""" with self.app.app_context(): - art = self.app.db.session.query(Article).get(self.article_id) + art = self.app.db.session.get(Article, self.article_id) data = art.serialized self.assertIsNone(data['thumbnail']) self.assertEqual(data['pages'], 0) diff --git a/scan_explorer_service/utils/search_utils.py b/scan_explorer_service/utils/search_utils.py index 631485c..a0c0442 100644 --- a/scan_explorer_service/utils/search_utils.py +++ b/scan_explorer_service/utils/search_utils.py @@ -55,7 +55,7 @@ class OrderOptions(str, enum.Enum): def parse_query_args(args): """Parse HTTP request args into a search query string, field dict, pagination, and sort order.""" - qs = re.sub(':\s*', ':', args.get('q', '', str)) + qs = re.sub(r':\s*', ':', args.get('q', '', str)) if not qs or not qs.strip(): raise ValueError('Query string is required') @@ -88,7 +88,7 @@ def parse_query_string(qs): check_query(qs_dict) #Adds a () around each free search to force OS to look for each individual entry against all default fields - for parameter in re.split('\s+', qs_only_free): + for parameter in re.split(r'\s+', qs_only_free): if parameter.upper() not in ['AND', 'OR', '']: qs = qs.replace(str(parameter), "(" + str(parameter) + ")") diff --git a/scan_explorer_service/views/image_proxy.py b/scan_explorer_service/views/image_proxy.py index 166c146..e00f326 100644 --- a/scan_explorer_service/views/image_proxy.py +++ b/scan_explorer_service/views/image_proxy.py @@ -182,7 +182,7 @@ def fetch_article(item, memory_limit): return send_file( file_stream, as_attachment=True, - attachment_filename=object_name, + download_name=object_name, mimetype='application/pdf' ) except Exception as e: