Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions api/thing.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@
editor_dependency,
viewer_dependency,
)
from db.thing import Thing, ThingIdLink, WellScreen
from db.deployment import Deployment
from db.thing import Thing, ThingIdLink, WellScreen
from schemas.deployment import DeploymentResponse
from schemas.thing import (
CreateThingIdLink,
CreateWell,
Expand All @@ -49,9 +50,9 @@
UpdateThingIdLink,
UpdateWellScreen,
)
from schemas.deployment import DeploymentResponse
from services.crud_helper import model_patcher, model_adder, model_deleter
from services.exceptions_helper import PydanticStyleException
from services.lexicon_helper import get_terms_by_category
from services.query_helper import (
simple_get_by_id,
paginated_all_getter,
Expand All @@ -66,7 +67,6 @@
modify_well_descriptor_tables,
WELL_DESCRIPTOR_MODEL_MAP,
)
from services.lexicon_helper import get_terms_by_category

router = APIRouter(prefix="/thing", tags=["thing"])

Expand Down
14 changes: 14 additions & 0 deletions core/initializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from sqlalchemy import text
from sqlalchemy.exc import DatabaseError
from sqlalchemy.orm import Session

from db import Base
from db.engine import engine, session_ctx
Expand Down Expand Up @@ -81,6 +82,19 @@ def init_parameter(path: str = None) -> None:
session.rollback()


def erase_and_rebuild_db(session: Session):
from sqlalchemy import text

with session.bind.connect() as conn:
conn.execute(text("DROP SCHEMA public CASCADE"))
conn.execute(text("CREATE SCHEMA public"))
conn.execute(text("CREATE EXTENSION IF NOT EXISTS postgis"))
conn.commit()

Base.metadata.drop_all(session.bind)
Base.metadata.create_all(session.bind)


def init_lexicon(path: str = None) -> None:
if path is None:
path = Path(__file__).parent / "lexicon.json"
Expand Down
7 changes: 4 additions & 3 deletions db/contact.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================
from typing import List, TYPE_CHECKING

from sqlalchemy import Integer, ForeignKey, String
from sqlalchemy.ext.associationproxy import association_proxy, AssociationProxy
from sqlalchemy.orm import relationship, Mapped, mapped_column
from sqlalchemy_utils import TSVectorType
from typing import List, TYPE_CHECKING

from db.base import Base, AutoBaseMixin, ReleaseMixin, lexicon_term


if TYPE_CHECKING:
from db.field import FieldEventParticipant, FieldEvent
from db.thing import Thing
Expand Down Expand Up @@ -118,7 +118,8 @@ class Phone(Base, AutoBaseMixin, ReleaseMixin):
contact_id: Mapped[int] = mapped_column(
ForeignKey("contact.id", ondelete="CASCADE"), nullable=False
)
phone_number: Mapped[str] = mapped_column(String(20), nullable=False)
nma_phone_number: Mapped[str] = mapped_column(String(20), nullable=True)
phone_number: Mapped[str] = mapped_column(String(20), nullable=True)
phone_type: Mapped[str] = lexicon_term(nullable=False)

contact: Mapped["Contact"] = relationship(
Expand Down
3 changes: 2 additions & 1 deletion db/geochronology.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================
from db.base import AutoBaseMixin, Base, lexicon_term
from sqlalchemy import Integer, Float
from sqlalchemy.orm import mapped_column

from db.base import AutoBaseMixin, Base, lexicon_term


class GeochronologyAge(Base, AutoBaseMixin):
location_id = mapped_column(Integer, nullable=False)
Expand Down
1 change: 1 addition & 0 deletions schemas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
# limitations under the License.
# ===============================================================================
from datetime import datetime, timezone

from pydantic import BaseModel, ConfigDict, AwareDatetime
from pydantic.json_schema import JsonSchemaValue
from pydantic_core import core_schema
Expand Down
31 changes: 21 additions & 10 deletions schemas/contact.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,26 @@ def validate_phone(cls, phone_number_str: str | None) -> str | None:
region = "US"
try:
phone_number_str = phone_number_str.strip()
parsed_number = phonenumbers.parse(phone_number_str, region)
if phonenumbers.is_valid_number(parsed_number):
formatted_number = phonenumbers.format_number(
parsed_number, phonenumbers.PhoneNumberFormat.E164
)
return formatted_number

# this is a major hack to deal with the phone numbers entered into
# NM_Aquifer without an area code
for p in (phone_number_str, f"505{phone_number_str}"):
parsed_number = phonenumbers.parse(p, region)
if phonenumbers.is_valid_number(parsed_number):
formatted_number = phonenumbers.format_number(
parsed_number, phonenumbers.PhoneNumberFormat.E164
)
return formatted_number
# for p in (
# phone_number_str,
# f"505{phone_number_str}",
# f"575{phone_number_str}",
# ):
# parsed_number = phonenumbers.parse(p, region)
# if phonenumbers.is_valid_number(parsed_number):
# formatted_number = phonenumbers.format_number(
# parsed_number, phonenumbers.PhoneNumberFormat.E164
# )
# return formatted_number
else:
raise ValueError(f"Invalid phone number. {phone_number_str}")
except NumberParseException as e:
Expand All @@ -89,7 +100,6 @@ class CreateEmail(BaseCreateModel, ValidateEmail):
"""

contact_id: int | None = None # set to None for when made via POST /contact
email: str
email_type: str = "Primary" # Default to 'Primary'


Expand All @@ -99,8 +109,8 @@ class CreatePhone(BaseCreateModel, ValidatePhone):
"""

contact_id: int | None = None # set to None for when made via POST /contact
phone_number: str
phone_type: str = "Primary" # Default to 'Primary'
nma_phone_number: str | None = None


class CreateAddress(BaseCreateModel):
Expand Down Expand Up @@ -160,8 +170,9 @@ class PhoneResponse(BaseItemResponse):
Response schema for phone details.
"""

phone_number: str
phone_number: str | None = None
phone_type: str # e.g., 'mobile', 'landline', etc.
nma_phone_number: str | None = None


class EmailResponse(BaseItemResponse):
Expand Down
4 changes: 3 additions & 1 deletion schemas/lexicon.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================
from pydantic import BaseModel, ConfigDict
from typing import List

from pydantic import BaseModel, ConfigDict

from schemas import UTCAwareDatetime


Expand Down
3 changes: 2 additions & 1 deletion schemas/observation.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
# limitations under the License.
# ===============================================================================
from datetime import timezone
from typing import Annotated

from pydantic import (
BaseModel,
AwareDatetime,
PastDatetime,
field_validator,
model_validator,
)
from typing import Annotated
from typing_extensions import Self

from schemas import (
Expand Down
7 changes: 4 additions & 3 deletions schemas/sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
# limitations under the License.
# ===============================================================================
from datetime import timezone
from typing import Annotated

from pydantic import (
BaseModel,
field_validator,
model_validator,
AwareDatetime,
PastDatetime,
)
from typing import Annotated
from typing_extensions import Self

from schemas import (
Expand All @@ -30,9 +31,9 @@
BaseResponseModel,
UTCAwareDatetime,
)
from schemas.thing import ThingResponse
from schemas.field import FieldEventResponse, FieldActivityResponse
from schemas.contact import ContactResponse
from schemas.field import FieldEventResponse, FieldActivityResponse
from schemas.thing import ThingResponse


# -------- VALIDATE ----------
Expand Down
1 change: 1 addition & 0 deletions schemas/thing.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from schemas import BaseCreateModel, BaseUpdateModel, BaseResponseModel
from schemas.location import LocationResponse


# -------- VALIDATE ----------


Expand Down
18 changes: 4 additions & 14 deletions tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,24 +30,14 @@
time.tzset()

from fastapi.testclient import TestClient
from sqlalchemy import text

from core.initializers import init_lexicon, init_parameter
from core.initializers import init_lexicon, init_parameter, erase_and_rebuild_db
from db import Base, Parameter
from db.engine import engine, session_ctx
from db.engine import session_ctx
from main import app


# Force clean database state by dropping and recreating schema
# This ensures test isolation similar to Docker environment
with engine.connect() as conn:
# Drop all tables with CASCADE to handle foreign key dependencies
conn.execute(text("DROP SCHEMA IF EXISTS public CASCADE"))
conn.execute(text("CREATE SCHEMA public"))
conn.execute(text("CREATE EXTENSION IF NOT EXISTS postgis"))
conn.commit()

Base.metadata.create_all(engine)
with session_ctx() as session:
erase_and_rebuild_db(session)

init_lexicon()
init_parameter()
Expand Down
8 changes: 5 additions & 3 deletions tests/test_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================
from datetime import timezone
from unittest.mock import patch

import pytest

from api.asset import get_storage_bucket
from core.app import app
from core.dependencies import viewer_function, admin_function, editor_function
from db import Asset
from tests import client, cleanup_post_test, override_authentication, cleanup_patch_test

import pytest
from datetime import timezone
from unittest.mock import patch

# CLASSES, FIXTURES, AND FUNCTIONS =============================================

Expand Down
13 changes: 7 additions & 6 deletions tests/test_contact.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import re
from datetime import timezone

import pytest
from pydantic import ValidationError

from core.dependencies import (
amp_viewer_function,
amp_editor_function,
amp_admin_function,
)
from db import Contact, Address, Email, Phone
from main import app
from tests import client, cleanup_post_test, cleanup_patch_test, override_authentication
from schemas.contact import ValidateEmail, ValidatePhone, ValidateContact

import pytest
from datetime import timezone
from pydantic import ValidationError
import re
from tests import client, cleanup_post_test, cleanup_patch_test, override_authentication


@pytest.fixture(scope="module", autouse=True)
Expand Down
7 changes: 4 additions & 3 deletions tests/test_group.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from datetime import timezone

import pytest
from geoalchemy2.shape import to_shape
from pydantic import ValidationError
import pytest
from datetime import timezone

from db import Group
from core.dependencies import admin_function, viewer_function, editor_function
from db import Group
from main import app
from schemas.group import ValidateGroup
from tests import client, override_authentication, cleanup_post_test, cleanup_patch_test
Expand Down
10 changes: 5 additions & 5 deletions tests/test_lexicon.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================
from db import LexiconTerm, LexiconCategory, LexiconTriple
from tests import client, override_authentication, cleanup_post_test, cleanup_patch_test
from datetime import timezone

import pytest

from core.dependencies import (
viewer_function,
lexicon_admin_function,
lexicon_editor_function,
)
from db import LexiconTerm, LexiconCategory, LexiconTriple
from main import app

import pytest
from datetime import timezone
from tests import client, override_authentication, cleanup_post_test, cleanup_patch_test


@pytest.fixture(scope="module", autouse=True)
Expand Down
5 changes: 3 additions & 2 deletions tests/test_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================
from geoalchemy2.shape import to_shape
import pytest
from datetime import timezone

import pytest
from geoalchemy2.shape import to_shape

from core.dependencies import admin_function, editor_function, viewer_function
from db import Location
from main import app
Expand Down
5 changes: 3 additions & 2 deletions tests/test_observation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
# ===============================================================================
import pytest
from datetime import timezone

from db import Observation
import pytest

from core.dependencies import (
amp_admin_function,
admin_function,
amp_viewer_function,
amp_editor_function,
viewer_function,
)
from db import Observation
from main import app
from tests import (
client,
Expand Down
Loading