diff --git a/backend/crud/records.py b/backend/crud/records.py new file mode 100644 index 0000000..81c9281 --- /dev/null +++ b/backend/crud/records.py @@ -0,0 +1,329 @@ +"""This module handles CRUD operations for vitals records in the database, based on pydanctic schemas.""" + +from datetime import datetime + +from sqlalchemy.orm import Session + +from backend.models import records as recordmodel +from backend.models import devices as devicemodel +from backend.schemas import records as recordschema +from backend.exceptions import NotFoundException + + +def create_heart_rate_record(db: Session, record: recordschema.HeartRateRecordCreate, device_id: int) -> recordschema.HeartRateRecord: + """Creates the specified heart rate record in the database.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + db_record = recordmodel.HeartRateRecord( + value=record.value, + measured=record.measured, + device_id=device_id, + ) + db.add(db_record) + db.commit() + db.refresh(db_record) + + return recordschema.HeartRateRecord.from_orm(db_record) + + +def read_heart_rate_records_by_device( + db: Session, device_id: int, + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ) -> list[recordschema.HeartRateRecord]: + """Queries the db for a range of heart rate records captured by the device with the specified id.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + filters = [recordmodel.HeartRateRecord.device == db_device] + if since: + filters.append(recordmodel.HeartRateRecord.measured >= since) + if until: + filters.append(recordmodel.HeartRateRecord.measured <= until) + + return db.query(recordmodel.HeartRateRecord).filter(*filters).order_by(recordmodel.HeartRateRecord.measured.desc()).offset(skip).limit(limit).all() + + +def delete_heart_rate_record(db: Session, record_id: int) -> recordschema.HeartRateRecord: + """Deletes the heart rate record with the provided id from the db.""" + + db_record = db.query(recordmodel.HeartRateRecord).filter(recordmodel.HeartRateRecord.id == record_id).first() + if not db_record: + raise NotFoundException(f"Record with id '{record_id}' does not exist.") + record_copy = recordschema.HeartRateRecord.from_orm(db_record) + + db.delete(db_record) + db.commit() + + return record_copy + + +def create_avpu_score_record(db: Session, record: recordschema.AvpuScoreRecordCreate, device_id: int) -> recordschema.AvpuScoreRecord: + """Creates the specified AVPU score record in the database.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + db_record = recordmodel.AvpuScoreRecord( + value=record.value, + measured=record.measured, + device_id=device_id, + ) + db.add(db_record) + db.commit() + db.refresh(db_record) + + return recordschema.AvpuScoreRecord.from_orm(db_record) + + +def read_avpu_score_records_by_device( + db: Session, device_id: int, + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ) -> list[recordschema.AvpuScoreRecord]: + """Queries the db for a range of avpu score records captured by the device with the specified id.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + filters = [recordmodel.AvpuScoreRecord.device == db_device] + if since: + filters.append(recordmodel.AvpuScoreRecord.measured >= since) + if until: + filters.append(recordmodel.AvpuScoreRecord.measured <= until) + + return db.query(recordmodel.AvpuScoreRecord).filter(*filters).order_by(recordmodel.AvpuScoreRecord.measured.desc()).offset(skip).limit(limit).all() + + +def delete_avpu_score_record(db: Session, record_id: int) -> recordschema.AvpuScoreRecord: + """Deletes the avpu score record with the provided id from the db.""" + + db_record = db.query(recordmodel.AvpuScoreRecord).filter(recordmodel.AvpuScoreRecord.id == record_id).first() + if not db_record: + raise NotFoundException(f"Record with id '{record_id}' does not exist.") + record_copy = recordschema.AvpuScoreRecord.from_orm(db_record) + + db.delete(db_record) + db.commit() + + return record_copy + + +def create_body_temperature_record(db: Session, record: recordschema.BodyTemperatureRecordCreate, device_id: int) -> recordschema.BodyTemperatureRecord: + """Creates the specified body temperature record in the database.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + db_record = recordmodel.BodyTemperatureRecord( + value=record.value, + measured=record.measured, + device_id=device_id, + ) + db.add(db_record) + db.commit() + db.refresh(db_record) + + return recordschema.BodyTemperatureRecord.from_orm(db_record) + + +def read_body_temperature_records_by_device( + db: Session, device_id: int, + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ) -> list[recordschema.BodyTemperatureRecord]: + """Queries the db for a range of body temperature records captured by the device with the specified id.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + filters = [recordmodel.BodyTemperatureRecord.device == db_device] + if since: + filters.append(recordmodel.BodyTemperatureRecord.measured >= since) + if until: + filters.append(recordmodel.BodyTemperatureRecord.measured <= until) + + return db.query(recordmodel.BodyTemperatureRecord).filter(*filters).order_by(recordmodel.BodyTemperatureRecord.measured.desc()).offset(skip).limit(limit).all() + + +def delete_body_temperature_record(db: Session, record_id: int) -> recordschema.BodyTemperatureRecord: + """Deletes the body temperature record with the provided id from the db.""" + + db_record = db.query(recordmodel.BodyTemperatureRecord).filter(recordmodel.BodyTemperatureRecord.id == record_id).first() + if not db_record: + raise NotFoundException(f"Record with id '{record_id}' does not exist.") + record_copy = recordschema.BodyTemperatureRecord.from_orm(db_record) + + db.delete(db_record) + db.commit() + + return record_copy + + +def create_blood_pressure_record(db: Session, record: recordschema.BloodPressureRecordCreate, device_id: int) -> recordschema.BloodPressureRecord: + """Creates the specified blood pressure record in the database.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + db_record = recordmodel.BloodPressureRecord( + value_systolic=record.value_systolic, + value_diastolic=record.value_diastolic, + measured=record.measured, + device_id=device_id, + ) + db.add(db_record) + db.commit() + db.refresh(db_record) + + return recordschema.BloodPressureRecord.from_orm(db_record) + + +def read_blood_pressure_records_by_device( + db: Session, device_id: int, + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ) -> list[recordschema.BloodPressureRecord]: + """Queries the db for a range of blood pressure records captured by the device with the specified id.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + filters = [recordmodel.BloodPressureRecord.device == db_device] + if since: + filters.append(recordmodel.BloodPressureRecord.measured >= since) + if until: + filters.append(recordmodel.BloodPressureRecord.measured <= until) + + return db.query(recordmodel.BloodPressureRecord).filter(*filters).order_by(recordmodel.BloodPressureRecord.measured.desc()).offset(skip).limit(limit).all() + + +def delete_blood_pressure_record(db: Session, record_id: int) -> recordschema.BloodPressureRecord: + """Deletes the blood pressure record with the provided id from the db.""" + + db_record = db.query(recordmodel.BloodPressureRecord).filter(recordmodel.BloodPressureRecord.id == record_id).first() + if not db_record: + raise NotFoundException(f"Record with id '{record_id}' does not exist.") + record_copy = recordschema.BloodPressureRecord.from_orm(db_record) + + db.delete(db_record) + db.commit() + + return record_copy + + +def create_blood_oxygen_record(db: Session, record: recordschema.BloodOxygenRecordCreate, device_id: int) -> recordschema.BloodOxygenRecord: + """Creates the specified blood oxygen record in the database.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + db_record = recordmodel.BloodOxygenRecord( + value=record.value, + measured=record.measured, + device_id=device_id, + ) + db.add(db_record) + db.commit() + db.refresh(db_record) + + return recordschema.BloodOxygenRecord.from_orm(db_record) + + +def read_blood_oxygen_records_by_device( + db: Session, device_id: int, + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ) -> list[recordschema.BloodOxygenRecord]: + """Queries the db for a range of blood oxygen records captured by the device with the specified id.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + filters = [recordmodel.BloodOxygenRecord.device == db_device] + if since: + filters.append(recordmodel.BloodOxygenRecord.measured >= since) + if until: + filters.append(recordmodel.BloodOxygenRecord.measured <= until) + + return db.query(recordmodel.BloodOxygenRecord).filter(*filters).order_by(recordmodel.BloodOxygenRecord.measured.desc()).offset(skip).limit(limit).all() + + +def delete_blood_oxygen_record(db: Session, record_id: int) -> recordschema.BloodOxygenRecord: + """Deletes the blood oxygen record with the provided id from the db.""" + + db_record = db.query(recordmodel.BloodOxygenRecord).filter(recordmodel.BloodOxygenRecord.id == record_id).first() + if not db_record: + raise NotFoundException(f"Record with id '{record_id}' does not exist.") + record_copy = recordschema.BloodOxygenRecord.from_orm(db_record) + + db.delete(db_record) + db.commit() + + return record_copy + + +def create_respiration_score_record(db: Session, record: recordschema.RespirationScoreRecordCreate, device_id: int) -> recordschema.RespirationScoreRecord: + """Creates the specified respiration score record in the database.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + db_record = recordmodel.RespirationScoreRecord( + value=record.value, + measured=record.measured, + device_id=device_id, + ) + db.add(db_record) + db.commit() + db.refresh(db_record) + + return recordschema.RespirationScoreRecord.from_orm(db_record) + + +def read_respiration_score_records_by_device( + db: Session, device_id: int, + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ) -> list[recordschema.RespirationScoreRecord]: + """Queries the db for a range of respiration score records captured by the device with the specified id.""" + + db_device = db.query(devicemodel.Device).filter(devicemodel.Device.id == device_id).first() + if not db_device: + raise NotFoundException(f"Device with id '{device_id}' does not exist.") + + filters = [recordmodel.RespirationScoreRecord.device == db_device] + if since: + filters.append(recordmodel.RespirationScoreRecord.measured >= since) + if until: + filters.append(recordmodel.RespirationScoreRecord.measured <= until) + + return db.query(recordmodel.RespirationScoreRecord).filter(*filters).order_by(recordmodel.RespirationScoreRecord.measured.desc()).offset(skip).limit(limit).all() + + +def delete_respiration_score_record(db: Session, record_id: int) -> recordschema.RespirationScoreRecord: + """Deletes the respiration score record with the provided id from the db.""" + + db_record = db.query(recordmodel.RespirationScoreRecord).filter(recordmodel.RespirationScoreRecord.id == record_id).first() + if not db_record: + raise NotFoundException(f"Record with id '{record_id}' does not exist.") + record_copy = recordschema.RespirationScoreRecord.from_orm(db_record) + + db.delete(db_record) + db.commit() + + return record_copy diff --git a/backend/crud/users.py b/backend/crud/users.py index b7b67c6..ce3806f 100644 --- a/backend/crud/users.py +++ b/backend/crud/users.py @@ -95,7 +95,7 @@ def read_user_by_email(db: Session, email: str) -> userschema.User | None: return _fill_missing_user_fields(db_user) -def read_users(db: Session, skip: int = 0, limit: int = 100) -> list[userschema.User]: +def read_users(db: Session, skip: int = 0, limit: int = 0) -> list[userschema.User]: """Returns an unfiltered range (by id) of users in the database.""" db_users = db.query(usermodel.User).offset(skip).limit(limit).all() diff --git a/backend/main.py b/backend/main.py index a114fe4..4deeca6 100644 --- a/backend/main.py +++ b/backend/main.py @@ -10,6 +10,8 @@ from backend.routes.devices import router as devices_router from backend.routes.devices import tag_metadata as devices_tags from backend.routes.users import router as users_router from backend.routes.users import tag_metadata as users_tags +from backend.routes.records import router as records_router +from backend.routes.records import tag_metadata as records_tags s = config.get_settings() @@ -25,11 +27,13 @@ app = FastAPI( openapi_tags=[ users_tags, devices_tags, + records_tags, ], ) app.include_router(devices_router) app.include_router(users_router) +app.include_router(records_router) @app.get("/hello/") diff --git a/backend/models/devices.py b/backend/models/devices.py index 37106f6..53f5756 100644 --- a/backend/models/devices.py +++ b/backend/models/devices.py @@ -7,6 +7,7 @@ from sqlalchemy.sql.functions import now from sqlalchemy.orm import relationship from backend.database.engine import Base +from backend.models import records class DeviceModel(Base): @@ -35,9 +36,9 @@ class Device(Base): model = relationship(DeviceModel, back_populates="instances", uselist=False) owner = relationship("Patient", back_populates="devices", uselist=False) - heart_rate_records = relationship("HeartRateRecord", back_populates="device", uselist=True, cascade="all, delete") - avpu_score_records = relationship("AvpuScoreRecord", back_populates="device", uselist=True, cascade="all, delete") - blood_pressure_records = relationship("BloodPressureRecord", back_populates="device", uselist=True, cascade="all, delete") - blood_oxygen_records = relationship("BloodOxygenRecord", back_populates="device", uselist=True, cascade="all, delete") - body_temperature_records = relationship("BodyTemperatureRecord", back_populates="device", uselist=True, cascade="all, delete") - respiration_score_records = relationship("RespirationScoreRecord", back_populates="device", uselist=True, cascade="all, delete") + heart_rate_records = relationship(records.HeartRateRecord, back_populates="device", uselist=True, cascade="all, delete") + avpu_score_records = relationship(records.AvpuScoreRecord, back_populates="device", uselist=True, cascade="all, delete") + blood_pressure_records = relationship(records.BloodPressureRecord, back_populates="device", uselist=True, cascade="all, delete") + blood_oxygen_records = relationship(records.BloodOxygenRecord, back_populates="device", uselist=True, cascade="all, delete") + body_temperature_records = relationship(records.BodyTemperatureRecord, back_populates="device", uselist=True, cascade="all, delete") + respiration_score_records = relationship(records.RespirationScoreRecord, back_populates="device", uselist=True, cascade="all, delete") diff --git a/backend/models/records.py b/backend/models/records.py index 8c5df9e..521dde6 100644 --- a/backend/models/records.py +++ b/backend/models/records.py @@ -6,7 +6,6 @@ from sqlalchemy import Column, ForeignKey, DateTime, SmallInteger, Integer, Enum from sqlalchemy.orm import relationship from backend.database.engine import Base -from backend.models.devices import Device class HeartRateRecord(Base): @@ -19,7 +18,7 @@ class HeartRateRecord(Base): value = Column('value', SmallInteger, nullable=False) device_id = Column('device_id', Integer, ForeignKey('devices.id', ondelete="CASCADE"), nullable=False, index=True) - device = relationship(Device, back_populates="heart_rate_records", uselist=False) + device = relationship("Device", back_populates="heart_rate_records", uselist=False) class AvpuScore(enum.Enum): @@ -39,7 +38,7 @@ class AvpuScoreRecord(Base): value = Column('value', Enum(AvpuScore), nullable=False) device_id = Column('device_id', Integer, ForeignKey('devices.id', ondelete="CASCADE"), nullable=False, index=True) - device = relationship(Device, back_populates="avpu_score_records", uselist=False) + device = relationship("Device", back_populates="avpu_score_records", uselist=False) class BodyTemperatureRecord(Base): @@ -55,7 +54,7 @@ class BodyTemperatureRecord(Base): value = Column('value', Numeric(precision=4, scale=2, decimal_return_scale=2), nullable=False) device_id = Column('device_id', Integer, ForeignKey('devices.id', ondelete="CASCADE"), nullable=False, index=True) - device = relationship(Device, back_populates="body_temperature_records", uselist=False) + device = relationship("Device", back_populates="body_temperature_records", uselist=False) class BloodPressureRecord(Base): @@ -69,7 +68,7 @@ class BloodPressureRecord(Base): value_diastolic = Column('value_diastolic', SmallInteger, nullable=False) device_id = Column('device_id', Integer, ForeignKey('devices.id', ondelete="CASCADE"), nullable=False, index=True) - device = relationship(Device, back_populates="blood_pressure_records", uselist=False) + device = relationship("Device", back_populates="blood_pressure_records", uselist=False) class BloodOxygenRecord(Base): @@ -85,7 +84,7 @@ class BloodOxygenRecord(Base): value = Column('value', Numeric(precision=5, scale=2, decimal_return_scale=2), nullable=False) device_id = Column('device_id', Integer, ForeignKey('devices.id', ondelete="CASCADE"), nullable=False, index=True) - device = relationship(Device, back_populates="blood_oxygen_records", uselist=False) + device = relationship("Device", back_populates="blood_oxygen_records", uselist=False) class RespirationScore(enum.Enum): @@ -107,4 +106,4 @@ class RespirationScoreRecord(Base): value = Column('value', Enum(RespirationScore), nullable=False) device_id = Column('device_id', Integer, ForeignKey('devices.id', ondelete="CASCADE"), nullable=False, index=True) - device = relationship(Device, back_populates="respiration_score_records", uselist=False) + device = relationship("Device", back_populates="respiration_score_records", uselist=False) diff --git a/backend/routes/devices.py b/backend/routes/devices.py index 9fc780b..c1bb7be 100644 --- a/backend/routes/devices.py +++ b/backend/routes/devices.py @@ -5,7 +5,7 @@ from sqlalchemy.orm import Session from backend.database.engine import get_db from backend.schemas import devices as deviceschema -import backend.crud.devices as devicecrud +from backend.crud import devices as devicecrud from backend.exceptions import NotFoundException diff --git a/backend/routes/records.py b/backend/routes/records.py new file mode 100644 index 0000000..389c640 --- /dev/null +++ b/backend/routes/records.py @@ -0,0 +1,191 @@ +"""This module contains endpoints for operations related to vitals records.""" + +from datetime import datetime +from datetime import datetime + +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session + +from backend.database.engine import get_db +from backend.schemas import records as recordschema +from backend.crud import records as recordcrud +from backend.exceptions import NotFoundException + + +router = APIRouter( + prefix="/devices", + tags=["records"] +) + +tag_metadata = { + "name": "records", + "description": "Operations related to vitals records." +} + + +@router.post("/{device_id}/heart-rate/", response_model=recordschema.HeartRateRecord) +def create_heart_rate_record(record: recordschema.HeartRateRecordCreate, device_id: int, db: Session = Depends(get_db)): + try: + return recordcrud.create_heart_rate_record(db, record, device_id) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.get("/{device_id}/heart-rate/", response_model=list[recordschema.HeartRateRecord]) +def read_heart_rate_records( + device_id: int, db: Session = Depends(get_db), + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ): + try: + return recordcrud.read_heart_rate_records_by_device(db, device_id, since, until, skip, limit) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.delete("/{device_id}/heart-rate/{record_id}", response_model=recordschema.HeartRateRecord) +def delete_heart_rate_record(id: int, db: Session = Depends(get_db)): + try: + return recordcrud.delete_heart_rate_record(db, id) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.post("/{device_id}/avpu-score/", response_model=recordschema.AvpuScoreRecord) +def create_avpu_score_record(record: recordschema.AvpuScoreRecordCreate, device_id: int, db: Session = Depends(get_db)): + try: + return recordcrud.create_avpu_score_record(db, record, device_id) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.get("/{device_id}/avpu-score/", response_model=list[recordschema.AvpuScoreRecord]) +def read_avpu_score_records( + device_id: int, db: Session = Depends(get_db), + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ): + try: + return recordcrud.read_avpu_score_records_by_device(db, device_id, since, until, skip, limit) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.delete("/{device_id}/avpu-score/{record_id}", response_model=recordschema.AvpuScoreRecord) +def delete_avpu_score_record(id: int, db: Session = Depends(get_db)): + try: + return recordcrud.delete_avpu_score_record(db, id) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.post("/{device_id}/body-temperature/", response_model=recordschema.BodyTemperatureRecord) +def create_body_temperature_record(record: recordschema.BodyTemperatureRecordCreate, device_id: int, db: Session = Depends(get_db)): + try: + return recordcrud.create_body_temperature_record(db, record, device_id) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.get("/{device_id}/body-temperature/", response_model=list[recordschema.BodyTemperatureRecord]) +def read_body_temperature_records( + device_id: int, db: Session = Depends(get_db), + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ): + try: + return recordcrud.read_body_temperature_records_by_device(db, device_id, since, until, skip, limit) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.delete("/{device_id}/body-temperature/{record_id}", response_model=recordschema.BodyTemperatureRecord) +def delete_body_temperature_record(id: int, db: Session = Depends(get_db)): + try: + return recordcrud.delete_body_temperature_record(db, id) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.post("/{device_id}/blood-pressure/", response_model=recordschema.BloodPressureRecord) +def create_blood_pressure_record(record: recordschema.BloodPressureRecordCreate, device_id: int, db: Session = Depends(get_db)): + try: + return recordcrud.create_blood_pressure_record(db, record, device_id) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.get("/{device_id}/blood-pressure/", response_model=list[recordschema.BloodPressureRecord]) +def read_blood_pressure_records( + device_id: int, db: Session = Depends(get_db), + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ): + try: + return recordcrud.read_blood_pressure_records_by_device(db, device_id, since, until, skip, limit) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.delete("/{device_id}/blood-pressure/{record_id}", response_model=recordschema.BloodPressureRecord) +def delete_blood_pressure_record(id: int, db: Session = Depends(get_db)): + try: + return recordcrud.delete_blood_pressure_record(db, id) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.post("/{device_id}/blood-oxygen/", response_model=recordschema.BloodOxygenRecord) +def create_blood_oxygen_record(record: recordschema.BloodOxygenRecordCreate, device_id: int, db: Session = Depends(get_db)): + try: + return recordcrud.create_blood_oxygen_record(db, record, device_id) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.get("/{device_id}/blood-oxygen/", response_model=list[recordschema.BloodOxygenRecord]) +def read_blood_oxygen_records( + device_id: int, db: Session = Depends(get_db), + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ): + try: + return recordcrud.read_blood_oxygen_records_by_device(db, device_id, since, until, skip, limit) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.delete("/{device_id}/blood-oxygen/{record_id}", response_model=recordschema.BloodOxygenRecord) +def delete_blood_oxygen_record(id: int, db: Session = Depends(get_db)): + try: + return recordcrud.delete_blood_oxygen_record(db, id) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.post("/{device_id}/respiration-score/", response_model=recordschema.RespirationScoreRecord) +def create_respiration_score_record(record: recordschema.RespirationScoreRecordCreate, device_id: int, db: Session = Depends(get_db)): + try: + return recordcrud.create_respiration_score_record(db, record, device_id) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.get("/{device_id}/respiration-score/", response_model=list[recordschema.RespirationScoreRecord]) +def read_respiration_score_records( + device_id: int, db: Session = Depends(get_db), + since: datetime | None = None, until: datetime | None = None, + skip: int = 0, limit: int = 100, + ): + try: + return recordcrud.read_respiration_score_records_by_device(db, device_id, since, until, skip, limit) + except NotFoundException as e: + raise HTTPException(404, str(e)) + + +@router.delete("/{device_id}/respiration-score/{record_id}", response_model=recordschema.RespirationScoreRecord) +def delete_respiration_score_record(id: int, db: Session = Depends(get_db)): + try: + return recordcrud.delete_respiration_score_record(db, id) + except NotFoundException as e: + raise HTTPException(404, str(e)) diff --git a/backend/routes/users.py b/backend/routes/users.py index 874fc9b..78e0cfb 100644 --- a/backend/routes/users.py +++ b/backend/routes/users.py @@ -4,8 +4,8 @@ from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from backend.database.engine import get_db -import backend.schemas.users as userschema -import backend.crud.users as usercrud +from backend.schemas import users as userschema +from backend.crud import users as usercrud from backend.exceptions import NotFoundException @@ -20,15 +20,17 @@ tag_metadata = { } -@router.post("/users/", response_model=userschema.User) +@router.post("/", response_model=userschema.User) def create_user(user: userschema.UserCreate, db: Session = Depends(get_db)): - existing_user = usercrud.read_user_by_email(db, email=user.email) - if existing_user: + try: + # An exception is expected, because we need to check if a user with this email is already registered + usercrud.read_user_by_email(db, email=user.email) raise HTTPException(400, "A user with this email address is already registered.") - return usercrud.create_user(db=db, user=user) + except NotFoundException: + return usercrud.create_user(db=db, user=user) -@router.get("/users/{id}", response_model=userschema.User) +@router.get("/{id}", response_model=userschema.User) def read_user(id: int, db: Session = Depends(get_db)): try: return usercrud.read_user(db=db, id=id) @@ -36,13 +38,13 @@ def read_user(id: int, db: Session = Depends(get_db)): raise HTTPException(404, str(e)) -@router.get("/users/", response_model=list[userschema.User]) +@router.get("/", response_model=list[userschema.User]) def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): users = usercrud.read_users(db=db, skip=skip, limit=limit) return users -@router.patch("/users/{id}", response_model=userschema.User) +@router.patch("/{id}", response_model=userschema.User) def update_user(id: int, user: userschema.UserUpdate, db: Session = Depends(get_db)): try: return usercrud.update_user(db=db, user=user, id=id) @@ -50,7 +52,7 @@ def update_user(id: int, user: userschema.UserUpdate, db: Session = Depends(get_ raise HTTPException(404, str(e)) -@router.delete("/users/{id}", response_model=userschema.User) +@router.delete("/{id}", response_model=userschema.User) def delete_user(id: int, db: Session = Depends(get_db)): try: return usercrud.delete_user(db=db, id=id) diff --git a/backend/schemas/records.py b/backend/schemas/records.py index d54582b..7b92be3 100644 --- a/backend/schemas/records.py +++ b/backend/schemas/records.py @@ -13,11 +13,10 @@ class AbstractRecordCreate(BaseModel, ABC): """Base class containing fields common to all vitals records during creation.""" measured: datetime - device_id: int @validator('measured') def assert_measured_is_valid(cls, measured: datetime) -> datetime: - if measured >= datetime.now(): + if measured >= datetime.now(tz=measured.tzinfo): raise ValueError("Time of measurement cannot be in the future.") return measured @@ -30,10 +29,13 @@ class AbstractRecord(BaseModel, ABC): @validator('measured') def assert_measured_is_valid(cls, measured: datetime) -> datetime: - if measured >= datetime.now(): + if measured >= datetime.now(tz=measured.tzinfo): raise ValueError("Time of measurement cannot be in the future.") return measured + class Config: + orm_mode = True + class AbstractHeartRateRecord(BaseModel, ABC): value: int @@ -133,8 +135,8 @@ class BloodOxygenRecord(AbstractRecord, AbstractBloodOxygenRecord): class AbstractRespirationScoreScoreRecord(BaseModel, ABC): value: RespirationScore -class RespirationScoreScoreRecordCreate(AbstractRecordCreate, AbstractRespirationScoreScoreRecord): +class RespirationScoreRecordCreate(AbstractRecordCreate, AbstractRespirationScoreScoreRecord): pass -class RespirationScoreScoreRecord(AbstractRecord, AbstractRespirationScoreScoreRecord): +class RespirationScoreRecord(AbstractRecord, AbstractRespirationScoreScoreRecord): pass