diff --git a/alembic/versions/342dce4f5753_rename_data_providers_to_data_producers.py b/alembic/versions/342dce4f5753_rename_data_providers_to_data_producers.py new file mode 100644 index 0000000..4aa079c --- /dev/null +++ b/alembic/versions/342dce4f5753_rename_data_providers_to_data_producers.py @@ -0,0 +1,32 @@ +"""Rename data_providers to data_producers and rename column in data_submissions + +Revision ID: 342dce4f5753 +Revises: 851709d3a162 +Create Date: 2024-02-14 08:40:16.897824 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = "342dce4f5753" +down_revision: Union[str, None] = "851709d3a162" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.rename_table("data_providers", "data_producers") + op.alter_column( + "data_submissions", "data_provider_id", new_column_name="data_producer_id" + ) + + +def downgrade() -> None: + op.alter_column( + "data_submissions", "data_producer_id", new_column_name="data_provider_id" + ) + op.rename_table("data_producers", "data_providers") diff --git a/alembic/versions/851709d3a162_add_report_column.py b/alembic/versions/851709d3a162_add_report_column.py index cd1f465..cefc2a8 100644 --- a/alembic/versions/851709d3a162_add_report_column.py +++ b/alembic/versions/851709d3a162_add_report_column.py @@ -26,5 +26,5 @@ def upgrade(): ) -def add_column(): +def downgrade(): op.drop_column("data_submissions", "report") diff --git a/documentation/terminology.md b/documentation/terminology.md new file mode 100644 index 0000000..6d8a230 --- /dev/null +++ b/documentation/terminology.md @@ -0,0 +1,14 @@ +# NAD Collaboration Hub Terminology + +## Acronyms + +- GIS: geographic information system +- NAD: National Address Database + +## Key Concepts + +- Data Producer: a representative from a state, local, or tribal government + who wants to submit address data for inclusion in the NAD. + +- Data Submission: a GIS dataset that a Data Producer submits for inclusion + in the NAD. diff --git a/nad_ch/application/interfaces.py b/nad_ch/application/interfaces.py index 8fd2de6..2e6d139 100644 --- a/nad_ch/application/interfaces.py +++ b/nad_ch/application/interfaces.py @@ -1,6 +1,6 @@ from typing import Optional, Protocol from nad_ch.application.dtos import DownloadResult -from nad_ch.domain.repositories import DataProviderRepository, DataSubmissionRepository +from nad_ch.domain.repositories import DataProducerRepository, DataSubmissionRepository class Logger(Protocol): @@ -37,8 +37,8 @@ def run_load_and_validate( class ApplicationContext: @property - def providers(self) -> DataProviderRepository: - return self._providers + def producers(self) -> DataProducerRepository: + return self._producers @property def submissions(self) -> DataSubmissionRepository: diff --git a/nad_ch/application/use_cases.py b/nad_ch/application/use_cases.py index 5c95062..f2e150e 100644 --- a/nad_ch/application/use_cases.py +++ b/nad_ch/application/use_cases.py @@ -4,42 +4,42 @@ from nad_ch.application.interfaces import ApplicationContext from nad_ch.application.view_models import ( get_view_model, - DataProviderViewModel, + DataProducerViewModel, DataSubmissionViewModel, ) -from nad_ch.domain.entities import DataProvider, DataSubmission +from nad_ch.domain.entities import DataProducer, DataSubmission -def add_data_provider( - ctx: ApplicationContext, provider_name: str -) -> DataProviderViewModel: - if not provider_name: - ctx.logger.error("Provider name required") +def add_data_producer( + ctx: ApplicationContext, producer_name: str +) -> DataProducerViewModel: + if not producer_name: + ctx.logger.error("Producer name required") return - matching_provider = ctx.providers.get_by_name(provider_name) - if matching_provider: - ctx.logger.error("Provider name must be unique") + matching_producer = ctx.producers.get_by_name(producer_name) + if matching_producer: + ctx.logger.error("Producer name must be unique") return - provider = DataProvider(provider_name) - ctx.providers.add(provider) - ctx.logger.info("Provider added") + producer = DataProducer(producer_name) + saved_producer = ctx.producers.add(producer) + ctx.logger.info("Producer added") - return get_view_model(provider) + return get_view_model(saved_producer) -def list_data_providers(ctx: ApplicationContext) -> List[DataProviderViewModel]: - providers = ctx.providers.get_all() - ctx.logger.info("Data Provider Names:") - for p in providers: +def list_data_producers(ctx: ApplicationContext) -> List[DataProducerViewModel]: + producers = ctx.producers.get_all() + ctx.logger.info("Data Producer Names:") + for p in producers: ctx.logger.info(p.name) - return get_view_model(providers) + return get_view_model(producers) def ingest_data_submission( - ctx: ApplicationContext, file_path: str, provider_name: str + ctx: ApplicationContext, file_path: str, producer_name: str ) -> DataSubmissionViewModel: if not file_path: ctx.logger.error("File path required") @@ -50,20 +50,20 @@ def ingest_data_submission( ctx.logger.error("Invalid file format. Only ZIP or CSV files are accepted.") return - provider = ctx.providers.get_by_name(provider_name) - if not provider: - ctx.logger.error("Provider with that name does not exist") + producer = ctx.producers.get_by_name(producer_name) + if not producer: + ctx.logger.error("Producer with that name does not exist") return try: - filename = DataSubmission.generate_filename(file_path, provider) + filename = DataSubmission.generate_filename(file_path, producer) ctx.storage.upload(file_path, filename) - submission = DataSubmission(filename, provider) - ctx.submissions.add(submission) - ctx.logger.info(f"Submission added: {submission.filename}") + submission = DataSubmission(filename, producer) + saved_submission = ctx.submissions.add(submission) + ctx.logger.info(f"Submission added: {saved_submission.filename}") - return get_view_model(submission) + return get_view_model(saved_submission) except Exception as e: ctx.storage.delete(filename) ctx.logger.error(f"Failed to process submission: {e}") @@ -80,18 +80,18 @@ def get_data_submission( return get_view_model(submission) -def list_data_submissions_by_provider( - ctx: ApplicationContext, provider_name: str +def list_data_submissions_by_producer( + ctx: ApplicationContext, producer_name: str ) -> List[DataSubmissionViewModel]: - provider = ctx.providers.get_by_name(provider_name) - if not provider: - ctx.logger.error("Provider with that name does not exist") + producer = ctx.producers.get_by_name(producer_name) + if not producer: + ctx.logger.error("Producer with that name does not exist") return - submissions = ctx.submissions.get_by_provider(provider) - ctx.logger.info(f"Data submissions for {provider.name}") + submissions = ctx.submissions.get_by_producer(producer) + ctx.logger.info(f"Data submissions for {producer.name}") for s in submissions: - ctx.logger.info(f"{s.provider.name}: {s.filename}") + ctx.logger.info(f"{s.producer.name}: {s.filename}") return get_view_model(submissions) diff --git a/nad_ch/application/view_models.py b/nad_ch/application/view_models.py index 54ab113..501a6e4 100644 --- a/nad_ch/application/view_models.py +++ b/nad_ch/application/view_models.py @@ -3,7 +3,7 @@ import json import numpy as np from typing import Union, List, Tuple -from nad_ch.domain.entities import Entity, DataProvider, DataSubmission +from nad_ch.domain.entities import Entity, DataProducer, DataSubmission def get_view_model(entity: Union[Entity, List[Entity]]) -> Union[Entity, List[Entity]]: @@ -12,7 +12,7 @@ def get_view_model(entity: Union[Entity, List[Entity]]) -> Union[Entity, List[En get a static view model object that it can return to its caller. """ entity_to_vm_function_map = { - DataProvider: create_data_provider_vm, + DataProducer: create_data_producer_vm, DataSubmission: create_data_submission_vm, } @@ -31,17 +31,17 @@ def get_view_model(entity: Union[Entity, List[Entity]]) -> Union[Entity, List[En @dataclass -class DataProviderViewModel: +class DataProducerViewModel: id: int date_created: str name: str -def create_data_provider_vm(provider: DataProvider) -> DataProviderViewModel: - return DataProviderViewModel( - id=provider.id, - date_created=present_date(provider.created_at), - name=provider.name, +def create_data_producer_vm(producer: DataProducer) -> DataProducerViewModel: + return DataProducerViewModel( + id=producer.id, + date_created=present_date(producer.created_at), + name=producer.name, ) @@ -50,7 +50,7 @@ class DataSubmissionViewModel: id: int date_created: str filename: str - provider_name: str + producer_name: str report: str @@ -65,7 +65,7 @@ def create_data_submission_vm(submission: DataSubmission) -> DataSubmissionViewM id=submission.id, date_created=present_date(submission.created_at), filename=submission.filename, - provider_name=submission.provider.name, + producer_name=submission.producer.name, report=report_json, ) diff --git a/nad_ch/config/development_local.py b/nad_ch/config/development_local.py index a669508..ac3a826 100644 --- a/nad_ch/config/development_local.py +++ b/nad_ch/config/development_local.py @@ -4,7 +4,7 @@ from nad_ch.application.interfaces import ApplicationContext from nad_ch.infrastructure.database import ( create_session_factory, - SqlAlchemyDataProviderRepository, + SqlAlchemyDataProducerRepository, SqlAlchemyDataSubmissionRepository, ) from nad_ch.infrastructure.logger import BasicLogger @@ -33,14 +33,14 @@ class DevLocalApplicationContext(ApplicationContext): def __init__(self): self._session_factory = create_session_factory(DATABASE_URL) - self._providers = self.create_provider_repository() + self._producers = self.create_producer_repository() self._submissions = self.create_submission_repository() self._logger = self.create_logger() self._storage = self.create_storage() self._task_queue = self.create_task_queue() - def create_provider_repository(self): - return SqlAlchemyDataProviderRepository(self._session_factory) + def create_producer_repository(self): + return SqlAlchemyDataProducerRepository(self._session_factory) def create_submission_repository(self): return SqlAlchemyDataSubmissionRepository(self._session_factory) diff --git a/nad_ch/config/development_remote.py b/nad_ch/config/development_remote.py index a95e123..2b28638 100644 --- a/nad_ch/config/development_remote.py +++ b/nad_ch/config/development_remote.py @@ -4,7 +4,7 @@ from nad_ch.application.interfaces import ApplicationContext from nad_ch.infrastructure.database import ( create_session_factory, - SqlAlchemyDataProviderRepository, + SqlAlchemyDataProducerRepository, SqlAlchemyDataSubmissionRepository, ) from nad_ch.infrastructure.logger import BasicLogger @@ -40,14 +40,14 @@ def get_credentials(service_name, default={}): class DevRemoteApplicationContext(ApplicationContext): def __init__(self): self._session_factory = create_session_factory(DATABASE_URL) - self._providers = self.create_provider_repository() + self._producers = self.create_producer_repository() self._submissions = self.create_submission_repository() self._logger = self.create_logger() self._storage = self.create_storage() self._task_queue = self.create_task_queue() - def create_provider_repository(self): - return SqlAlchemyDataProviderRepository(self._session_factory) + def create_producer_repository(self): + return SqlAlchemyDataProducerRepository(self._session_factory) def create_submission_repository(self): return SqlAlchemyDataSubmissionRepository(self._session_factory) diff --git a/nad_ch/config/test.py b/nad_ch/config/test.py index d42138e..4bb2f01 100644 --- a/nad_ch/config/test.py +++ b/nad_ch/config/test.py @@ -3,7 +3,7 @@ from nad_ch.application.interfaces import ApplicationContext from nad_ch.infrastructure.logger import BasicLogger from tests.fakes_and_mocks import ( - FakeDataProviderRepository, + FakeDataProducerRepository, FakeDataSubmissionRepository, FakeStorage, ) @@ -17,14 +17,14 @@ class TestApplicationContext(ApplicationContext): def __init__(self): self._session = None - self._providers = self.create_provider_repository() + self._producers = self.create_producer_repository() self._submissions = self.create_submission_repository() self._logger = self.create_logger() self._storage = self.create_storage() self._task_queue = self.create_task_queue() - def create_provider_repository(self): - return FakeDataProviderRepository() + def create_producer_repository(self): + return FakeDataProducerRepository() def create_submission_repository(self): return FakeDataSubmissionRepository() diff --git a/nad_ch/controllers/cli.py b/nad_ch/controllers/cli.py index c97cc18..30da523 100644 --- a/nad_ch/controllers/cli.py +++ b/nad_ch/controllers/cli.py @@ -1,9 +1,9 @@ import click from nad_ch.application.use_cases import ( - add_data_provider, - list_data_providers, + add_data_producer, + list_data_producers, ingest_data_submission, - list_data_submissions_by_provider, + list_data_submissions_by_producer, validate_data_submission, ) @@ -16,34 +16,34 @@ def cli(ctx): @cli.command() @click.pass_context -@click.argument("provider_name") -def add_provider(ctx, provider_name): +@click.argument("producer_name") +def add_producer(ctx, producer_name): context = ctx.obj - add_data_provider(context, provider_name) + add_data_producer(context, producer_name) @cli.command() @click.pass_context -def list_providers(ctx): +def list_producers(ctx): context = ctx.obj - list_data_providers(context) + list_data_producers(context) @cli.command() @click.pass_context @click.argument("file_path") -@click.argument("provider") -def ingest(ctx, file_path, provider): +@click.argument("producer") +def ingest(ctx, file_path, producer): context = ctx.obj - ingest_data_submission(context, file_path, provider) + ingest_data_submission(context, file_path, producer) @cli.command() @click.pass_context -@click.argument("provider") -def list_submissions_by_provider(ctx, provider): +@click.argument("producer") +def list_submissions_by_producer(ctx, producer): context = ctx.obj - list_data_submissions_by_provider(context, provider) + list_data_submissions_by_producer(context, producer) @cli.command() diff --git a/nad_ch/controllers/web/routes.py b/nad_ch/controllers/web/routes.py index 3265882..5521811 100644 --- a/nad_ch/controllers/web/routes.py +++ b/nad_ch/controllers/web/routes.py @@ -1,6 +1,6 @@ from flask import Blueprint, current_app, render_template, g from nad_ch.application.use_cases import ( - list_data_submissions_by_provider, + list_data_submissions_by_producer, get_data_submission, ) @@ -20,8 +20,8 @@ def home(): @home_bp.route("/reports") def reports(): - # For demo purposes, hard-code the provider name - view_model = list_data_submissions_by_provider(g.ctx, "NJ") + # For demo purposes, hard-code the producer name + view_model = list_data_submissions_by_producer(g.ctx, "NJ") return render_template("reports/index.html", submissions=view_model) diff --git a/nad_ch/domain/entities.py b/nad_ch/domain/entities.py index be60a4b..93487a4 100644 --- a/nad_ch/domain/entities.py +++ b/nad_ch/domain/entities.py @@ -16,38 +16,38 @@ def set_updated_at(self, updated_at: datetime): self.updated_at = updated_at -class DataProvider(Entity): +class DataProducer(Entity): def __init__(self, name: str, id: int = None): super().__init__(id) self.name = name def __repr__(self): - return f"DataProvider {self.id}, {self.name})" + return f"DataProducer {self.id}, {self.name})" class DataSubmission(Entity): def __init__( self, filename: str, - provider: DataProvider, + producer: DataProducer, report=None, id: int = None, ): super().__init__(id) self.filename = filename - self.provider = provider + self.producer = producer self.report = report def __repr__(self): return f"DataSubmission \ - {self.id}, {self.filename}, {self.provider}" + {self.id}, {self.filename}, {self.producer}" @staticmethod - def generate_filename(file_path: str, provider: DataProvider) -> str: - s = re.sub(r"\W+", "_", provider.name) + def generate_filename(file_path: str, producer: DataProducer) -> str: + s = re.sub(r"\W+", "_", producer.name) s = s.lower() s = s.strip("_") - formatted_provider_name = re.sub(r"_+", "_", s) + formatted_producer_name = re.sub(r"_+", "_", s) current_time_utc = datetime.now(timezone.utc) timestamp = current_time_utc.timestamp() @@ -55,7 +55,7 @@ def generate_filename(file_path: str, provider: DataProvider) -> str: datetime_str = datetime_obj.strftime("%Y%m%d_%H%M%S") _, file_extension = os.path.splitext(file_path) - filename = f"{formatted_provider_name}_{datetime_str}{file_extension}" + filename = f"{formatted_producer_name}_{datetime_str}{file_extension}" return filename def has_report(self) -> bool: diff --git a/nad_ch/domain/repositories.py b/nad_ch/domain/repositories.py index e0d8808..1e43cf8 100644 --- a/nad_ch/domain/repositories.py +++ b/nad_ch/domain/repositories.py @@ -1,16 +1,16 @@ from typing import Optional, Protocol from collections.abc import Iterable -from nad_ch.domain.entities import DataProvider, DataSubmission +from nad_ch.domain.entities import DataProducer, DataSubmission -class DataProviderRepository(Protocol): - def add(self, provider: DataProvider) -> DataProvider: +class DataProducerRepository(Protocol): + def add(self, producer: DataProducer) -> DataProducer: ... - def get_by_name(self, name: str) -> Optional[DataProvider]: + def get_by_name(self, name: str) -> Optional[DataProducer]: ... - def get_all(self) -> Iterable[DataProvider]: + def get_all(self) -> Iterable[DataProducer]: ... @@ -21,7 +21,7 @@ def add(self, submission: DataSubmission) -> DataSubmission: def get_by_id() -> Optional[DataSubmission]: ... - def get_by_provider(self, provider: DataProvider) -> Iterable[DataSubmission]: + def get_by_producer(self, producer: DataProducer) -> Iterable[DataSubmission]: ... def get_by_filename() -> Optional[DataSubmission]: diff --git a/nad_ch/infrastructure/database.py b/nad_ch/infrastructure/database.py index 79ef192..ca05978 100644 --- a/nad_ch/infrastructure/database.py +++ b/nad_ch/infrastructure/database.py @@ -4,8 +4,8 @@ from sqlalchemy.sql import func from sqlalchemy.types import JSON import contextlib -from nad_ch.domain.entities import DataProvider, DataSubmission -from nad_ch.domain.repositories import DataProviderRepository, DataSubmissionRepository +from nad_ch.domain.entities import DataProducer, DataSubmission +from nad_ch.domain.repositories import DataProducerRepository, DataSubmissionRepository def create_session_factory(connection_string: str): @@ -46,22 +46,22 @@ class CommonBase(ModelBase): ) -class DataProviderModel(CommonBase): - __tablename__ = "data_providers" +class DataProducerModel(CommonBase): + __tablename__ = "data_producers" name = Column(String) data_submissions = relationship( - "DataSubmissionModel", back_populates="data_provider" + "DataSubmissionModel", back_populates="data_producer" ) @staticmethod - def from_entity(provider): - model = DataProviderModel(id=provider.id, name=provider.name) + def from_entity(producer): + model = DataProducerModel(id=producer.id, name=producer.name) return model def to_entity(self): - entity = DataProvider(id=self.id, name=self.name) + entity = DataProducer(id=self.id, name=self.name) if self.created_at is not None: entity.set_created_at(self.created_at) @@ -76,10 +76,10 @@ class DataSubmissionModel(CommonBase): __tablename__ = "data_submissions" filename = Column(String) - data_provider_id = Column(Integer, ForeignKey("data_providers.id")) + data_producer_id = Column(Integer, ForeignKey("data_producers.id")) report = Column(JSON) - data_provider = relationship("DataProviderModel", back_populates="data_submissions") + data_producer = relationship("DataProducerModel", back_populates="data_submissions") @staticmethod def from_entity(submission): @@ -87,13 +87,13 @@ def from_entity(submission): id=submission.id, filename=submission.filename, report=submission.report, - data_provider_id=submission.provider.id, + data_producer_id=submission.producer.id, ) return model - def to_entity(self, provider: DataProvider): + def to_entity(self, producer: DataProducer): entity = DataSubmission( - id=self.id, filename=self.filename, report=self.report, provider=provider + id=self.id, filename=self.filename, report=self.report, producer=producer ) if self.created_at is not None: @@ -105,32 +105,32 @@ def to_entity(self, provider: DataProvider): return entity -class SqlAlchemyDataProviderRepository(DataProviderRepository): +class SqlAlchemyDataProducerRepository(DataProducerRepository): def __init__(self, session_factory): self.session_factory = session_factory - def add(self, provider: DataProvider) -> DataProvider: + def add(self, producer: DataProducer) -> DataProducer: with session_scope(self.session_factory) as session: - provider_model = DataProviderModel.from_entity(provider) - session.add(provider_model) + producer_model = DataProducerModel.from_entity(producer) + session.add(producer_model) session.commit() - session.refresh(provider_model) - return provider_model.to_entity() + session.refresh(producer_model) + return producer_model.to_entity() - def get_by_name(self, name: str) -> Optional[DataProvider]: + def get_by_name(self, name: str) -> Optional[DataProducer]: with session_scope(self.session_factory) as session: - provider_model = ( - session.query(DataProviderModel) - .filter(DataProviderModel.name == name) + producer_model = ( + session.query(DataProducerModel) + .filter(DataProducerModel.name == name) .first() ) - return provider_model.to_entity() if provider_model else None + return producer_model.to_entity() if producer_model else None - def get_all(self) -> List[DataProvider]: + def get_all(self) -> List[DataProducer]: with session_scope(self.session_factory) as session: - provider_models = session.query(DataProviderModel).all() - providers_entities = [provider.to_entity() for provider in provider_models] - return providers_entities + producer_models = session.query(DataProducerModel).all() + producer_entities = [producer.to_entity() for producer in producer_models] + return producer_entities class SqlAlchemyDataSubmissionRepository(DataSubmissionRepository): @@ -143,58 +143,58 @@ def add(self, submission: DataSubmission) -> DataSubmission: session.add(submission_model) session.commit() session.refresh(submission_model) - provider_model = ( - session.query(DataProviderModel) - .filter(DataProviderModel.id == submission_model.data_provider_id) + producer_model = ( + session.query(DataProducerModel) + .filter(DataProducerModel.id == submission_model.data_producer_id) .first() ) - return submission_model.to_entity(provider_model.to_entity()) + return submission_model.to_entity(producer_model.to_entity()) def get_by_id(self, id: int) -> Optional[DataSubmission]: with session_scope(self.session_factory) as session: result = ( - session.query(DataSubmissionModel, DataProviderModel) + session.query(DataSubmissionModel, DataProducerModel) .join( - DataProviderModel, - DataProviderModel.id == DataSubmissionModel.data_provider_id, + DataProducerModel, + DataProducerModel.id == DataSubmissionModel.data_producer_id, ) .filter(DataSubmissionModel.id == id) .first() ) if result: - submission_model, provider_model = result - return submission_model.to_entity(provider_model.to_entity()) + submission_model, producer_model = result + return submission_model.to_entity(producer_model.to_entity()) else: return None - def get_by_provider(self, provider: DataProvider) -> List[DataSubmission]: + def get_by_producer(self, producer: DataProducer) -> List[DataSubmission]: with session_scope(self.session_factory) as session: submission_models = ( session.query(DataSubmissionModel) - .filter(DataSubmissionModel.data_provider_id == provider.id) + .filter(DataSubmissionModel.data_producer_id == producer.id) .all() ) submission_entities = [ - submission.to_entity(provider) for submission in submission_models + submission.to_entity(producer) for submission in submission_models ] return submission_entities def get_by_filename(self, filename: str) -> Optional[DataSubmission]: with session_scope(self.session_factory) as session: result = ( - session.query(DataSubmissionModel, DataProviderModel) + session.query(DataSubmissionModel, DataProducerModel) .join( - DataProviderModel, - DataProviderModel.id == DataSubmissionModel.data_provider_id, + DataProducerModel, + DataProducerModel.id == DataSubmissionModel.data_producer_id, ) .filter(DataSubmissionModel.filename == filename) .first() ) if result: - submission_model, provider_model = result - return submission_model.to_entity(provider_model.to_entity()) + submission_model, producer_model = result + return submission_model.to_entity(producer_model.to_entity()) else: return None diff --git a/tests/application/test_use_cases.py b/tests/application/test_use_cases.py index 2bc7f4d..91e40d0 100644 --- a/tests/application/test_use_cases.py +++ b/tests/application/test_use_cases.py @@ -2,14 +2,14 @@ import re from nad_ch.application.dtos import DataSubmissionReport, DataSubmissionReportOverview from nad_ch.application.use_cases import ( - add_data_provider, - list_data_providers, + add_data_producer, + list_data_producers, ingest_data_submission, - list_data_submissions_by_provider, + list_data_submissions_by_producer, validate_data_submission, ) from nad_ch.application.view_models import ( - DataProviderViewModel, + DataProducerViewModel, DataSubmissionViewModel, ) from nad_ch.config import create_app_context @@ -32,85 +32,85 @@ def is_valid_date_format(date_str: str) -> bool: return bool(match) -def test_add_data_provider(app_context): +def test_add_data_producer(app_context): name = "State X" - result = add_data_provider(app_context, name) + result = add_data_producer(app_context, name) - assert isinstance(result, DataProviderViewModel) + assert isinstance(result, DataProducerViewModel) assert is_valid_date_format(result.date_created) -def test_add_data_provider_logs_error_if_no_provider_name_given(mocker): +def test_add_data_producer_logs_error_if_no_producer_name_given(mocker): mock_context = mocker.patch("nad_ch.config.create_app_context") - add_data_provider(mock_context, "") - mock_context.logger.error.assert_called_once_with("Provider name required") + add_data_producer(mock_context, "") + mock_context.logger.error.assert_called_once_with("Producer name required") -def test_add_data_provider_logs_error_if_provider_name_not_unique(mocker): +def test_add_data_producer_logs_error_if_producer_name_not_unique(mocker): mock_context = mocker.patch("nad_ch.config.create_app_context") - mock_context.providers.get_by_name.return_value("State X") - add_data_provider(mock_context, "State X") + mock_context.producer.get_by_name.return_value("State X") + add_data_producer(mock_context, "State X") - mock_context.logger.error.assert_called_once_with("Provider name must be unique") + mock_context.logger.error.assert_called_once_with("Producer name must be unique") -def test_list_a_single_data_provider(app_context): +def test_list_a_single_data_producer(app_context): name = "State X" - add_data_provider(app_context, name) + add_data_producer(app_context, name) - result = list_data_providers(app_context) + result = list_data_producers(app_context) assert len(result) == 1 - assert isinstance(result[0], DataProviderViewModel) + assert isinstance(result[0], DataProducerViewModel) assert result[0].name == name -def test_list_multiple_data_providers(app_context): +def test_list_multiple_data_producers(app_context): first_name = "State X" - add_data_provider(app_context, first_name) + add_data_producer(app_context, first_name) second_name = "State Y" - add_data_provider(app_context, second_name) + add_data_producer(app_context, second_name) - result = list_data_providers(app_context) + result = list_data_producers(app_context) assert len(result) == 2 - assert isinstance(result[0], DataProviderViewModel) + assert isinstance(result[0], DataProducerViewModel) assert result[0].name == first_name - assert isinstance(result[1], DataProviderViewModel) + assert isinstance(result[1], DataProducerViewModel) assert result[1].name == second_name def test_ingest_data_submission(app_context): - provider_name = "State X" - add_data_provider(app_context, provider_name) + producer_name = "State X" + add_data_producer(app_context, producer_name) filename = "my_cool_file.zip" - result = ingest_data_submission(app_context, filename, provider_name) + result = ingest_data_submission(app_context, filename, producer_name) assert isinstance(result, DataSubmissionViewModel) -def test_list_data_submissions_by_provider(app_context): - provider_name = "State X" - add_data_provider(app_context, provider_name) +def test_list_data_submissions_by_producer(app_context): + producer_name = "State X" + add_data_producer(app_context, producer_name) filename = "my_cool_file.zip" - ingest_data_submission(app_context, filename, provider_name) + ingest_data_submission(app_context, filename, producer_name) - result = list_data_submissions_by_provider(app_context, provider_name) + result = list_data_submissions_by_producer(app_context, producer_name) assert len(result) == 1 assert isinstance(result[0], DataSubmissionViewModel) def test_validate_data_submission(app_context, caplog): - provider_name = "State X" - add_data_provider(app_context, provider_name) + producer_name = "State X" + add_data_producer(app_context, producer_name) filename = "my_cool_file.zip" - ingest_data_submission(app_context, filename, provider_name) + ingest_data_submission(app_context, filename, producer_name) submission = app_context.submissions.get_by_id(1) class CustomMockTestTaskQueue: diff --git a/tests/application/test_view_models.py b/tests/application/test_view_models.py index 11692b9..4854d2f 100644 --- a/tests/application/test_view_models.py +++ b/tests/application/test_view_models.py @@ -1,33 +1,33 @@ from datetime import datetime from nad_ch.application.view_models import ( get_view_model, - DataProviderViewModel, + DataProducerViewModel, DataSubmissionViewModel, ) -from nad_ch.domain.entities import DataProvider, DataSubmission +from nad_ch.domain.entities import DataProducer, DataSubmission -def test_get_a_single_data_provider_view_model(): - provider = DataProvider("State X") +def test_get_a_single_data_producer_view_model(): + producer = DataProducer("State X") date = datetime(2024, 2, 29, 20, 48, 58, 205608) - provider.set_created_at(date) + producer.set_created_at(date) - result = get_view_model(provider) + result = get_view_model(producer) - assert isinstance(result, DataProviderViewModel) + assert isinstance(result, DataProducerViewModel) assert result.date_created == "February 29, 2024" -def test_get_a_list_of_data_provider_view_models(): - provider_a = DataProvider("State A") +def test_get_a_list_of_data_producer_view_models(): + producer_a = DataProducer("State A") date_a = datetime(2024, 2, 29, 20, 48, 58, 205608) - provider_a.set_created_at(date_a) + producer_a.set_created_at(date_a) - provider_b = DataProvider("State B") + producer_b = DataProducer("State B") date_b = datetime(2024, 5, 1, 20, 48, 58, 205608) - provider_b.set_created_at(date_b) + producer_b.set_created_at(date_b) - result = get_view_model([provider_a, provider_b]) + result = get_view_model([producer_a, producer_b]) assert len(result) == 2 assert result[0].date_created == "February 29, 2024" @@ -35,8 +35,8 @@ def test_get_a_list_of_data_provider_view_models(): def test_get_a_single_data_submisson_view_model(): - provider = DataProvider("State X") - submission = DataSubmission("some_file_name", provider) + producer = DataProducer("State X") + submission = DataSubmission("some_file_name", producer) date = datetime(2024, 2, 29, 20, 48, 58, 205608) submission.set_created_at(date) @@ -44,17 +44,17 @@ def test_get_a_single_data_submisson_view_model(): assert isinstance(result, DataSubmissionViewModel) assert result.date_created == "February 29, 2024" - assert result.provider_name == "State X" + assert result.producer_name == "State X" def test_get_a_list_of_data_submisson_view_models(): - provider_a = DataProvider("State A") - submission_a = DataSubmission("some_file_name", provider_a) + producer_a = DataProducer("State A") + submission_a = DataSubmission("some_file_name", producer_a) date_a = datetime(2024, 2, 29, 20, 48, 58, 205608) submission_a.set_created_at(date_a) - provider_b = DataProvider("State B") - submission_b = DataSubmission("some_other_file_name", provider_b) + producer_b = DataProducer("State B") + submission_b = DataSubmission("some_other_file_name", producer_b) date_b = datetime(2024, 5, 1, 20, 48, 58, 205608) submission_b.set_created_at(date_b) @@ -62,6 +62,6 @@ def test_get_a_list_of_data_submisson_view_models(): assert len(result) == 2 assert result[0].date_created == "February 29, 2024" - assert result[0].provider_name == "State A" + assert result[0].producer_name == "State A" assert result[1].date_created == "May 01, 2024" - assert result[1].provider_name == "State B" + assert result[1].producer_name == "State B" diff --git a/tests/domain/test_entities.py b/tests/domain/test_entities.py index 8908486..c1d99cb 100644 --- a/tests/domain/test_entities.py +++ b/tests/domain/test_entities.py @@ -1,12 +1,12 @@ import datetime -from nad_ch.domain.entities import DataProvider, DataSubmission +from nad_ch.domain.entities import DataProducer, DataSubmission def test_data_submission_generates_filename(): - provider = DataProvider("Some Provider") - filename = DataSubmission.generate_filename("someupload.zip", provider) + producer = DataProducer("Some Producer") + filename = DataSubmission.generate_filename("someupload.zip", producer) todays_date = datetime.datetime.now().strftime("%Y%m%d") - assert filename.startswith("some_provider_") + assert filename.startswith("some_producer_") assert todays_date in filename assert filename.endswith(".zip") @@ -14,11 +14,11 @@ def test_data_submission_generates_filename(): def test_data_submission_knows_if_it_has_a_report(): report_data = {"key1": "value1", "key2": "value2"} submission = DataSubmission( - "someupload.zip", DataProvider("Some provider"), report_data + "someupload.zip", DataProducer("Some producer"), report_data ) assert submission.has_report() def test_data_submission_knows_if_it_does_not_have_a_report(): - submission = DataSubmission("someupload.zip", DataProvider("Some provider")) + submission = DataSubmission("someupload.zip", DataProducer("Some producer")) assert not submission.has_report() diff --git a/tests/fakes_and_mocks.py b/tests/fakes_and_mocks.py index b29aa9b..8ef2844 100644 --- a/tests/fakes_and_mocks.py +++ b/tests/fakes_and_mocks.py @@ -1,27 +1,27 @@ from datetime import datetime from typing import Optional from nad_ch.application.dtos import DownloadResult -from nad_ch.domain.entities import DataProvider, DataSubmission -from nad_ch.domain.repositories import DataProviderRepository, DataSubmissionRepository +from nad_ch.domain.entities import DataProducer, DataSubmission +from nad_ch.domain.repositories import DataProducerRepository, DataSubmissionRepository -class FakeDataProviderRepository(DataProviderRepository): +class FakeDataProducerRepository(DataProducerRepository): def __init__(self) -> None: - self._providers = set() + self._producers = set() self._next_id = 1 - def add(self, provider: DataProvider) -> DataProvider: - provider.id = self._next_id - provider.set_created_at(datetime.now()) - self._providers.add(provider) + def add(self, producer: DataProducer) -> DataProducer: + producer.id = self._next_id + producer.set_created_at(datetime.now()) + self._producers.add(producer) self._next_id += 1 - return provider + return producer - def get_by_name(self, name: str) -> Optional[DataProvider]: - return next((p for p in self._providers if p.name == name), None) + def get_by_name(self, name: str) -> Optional[DataProducer]: + return next((p for p in self._producers if p.name == name), None) def get_all(self): - return sorted(list(self._providers), key=lambda provider: provider.id) + return sorted(list(self._producers), key=lambda producer: producer.id) class FakeDataSubmissionRepository(DataSubmissionRepository): @@ -39,8 +39,8 @@ def add(self, submission: DataSubmission) -> DataSubmission: def get_by_id(self, id: int) -> Optional[DataSubmission]: return next((s for s in self._submissions if s.id == id), None) - def get_by_provider(self, provider: DataProvider) -> Optional[DataSubmission]: - return [s for s in self._submissions if s.provider.name == provider.name] + def get_by_producer(self, producer: DataProducer) -> Optional[DataSubmission]: + return [s for s in self._submissions if s.producer.name == producer.name] def get_by_filename(self, filename: str) -> Optional[DataSubmission]: return next((s for s in self._submissions if s.filename == filename), None) diff --git a/tests/infrastructure/test_database.py b/tests/infrastructure/test_database.py index 99e6fa6..9329c0b 100644 --- a/tests/infrastructure/test_database.py +++ b/tests/infrastructure/test_database.py @@ -2,10 +2,10 @@ import pytest from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from nad_ch.domain.entities import DataProvider, DataSubmission +from nad_ch.domain.entities import DataProducer, DataSubmission from nad_ch.infrastructure.database import ( ModelBase, - SqlAlchemyDataProviderRepository, + SqlAlchemyDataProducerRepository, SqlAlchemyDataSubmissionRepository, ) @@ -18,9 +18,9 @@ def test_database(): @pytest.fixture(scope="function") -def providers(test_database): +def producers(test_database): Session = sessionmaker(bind=test_database) - return SqlAlchemyDataProviderRepository(Session) + return SqlAlchemyDataProducerRepository(Session) @pytest.fixture(scope="function") @@ -29,44 +29,44 @@ def submissions(test_database): return SqlAlchemyDataSubmissionRepository(Session) -def test_add_data_provider_to_repository_and_get_by_name(providers): - provider_name = "State X" - new_provider = DataProvider(provider_name) +def test_add_data_producer_to_repository_and_get_by_name(producers): + producer_name = "State X" + new_producer = DataProducer(producer_name) - providers.add(new_provider) + producers.add(new_producer) - retrieved_provider = providers.get_by_name(provider_name) - assert retrieved_provider.id == 1 - assert retrieved_provider.created_at is not None - assert retrieved_provider.updated_at is not None - assert retrieved_provider.name == provider_name - assert isinstance(retrieved_provider, DataProvider) is True + retrieved_producer = producers.get_by_name(producer_name) + assert retrieved_producer.id == 1 + assert retrieved_producer.created_at is not None + assert retrieved_producer.updated_at is not None + assert retrieved_producer.name == producer_name + assert isinstance(retrieved_producer, DataProducer) is True -def test_add_data_provider_and_then_data_submission(providers, submissions): - provider_name = "State X" - new_provider = DataProvider(provider_name) - saved_provider = providers.add(new_provider) - new_submission = DataSubmission("some-file-name", saved_provider) +def test_add_data_producer_and_then_data_submission(producers, submissions): + producer_name = "State X" + new_producer = DataProducer(producer_name) + saved_producer = producers.add(new_producer) + new_submission = DataSubmission("some-file-name", saved_producer) result = submissions.add(new_submission) assert result.id == 1 assert result.created_at is not None assert result.updated_at is not None - assert result.provider.id == saved_provider.id + assert result.producer.id == saved_producer.id assert result.filename == "some-file-name" -def test_retrieve_a_list_of_submissions_by_provider(providers, submissions): - provider_name = "State X" - new_provider = DataProvider(provider_name) - saved_provider = providers.add(new_provider) - new_submission = DataSubmission("some-file-name", saved_provider) +def test_retrieve_a_list_of_submissions_by_producer(producers, submissions): + producer_name = "State X" + new_producer = DataProducer(producer_name) + saved_producer = producers.add(new_producer) + new_submission = DataSubmission("some-file-name", saved_producer) submissions.add(new_submission) - another_new_submission = DataSubmission("some-other-file-name", saved_provider) + another_new_submission = DataSubmission("some-other-file-name", saved_producer) submissions.add(another_new_submission) - submissions = submissions.get_by_provider(saved_provider) + submissions = submissions.get_by_producer(saved_producer) assert len(submissions) == 2