diff --git a/.flaskenv b/.flaskenv new file mode 100644 index 0000000..7809a84 --- /dev/null +++ b/.flaskenv @@ -0,0 +1,4 @@ +FLASK_APP=pypnnomenclature:create_app +FLASK_ENV=development +FLASK_DEBUG=1 +NOMENCLATURE_SETTINGS=settings.py diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml new file mode 100644 index 0000000..7d324a9 --- /dev/null +++ b/.github/workflows/pytest.yml @@ -0,0 +1,84 @@ +name: pytest + +on: + push: + branches: + - master + - develop + pull_request: + branches: + - master + - develop + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + include: + - name: "Debian 10" + python-version: "3.7" + postgres-version: "11-stretch" + - name: "Debian 11" + python-version: "3.9" + postgres-version: "13-bullseye" + + name: ${{ matrix.name }} + + services: + postgres: + image: postgres:${{ matrix.postgres-version }} + env: + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + steps: + - name: Add database extensions + run: | + psql -h localhost -U postgres -d postgres -tc 'CREATE EXTENSION "uuid-ossp";' + psql -h localhost -U postgres -d postgres -tc 'CREATE EXTENSION "pg_trgm";' + env: + PGPASSWORD: postgres + - uses: actions/checkout@v2 + with: + submodules: recursive + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install \ + pytest-cov \ + -e .[tests] \ + -e file:dependencies/Utils-Flask-SQLAlchemy#egg=utils-flask-sqlalchemy \ + -e file:dependencies/TaxHub#egg=taxhub \ + -e file:dependencies/UsersHub-authentification-module#egg=pypnusershub + - name: Install database + run: | + flask db upgrade nomenclatures@head + flask db upgrade nomenclatures_inpn_data@head + flask db upgrade nomenclatures_taxonomie@head + flask db upgrade nomenclatures_taxonomie_inpn_data@head + env: + NOMENCLATURE_SETTINGS: test_settings.py + - name: Test with pytest + run: | + pytest -v --cov --cov-report xml + env: + NOMENCLATURE_SETTINGS: test_settings.py + - name: Upload coverage to Codecov + if: ${{ matrix.name == 'Debian 11' }} + uses: codecov/codecov-action@v2 + with: + flags: pytest diff --git a/.gitignore b/.gitignore index 1e7cad8..01149a5 100644 --- a/.gitignore +++ b/.gitignore @@ -103,3 +103,8 @@ ENV/ # mypy .mypy_cache/ + +*.swp +*.swo + +/*settings.py diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..45e5483 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "dependencies/Utils-Flask-SQLAlchemy"] + path = dependencies/Utils-Flask-SQLAlchemy + url = https://github.com/PnX-SI/Utils-Flask-SQLAlchemy.git +[submodule "dependencies/TaxHub"] + path = dependencies/TaxHub + url = https://github.com/PnX-SI/TaxHub.git +[submodule "dependencies/UsersHub-authentification-module"] + path = dependencies/UsersHub-authentification-module + url = https://github.com/PnX-SI/UsersHub-authentification-module.git diff --git a/README.md b/README.md index a56ca6c..a5525b4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Nomenclature-api-module +[![pytest](https://github.com/PnX-SI/Nomenclature-api-module/actions/workflows/pytest.yml/badge.svg)](https://github.com/PnX-SI/Nomenclature-api-module/actions/workflows/pytest.yml) +[![codecov](https://codecov.io/gh/PnX-SI/Nomenclature-api-module/branch/master/graph/badge.svg?token=KGZRGXFWCK)](https://codecov.io/gh/PnX-SI/Nomenclature-api-module) + Flask (Python) module for Nomenclature API. It is used in [GeoNature](https://github.com/PnX-SI/GeoNature) but can also be used as a standalone API service to manage and returns various nomenclatures with their hierarchy. diff --git a/VERSION b/VERSION index e516bb9..bc80560 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.5 +1.5.0 diff --git a/admin.py b/admin.py deleted file mode 100644 index 56dd7df..0000000 --- a/admin.py +++ /dev/null @@ -1,13 +0,0 @@ -# coding: utf8 - -import os -import sys - -# We then load the all namespaces so that if it's imported in any way, it's -# still available -CURDIR = os.path.dirname(os.path.abspath(__file__)) -SRCDIR = os.path.join(CURDIR, 'src') - -sys.path.append(SRCDIR) - -from pypnnomenclature.admin import * # noqa \ No newline at end of file diff --git a/dependencies/TaxHub b/dependencies/TaxHub new file mode 160000 index 0000000..11fbe53 --- /dev/null +++ b/dependencies/TaxHub @@ -0,0 +1 @@ +Subproject commit 11fbe53c8f2f969a18cf358d1910ce93581c80f6 diff --git a/dependencies/UsersHub-authentification-module b/dependencies/UsersHub-authentification-module new file mode 160000 index 0000000..699efc1 --- /dev/null +++ b/dependencies/UsersHub-authentification-module @@ -0,0 +1 @@ +Subproject commit 699efc1b04b41c0706b2824a30f0d46d99db17ab diff --git a/dependencies/Utils-Flask-SQLAlchemy b/dependencies/Utils-Flask-SQLAlchemy new file mode 160000 index 0000000..df44a22 --- /dev/null +++ b/dependencies/Utils-Flask-SQLAlchemy @@ -0,0 +1 @@ +Subproject commit df44a226b127c3ab948913fa2e65472926fcb585 diff --git a/docs/changelog.rst b/docs/changelog.rst index 4177c2f..c402443 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,25 @@ CHANGELOG ========= +1.5.0 (2022-01-04) +------------------ + +**🚀 NouveautĂ©s** + +* PossibilitĂ© de lancer l’API Nomenclature de maniĂšre autonome +* PossibilitĂ© de crĂ©er son schĂ©ma de base de donnĂ©es de maniĂšre autonome +* Mise en place des tests unitaires +* Mise en place de l’intĂ©gration continue +* IntĂ©gration des dĂ©pendances en tant que sous-module Git + + * Utils-Flask-SQLAlchemy + * TaxHub (pour la taxonomie) + * UsersHub-authentification-module (car requis par TaxHub) + +**🐛 Corrections** + +* Suppression d’anciens fichiers devenus inutiles suite au paquetage + 1.4.5 (2021-01-03) ------------------ diff --git a/install_app.sh b/install_app.sh deleted file mode 100755 index ce8d624..0000000 --- a/install_app.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash - -. settings.ini - -#Installation du virtual env -echo "Installation du virtual env..." -virtualenv venv - -if [[ $python_path ]]; then - virtualenv -p $python_path venv -fi - -source venv/bin/activate -pip install -r requirements.txt -deactivate diff --git a/models.py b/models.py deleted file mode 100644 index a0ba2c1..0000000 --- a/models.py +++ /dev/null @@ -1,17 +0,0 @@ -# coding: utf8 - - -from __future__ import (unicode_literals, print_function, - absolute_import, division) - -import os -import sys - -# We then load the all namespaces so that if it's imported in any way, it's -# still available -CURDIR = os.path.dirname(os.path.abspath(__file__)) -SRCDIR = os.path.join(CURDIR, 'src') - -sys.path.append(SRCDIR) - -from pypnnomenclature.models import * # noqa diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8f60e6c --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,14 @@ +[tool.pytest.ini_options] +minversion = "6.0" +testpaths = [ + "src", +] + +[tool.coverage.run] +source = [ + "pypnnomenclature", +] +omit = [ + "*/tests/*", + "*/migrations/*", +] diff --git a/repository.py b/repository.py deleted file mode 100644 index b8a6eb4..0000000 --- a/repository.py +++ /dev/null @@ -1,17 +0,0 @@ -# coding: utf8 - - -from __future__ import (unicode_literals, print_function, - absolute_import, division) - -import os -import sys - -# We then load the all namespaces so that if it's imported in any way, it's -# still available -CURDIR = os.path.dirname(os.path.abspath(__file__)) -SRCDIR = os.path.join(CURDIR, 'src') - -sys.path.append(SRCDIR) - -from pypnnomenclature.repository import * # noqa diff --git a/requirements.in b/requirements.in index c9307bb..d433862 100755 --- a/requirements.in +++ b/requirements.in @@ -1,7 +1,10 @@ +python-dotenv flask Flask-Admin flask-sqlalchemy +flask-migrate psycopg2 -utils-flask-sqlalchemy +utils-flask-sqlalchemy>=0.2.6 flask-marshmallow +marshmallow-sqlalchemy diff --git a/routes.py b/routes.py deleted file mode 100644 index d3433a7..0000000 --- a/routes.py +++ /dev/null @@ -1,17 +0,0 @@ -# coding: utf8 - - -from __future__ import (unicode_literals, print_function, - absolute_import, division) - -import os -import sys - -# We then load the all namespaces so that if it's imported in any way, it's -# still available -CURDIR = os.path.dirname(os.path.abspath(__file__)) -SRCDIR = os.path.join(CURDIR, 'src') - -sys.path.append(SRCDIR) - -from pypnnomenclature.routes import * # noqa diff --git a/server.py b/server.py deleted file mode 100644 index 6501433..0000000 --- a/server.py +++ /dev/null @@ -1,30 +0,0 @@ -# coding: utf8 -from flask import Flask, request -from flask_admin import Admin -import importlib -import datetime -from src.pypnnomenclature.env import DB -app_globals = {} - - -def init_app(): - if app_globals.get('app', False): - app = app_globals['app'] - else: - app = Flask(__name__) - app.config.from_pyfile('config.py') - DB.init_app(app) - - with app.app_context(): - from routes import routes - app.register_blueprint(routes, url_prefix='/nomenclatures') - - # import admin - - from admin import admin - return app - - -app = init_app() -if __name__ == '__main__': - app.run() diff --git a/config.py.sample b/settings.py.sample similarity index 81% rename from config.py.sample rename to settings.py.sample index c109a05..5d9bfeb 100644 --- a/config.py.sample +++ b/settings.py.sample @@ -13,7 +13,7 @@ COOKIE_EXPIRATION = 3600 COOKIE_AUTORENEW = True # Ajouter les filtres taxonomiques Ă  l'API -# NĂ©cessite d'avoir exĂ©cutĂ© le script 'data/nomenclature_taxonomie.sql' qui installe les tables nĂ©cessaires +# NĂ©cessite d'avoir montĂ© la branche alembic nomenclature_taxonomie ENABLE_NOMENCLATURE_TAXONOMIC_FILTERS = True # URL de l'interface d'administration diff --git a/setup.py b/setup.py index 94f37c2..2677c00 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,7 @@ with (root_dir / 'requirements.in').open() as f: requirements = f.read().splitlines() + setuptools.setup( name='pypnnomenclature', version=version, @@ -23,6 +24,9 @@ package_dir={'': 'src'}, package_data={'pypnnomenclature.migrations': ['data/*.sql']}, install_requires=requirements, + extras_require={ + 'tests': [ 'pytest', 'pytest-flask', ], + }, entry_points={ 'alembic': [ 'migrations = pypnnomenclature.migrations:versions', diff --git a/src/pypnnomenclature/__init__.py b/src/pypnnomenclature/__init__.py index a82b376..51148df 100644 --- a/src/pypnnomenclature/__init__.py +++ b/src/pypnnomenclature/__init__.py @@ -1 +1,33 @@ -__version__ = "1.1.1" +from pathlib import Path +from itertools import chain +from pkg_resources import iter_entry_points + +from flask import Flask +from flask_migrate import Migrate + +from pypnnomenclature.env import db, ma +from pypnnomenclature.routes import routes + + +migrate = Migrate() + + +@migrate.configure +def configure_alembic(alembic_config): + version_locations = alembic_config.get_main_option('version_locations', default='').split() + for entry_point in chain(iter_entry_points('alembic', 'migrations')): + _, migrations = str(entry_point).split('=', 1) + version_locations += [ migrations.strip() ] + alembic_config.set_main_option('version_locations', ' '.join(version_locations)) + return alembic_config + + +def create_app(): + app = Flask('Habref') + app.config.from_envvar('NOMENCLATURE_SETTINGS') + ma.init_app(app) + db.init_app(app) + migrate.init_app(app, db, directory=Path(__file__).parent / 'migrations') + app.register_blueprint(routes, url_prefix='/nomenclatures') + # TODO admin + return app diff --git a/src/pypnnomenclature/admin.py b/src/pypnnomenclature/admin.py index f8b717e..b51c7d7 100644 --- a/src/pypnnomenclature/admin.py +++ b/src/pypnnomenclature/admin.py @@ -8,7 +8,7 @@ TNomenclaturesAdmin, BibNomenclaturesTypesAdmin ) -from .env import DB +from .env import db # https://github.com/flask-admin/flask-admin/issues/1807 @@ -38,7 +38,7 @@ def get_dynamic_options(self, view): g.TNomenclatureFiltersType = [ (nomenclature.id_type, nomenclature.label_default) for nomenclature - in DB.session.query(BibNomenclaturesTypesAdmin).order_by(BibNomenclaturesTypesAdmin.label_default) # noqa + in db.session.query(BibNomenclaturesTypesAdmin).order_by(BibNomenclaturesTypesAdmin.label_default) # noqa ] yield from g.TNomenclatureFiltersType @@ -63,7 +63,7 @@ def get_dynamic_options(self, view): g.TNomenclatureFiltersMnemonique = [ (nomenclature.id_type, nomenclature.mnemonique) for nomenclature - in DB.session.query(BibNomenclaturesTypesAdmin).order_by(BibNomenclaturesTypesAdmin.mnemonique) # noqa + in db.session.query(BibNomenclaturesTypesAdmin).order_by(BibNomenclaturesTypesAdmin.mnemonique) # noqa ] yield from g.TNomenclatureFiltersMnemonique @@ -136,7 +136,7 @@ def get_dynamic_options(self, view): g.BibNomenclatureFiltersLabel = [ (nomenclature.label_default, nomenclature.label_default) for nomenclature - in DB.session.query(BibNomenclaturesTypesAdmin).order_by(BibNomenclaturesTypesAdmin.label_default) # noqa + in db.session.query(BibNomenclaturesTypesAdmin).order_by(BibNomenclaturesTypesAdmin.label_default) # noqa ] yield from g.BibNomenclatureFiltersLabel @@ -169,7 +169,7 @@ def get_dynamic_options(self, view): g.BibNomenclatureFiltersMnemonique = [ (nomenclature.mnemonique, nomenclature.mnemonique) for nomenclature - in DB.session.query(BibNomenclaturesTypesAdmin).order_by(BibNomenclaturesTypesAdmin.mnemonique) # noqa + in db.session.query(BibNomenclaturesTypesAdmin).order_by(BibNomenclaturesTypesAdmin.mnemonique) # noqa ] yield from g.BibNomenclatureFiltersMnemonique diff --git a/src/pypnnomenclature/env.py b/src/pypnnomenclature/env.py index 6fd3bfd..05b3838 100644 --- a/src/pypnnomenclature/env.py +++ b/src/pypnnomenclature/env.py @@ -11,17 +11,17 @@ if db_path: db_module_name, db_object_name = db_path.rsplit('.', 1) db_module = import_module(db_module_name) - DB = getattr(db_module, db_object_name) + db = getattr(db_module, db_object_name) else: - DB = SQLAlchemy() + db = SQLAlchemy() marsmallow_path = environ.get('FLASK_MARSHMALLOW') if marsmallow_path: ma_module_name, ma_object_name = marsmallow_path.rsplit('.', 1) ma_module = import_module(ma_module_name) - MA = getattr(ma_module, ma_object_name) + ma = getattr(ma_module, ma_object_name) else: - MA = Marshmallow() + ma = Marshmallow() -__all__ = ['DB', 'MA'] +__all__ = ['db', 'ma'] diff --git a/src/pypnnomenclature/migrations/alembic.ini b/src/pypnnomenclature/migrations/alembic.ini new file mode 100644 index 0000000..ec9d45c --- /dev/null +++ b/src/pypnnomenclature/migrations/alembic.ini @@ -0,0 +1,50 @@ +# A generic, single database configuration. + +[alembic] +# template used to generate migration files +# file_template = %%(rev)s_%%(slug)s + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + + +# Logging configuration +[loggers] +keys = root,sqlalchemy,alembic,flask_migrate + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARN +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARN +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[logger_flask_migrate] +level = INFO +handlers = +qualname = flask_migrate + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/src/pypnnomenclature/migrations/env.py b/src/pypnnomenclature/migrations/env.py new file mode 100644 index 0000000..68feded --- /dev/null +++ b/src/pypnnomenclature/migrations/env.py @@ -0,0 +1,91 @@ +from __future__ import with_statement + +import logging +from logging.config import fileConfig + +from flask import current_app + +from alembic import context + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +fileConfig(config.config_file_name) +logger = logging.getLogger('alembic.env') + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +# target_metadata = mymodel.Base.metadata +config.set_main_option( + 'sqlalchemy.url', + str(current_app.extensions['migrate'].db.get_engine().url).replace( + '%', '%%')) +target_metadata = current_app.extensions['migrate'].db.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline(): + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, target_metadata=target_metadata, literal_binds=True + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online(): + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + connectable = current_app.extensions['migrate'].db.get_engine() + + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/src/pypnnomenclature/migrations/script.py.mako b/src/pypnnomenclature/migrations/script.py.mako new file mode 100644 index 0000000..2c01563 --- /dev/null +++ b/src/pypnnomenclature/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/src/pypnnomenclature/models.py b/src/pypnnomenclature/models.py index af8a182..06162cc 100644 --- a/src/pypnnomenclature/models.py +++ b/src/pypnnomenclature/models.py @@ -1,7 +1,3 @@ - -# coding: utf8 -from __future__ import (unicode_literals, print_function, - absolute_import, division) from importlib import import_module from flask import current_app from sqlalchemy import ForeignKey @@ -9,24 +5,24 @@ from sqlalchemy.sql import select, func from utils_flask_sqla.serializers import serializable -from .env import DB +from .env import db @serializable -class CorTaxrefNomenclature(DB.Model): +class CorTaxrefNomenclature(db.Model): """ Relation entre taxonomie et nomenclature. A n'utiliser uniquement lorsque que l'extension 'taxonomie' des nomenclatures est installĂ©e """ __tablename__ = 'cor_taxref_nomenclature' __table_args__ = {'schema': 'ref_nomenclatures'} - id_nomenclature = DB.Column( - DB.Integer, + id_nomenclature = db.Column( + db.Integer, ForeignKey('ref_nomenclatures.t_nomenclatures.id_nomenclature'), primary_key=True ) - regne = DB.Column(DB.Unicode, primary_key=True) - group2_inpn = DB.Column(DB.Unicode, primary_key=True) + regne = db.Column(db.Unicode, primary_key=True) + group2_inpn = db.Column(db.Unicode, primary_key=True) @serializable(exclude=[ @@ -41,36 +37,36 @@ class CorTaxrefNomenclature(DB.Model): "meta_create_date", "meta_update_date", ]) -class TNomenclatures(DB.Model): +class TNomenclatures(db.Model): __tablename__ = 't_nomenclatures' __table_args__ = {'schema': 'ref_nomenclatures'} - id_nomenclature = DB.Column(DB.Integer, primary_key=True) - id_type = DB.Column( - DB.Integer, + id_nomenclature = db.Column(db.Integer, primary_key=True) + id_type = db.Column( + db.Integer, ForeignKey('ref_nomenclatures.bib_nomenclatures_types.id_type') ) nomenclature_type = relationship('BibNomenclaturesTypes', backref="nomenclatures") - cd_nomenclature = DB.Column(DB.Unicode) - mnemonique = DB.Column(DB.Unicode) - label_default = DB.Column(DB.Unicode) - definition_default = DB.Column(DB.Unicode) - label_fr = DB.Column(DB.Unicode) - definition_fr = DB.Column(DB.Unicode) - label_en = DB.Column(DB.Unicode) - definition_en = DB.Column(DB.Unicode) - label_es = DB.Column(DB.Unicode) - definition_es = DB.Column(DB.Unicode) - label_de = DB.Column(DB.Unicode) - definition_de = DB.Column(DB.Unicode) - label_it = DB.Column(DB.Unicode) - definition_it = DB.Column(DB.Unicode) - source = DB.Column(DB.Unicode) - statut = DB.Column(DB.Unicode) - id_broader = DB.Column(DB.Integer) - hierarchy = DB.Column(DB.Unicode) - active = DB.Column(DB.BOOLEAN) - meta_create_date = DB.Column(DB.DateTime) - meta_update_date = DB.Column(DB.DateTime) + cd_nomenclature = db.Column(db.Unicode) + mnemonique = db.Column(db.Unicode) + label_default = db.Column(db.Unicode) + definition_default = db.Column(db.Unicode) + label_fr = db.Column(db.Unicode) + definition_fr = db.Column(db.Unicode) + label_en = db.Column(db.Unicode) + definition_en = db.Column(db.Unicode) + label_es = db.Column(db.Unicode) + definition_es = db.Column(db.Unicode) + label_de = db.Column(db.Unicode) + definition_de = db.Column(db.Unicode) + label_it = db.Column(db.Unicode) + definition_it = db.Column(db.Unicode) + source = db.Column(db.Unicode) + statut = db.Column(db.Unicode) + id_broader = db.Column(db.Integer) + hierarchy = db.Column(db.Unicode) + active = db.Column(db.BOOLEAN) + meta_create_date = db.Column(db.DateTime) + meta_update_date = db.Column(db.DateTime) @staticmethod def get_default_nomenclature(mnemonique, id_organism=0): @@ -79,7 +75,7 @@ def get_default_nomenclature(mnemonique, id_organism=0): mnemonique, id_organism ).label('default') ]) - result = DB.session.execute(q) + result = db.session.execute(q) return result.fetchone()['default'] @@ -94,27 +90,27 @@ class TNomenclatureTaxonomy(TNomenclatures): @serializable -class BibNomenclaturesTypes(DB.Model): +class BibNomenclaturesTypes(db.Model): __tablename__ = 'bib_nomenclatures_types' __table_args__ = {'schema': 'ref_nomenclatures'} - id_type = DB.Column(DB.Integer, primary_key=True) - mnemonique = DB.Column(DB.Unicode) - label_default = DB.Column(DB.Unicode) - definition_default = DB.Column(DB.Unicode) - label_fr = DB.Column(DB.Unicode) - definition_fr = DB.Column(DB.Unicode) - label_en = DB.Column(DB.Unicode) - definition_en = DB.Column(DB.Unicode) - label_es = DB.Column(DB.Unicode) - definition_es = DB.Column(DB.Unicode) - label_de = DB.Column(DB.Unicode) - definition_de = DB.Column(DB.Unicode) - label_it = DB.Column(DB.Unicode) - definition_it = DB.Column(DB.Unicode) - source = DB.Column(DB.Unicode) - statut = DB.Column(DB.Unicode) - meta_create_date = DB.Column(DB.DateTime) - meta_update_date = DB.Column(DB.DateTime) + id_type = db.Column(db.Integer, primary_key=True) + mnemonique = db.Column(db.Unicode) + label_default = db.Column(db.Unicode) + definition_default = db.Column(db.Unicode) + label_fr = db.Column(db.Unicode) + definition_fr = db.Column(db.Unicode) + label_en = db.Column(db.Unicode) + definition_en = db.Column(db.Unicode) + label_es = db.Column(db.Unicode) + definition_es = db.Column(db.Unicode) + label_de = db.Column(db.Unicode) + definition_de = db.Column(db.Unicode) + label_it = db.Column(db.Unicode) + definition_it = db.Column(db.Unicode) + source = db.Column(db.Unicode) + statut = db.Column(db.Unicode) + meta_create_date = db.Column(db.DateTime) + meta_update_date = db.Column(db.DateTime) def __repr__(self): return self.label_default @@ -126,7 +122,7 @@ def get_default_nomenclature(mnemonique, id_organism=0): mnemonique, id_organism ).label('default') ]) - result = DB.session.execute(q) + result = db.session.execute(q) return result.fetchone()['default'] @@ -145,40 +141,40 @@ class BibNomenclaturesTypeTaxo(BibNomenclaturesTypes): # ModĂšle utilisĂ© seulement si l'extension 'taxonomie' # du module est activĂ©e et installĂ©e @serializable -class VNomenclatureTaxonomie(DB.Model): +class VNomenclatureTaxonomie(db.Model): __tablename__ = 'v_nomenclature_taxonomie' __table_args__ = {'schema': 'ref_nomenclatures'} - id_type = DB.Column(DB.Integer) - type_label = DB.Column(DB.Unicode) - type_definition = DB.Column(DB.Unicode) - type_label_fr = DB.Column(DB.Unicode) - type_definition_fr = DB.Column(DB.Unicode) - type_label_en = DB.Column(DB.Unicode) - type_definition_en = DB.Column(DB.Unicode) - type_label_es = DB.Column(DB.Unicode) - type_definition_es = DB.Column(DB.Unicode) - type_label_de = DB.Column(DB.Unicode) - type_definition_de = DB.Column(DB.Unicode) - type_label_it = DB.Column(DB.Unicode) - type_definition_it = DB.Column(DB.Unicode) - regne = DB.Column(DB.Unicode, primary_key=True) - group2_inpn = DB.Column(DB.Unicode, primary_key=True) - id_nomenclature = DB.Column(DB.Integer, primary_key=True) - mnemonique = DB.Column(DB.Unicode) - nomenclature_label = DB.Column(DB.Unicode) - nomenclature_definition = DB.Column(DB.Unicode) - nomenclature_label_fr = DB.Column(DB.Unicode) - nomenclature_definition_fr = DB.Column(DB.Unicode) - nomenclature_label_en = DB.Column(DB.Unicode) - nomenclature_definition_en = DB.Column(DB.Unicode) - nomenclature_label_es = DB.Column(DB.Unicode) - nomenclature_definition_es = DB.Column(DB.Unicode) - nomenclature_label_de = DB.Column(DB.Unicode) - nomenclature_definition_de = DB.Column(DB.Unicode) - nomenclature_label_it = DB.Column(DB.Unicode) - nomenclature_definition_it = DB.Column(DB.Unicode) - id_broader = DB.Column(DB.Integer) - hierarchy = DB.Column(DB.Unicode) + id_type = db.Column(db.Integer) + type_label = db.Column(db.Unicode) + type_definition = db.Column(db.Unicode) + type_label_fr = db.Column(db.Unicode) + type_definition_fr = db.Column(db.Unicode) + type_label_en = db.Column(db.Unicode) + type_definition_en = db.Column(db.Unicode) + type_label_es = db.Column(db.Unicode) + type_definition_es = db.Column(db.Unicode) + type_label_de = db.Column(db.Unicode) + type_definition_de = db.Column(db.Unicode) + type_label_it = db.Column(db.Unicode) + type_definition_it = db.Column(db.Unicode) + regne = db.Column(db.Unicode, primary_key=True) + group2_inpn = db.Column(db.Unicode, primary_key=True) + id_nomenclature = db.Column(db.Integer, primary_key=True) + mnemonique = db.Column(db.Unicode) + nomenclature_label = db.Column(db.Unicode) + nomenclature_definition = db.Column(db.Unicode) + nomenclature_label_fr = db.Column(db.Unicode) + nomenclature_definition_fr = db.Column(db.Unicode) + nomenclature_label_en = db.Column(db.Unicode) + nomenclature_definition_en = db.Column(db.Unicode) + nomenclature_label_es = db.Column(db.Unicode) + nomenclature_definition_es = db.Column(db.Unicode) + nomenclature_label_de = db.Column(db.Unicode) + nomenclature_definition_de = db.Column(db.Unicode) + nomenclature_label_it = db.Column(db.Unicode) + nomenclature_definition_it = db.Column(db.Unicode) + id_broader = db.Column(db.Integer) + hierarchy = db.Column(db.Unicode) # Model for Admin diff --git a/src/pypnnomenclature/repository.py b/src/pypnnomenclature/repository.py index 7fbd0be..e0dedfe 100644 --- a/src/pypnnomenclature/repository.py +++ b/src/pypnnomenclature/repository.py @@ -13,7 +13,7 @@ ) from sqlalchemy import text -from .env import DB +from .env import db def get_nomenclature_list( @@ -28,7 +28,7 @@ def get_nomenclature_list( RĂ©cupĂ©ration de la liste des termes d'un type de nomenclature """ - q = DB.session.query(BibNomenclaturesTypes) + q = db.session.query(BibNomenclaturesTypes) if filter_params is None: filter_params = [] @@ -44,7 +44,7 @@ def get_nomenclature_list( # Terme de nomenclatures q = ( - DB.session.query(TNomenclatures) + db.session.query(TNomenclatures) .filter_by(id_type=nomenclature.id_type) .filter_by(active=True) ) @@ -115,7 +115,7 @@ def get_nomenclature_with_taxonomy_list(): Fetch nomenclature definition list with taxonomy """ - q = DB.session.query(BibNomenclaturesTypeTaxo).order_by("mnemonique") + q = db.session.query(BibNomenclaturesTypeTaxo).order_by("mnemonique") nomenclature_types = q.all() data = list() @@ -177,7 +177,7 @@ def get_nomenclature_id_term(cd_type, cd_term, raise_exp=True): t = text("SELECT ref_nomenclatures.get_id_nomenclature(:cd_type, :cd_term) as id") try: value = ( - DB.session.query("id") + db.session.query("id") .from_statement(t.params(cd_type=cd_type, cd_term=cd_term)) .first() ) diff --git a/src/pypnnomenclature/schemas.py b/src/pypnnomenclature/schemas.py index 1edd48c..66dbdb8 100644 --- a/src/pypnnomenclature/schemas.py +++ b/src/pypnnomenclature/schemas.py @@ -1,7 +1,8 @@ -from pypnnomenclature.env import MA +from pypnnomenclature.env import ma from pypnnomenclature.models import TNomenclatures -class NomenclatureSchema(MA.SQLAlchemyAutoSchema): + +class NomenclatureSchema(ma.SQLAlchemyAutoSchema): class Meta: model = TNomenclatures load_instance = True diff --git a/__init__.py b/src/pypnnomenclature/tests/__init__.py similarity index 100% rename from __init__.py rename to src/pypnnomenclature/tests/__init__.py diff --git a/src/pypnnomenclature/tests/conftest.py b/src/pypnnomenclature/tests/conftest.py new file mode 100644 index 0000000..645e440 --- /dev/null +++ b/src/pypnnomenclature/tests/conftest.py @@ -0,0 +1,14 @@ +import pytest + +from pypnnomenclature import create_app +from pypnnomenclature.env import db + + +@pytest.fixture(scope='session') +def app(): + return create_app() + + +@pytest.fixture(scope='session') +def _session(app): + return db.session diff --git a/src/pypnnomenclature/tests/test_nomenclatures.py b/src/pypnnomenclature/tests/test_nomenclatures.py new file mode 100644 index 0000000..45ba0d7 --- /dev/null +++ b/src/pypnnomenclature/tests/test_nomenclatures.py @@ -0,0 +1,12 @@ +import pytest +from flask import url_for + +from utils_flask_sqla.tests.utils import JSONClient + + +@pytest.mark.usefixtures("client_class", "temporary_transaction") +class TestNomenclatures: + def test_nomenclature_with_taxonomy_list(self): + response = self.client.get(url_for("nomenclatures.get_nomenclature_with_taxonomy_list")) + assert response.status_code == 200 + assert len(response.json) > 0 diff --git a/test_settings.py b/test_settings.py new file mode 100644 index 0000000..ad4f595 --- /dev/null +++ b/test_settings.py @@ -0,0 +1,2 @@ +SQLALCHEMY_DATABASE_URI = "postgresql://postgres:postgres@localhost/postgres" +SQLALCHEMY_TRACK_MODIFICATIONS = False