From dcd2ff5b8919d6525a76e85e2537a82d99335a34 Mon Sep 17 00:00:00 2001 From: HeshamTB Date: Thu, 9 Jun 2022 21:52:42 +0300 Subject: [PATCH] sql_app: Split doors and monitors A alot of interface and database boiler plate Signed-off-by: HeshamTB --- sql_app/auth_helper.py | 12 ++++- sql_app/crud.py | 56 +++++++++++++++++--- sql_app/init_db.py | 16 ++++-- sql_app/main.py | 114 +++++++++++++++++++++++++++++++---------- sql_app/models.py | 21 +++++++- sql_app/schemas.py | 25 ++++++++- 6 files changed, 200 insertions(+), 44 deletions(-) diff --git a/sql_app/auth_helper.py b/sql_app/auth_helper.py index 5f41f4a..ff9a66b 100644 --- a/sql_app/auth_helper.py +++ b/sql_app/auth_helper.py @@ -55,4 +55,14 @@ def valid_iot_token(token : str, db: Session): mac_signed = payload.get("bluetooth_mac") device = crud.get_iot_entity_by_bluetooth_mac(db, mac_signed) - return device \ No newline at end of file + return device + +def valid_monitor_token(token: str, db: Session): + try: + payload = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGO]) + except jwt.DecodeError: + return None + + mac_signed = payload.get("bluetooth_mac") + monitor = crud.get_monitor_bluetooth(db, mac_signed) + return monitor \ No newline at end of file diff --git a/sql_app/crud.py b/sql_app/crud.py index f166f45..cb89580 100644 --- a/sql_app/crud.py +++ b/sql_app/crud.py @@ -17,7 +17,7 @@ from warnings import warn def get_user(db: Session, user_id: int) -> models.User: return db.query(models.User).get(user_id) -def get_iot_entity(db: Session, id: int): +def get_iot_entity(db: Session, id: int) -> models.IotEntity: return db.query(models.IotEntity).get(id) def get_iot_entity_by_description(db: Session, description: str): @@ -44,8 +44,12 @@ def get_access_log_for_door_by_door_mac(db: Session, iot_id: str): def get_access_log_for_user_by_id(db: Session, id : str): return db.query(models.DoorAccessLog).filter(models.DoorAccessLog.user_id == id).all() -def get_room_data_now(db: Session) -> models.RoomSensorData: - return db.query(models.RoomSensorData)[-1] +# def get_room_data_now(db: Session, door_id: int) -> models.RoomSensorData: +# door = get_iot_entity(db, door_id) +# monitor : models.Monitors = door.monitor +# if not monitor: return -1 +# if len(monitor.sensor_history) == 0: return -2 +# return monitor.sensor_history[-1] def create_user(db: Session, user: schemas.UserCreate): key = crypto.gen_new_key(user.password) @@ -73,6 +77,8 @@ def update_user_password(db: Session, user: models.User, request: schemas.UserUp def get_iot_entities(db: Session, skip: int = 0, limit: int = 100): return db.query(models.IotEntity).offset(skip).limit(limit).all() +def get_monitors(db: Session, skip: int = 0, limit: int = 100): + return db.query(models.Monitors).offset(skip).limit(limit).all() def create_iot_entity(db: Session, iot_entity: schemas.IotEntityCreate): db_item = models.IotEntity(bluetooth_mac=iot_entity.bluetooth_mac, @@ -82,6 +88,37 @@ def create_iot_entity(db: Session, iot_entity: schemas.IotEntityCreate): db.refresh(db_item) return db_item +def create_monitor(db: Session, monitor: schemas.IotEntityBase): + db_item = models.Monitors(bluetooth_mac=monitor.bluetooth_mac, + description=monitor.description) + + db.add(db_item) + db.commit() + db.refresh(db_item) + return db_item + +def get_monitor(db: Session, id: int) -> models.Monitors: + return db.query(models.Monitors).get(id) + +def get_monitor_bluetooth(db: Session, bluetooth_mac: str) -> models.Monitors: + return db.query(models.Monitors).filter(models.Monitors.bluetooth_mac == bluetooth_mac).first() + +def update_monitor(db: Session, monitor: models.Monitors): + db.add(monitor) + db.commit() + db.refresh(monitor) + +def update_monitor_readings(db: Session, monitor_upadte: schemas.MonitorUpdateReadings, bluetooth_mac: str): + monitor = get_monitor_bluetooth(db, bluetooth_mac) + monitor.humidity = monitor_upadte.humidity + monitor.people = monitor_upadte.people + monitor.smoke_sensor_reading = monitor_upadte.smoke_sensor_reading + monitor.temperature = monitor_upadte.temperature + + db.add(monitor) + db.commit() + db.refresh(monitor) + def create_user_link_to_iot(db: Session, user_id: int, iot_dev_id: int): # Ensure link is not already present and it does not allow duplicates link = db.query(models.UserAuthToIoTDev).filter(models.UserAuthToIoTDev.user_id == user_id).filter(models.UserAuthToIoTDev.iot_id == iot_dev_id).first() @@ -168,12 +205,14 @@ def record_door_access_log(db: Session, entry: schemas.DoorAccessLog): db.commit() db.refresh(db_item) -def record_room_sensor_data(db: Session, entry: schemas.IotMonitorRoomInfo): +def record_room_sensor_data(db: Session, entry: schemas.MonitorUpdateReadings, + monitor :models.Monitors): db_item = models.RoomSensorData(humidity=entry.humidity, people=entry.people, temperature=entry.temperature, smoke_sensor_reading=entry.smoke_sensor_reading, - timestamp=datetime.now()) + timestamp=datetime.now(), + monitor_id=monitor.id) db.add(db_item) db.commit() db.refresh(db_item) @@ -190,9 +229,10 @@ def record_user_connection(db: Session, user: models.User, time: datetime): db.commit() db.refresh(entry) -def get_sensor_data_for_room(db: Session, skip: int = 0, limit: int = 100): - data = db.query(models.RoomSensorData).offset(skip).limit(limit).all() - return data +# def get_sensor_data_for_room(db: Session, monitor_id: int, count_last: int): +# data = db.query(models.RoomSensorData).all() +# if not data or len(data) == 0: return -1 +# return data[-count_last] def update_user_status(db: Session, user: models.User, state: bool): user.is_active = state diff --git a/sql_app/init_db.py b/sql_app/init_db.py index 547d81a..bebe34f 100644 --- a/sql_app/init_db.py +++ b/sql_app/init_db.py @@ -57,15 +57,15 @@ def init_door(): def init_monitor(): iot_monitor = schemas.IotEntityCreate(bluetooth_mac="ff:ff:00:ff", description="Iot Lab Monitor") - monitor_exists = crud.get_iot_entity_by_bluetooth_mac(db, iot_monitor.bluetooth_mac) + monitor_exists = crud.get_monitor_bluetooth(db, iot_monitor.bluetooth_mac) if monitor_exists: return - crud.create_iot_entity(db, iot_monitor) + crud.create_monitor(db, iot_monitor) def init_allowance(): crud.create_user_link_to_iot(db, 1, 1) def init_sensor_data(): - + monitor = crud.get_monitor(db, 1) for i in range(50): room_data = \ schemas.\ @@ -75,7 +75,7 @@ def init_sensor_data(): temperature=randint(18, 27), smoke_sensor_reading=randint(150, 700), token='dummy') - crud.record_room_sensor_data(db, room_data) + crud.record_room_sensor_data(db, room_data, monitor) def init_open_close_requests(): user = crud.get_user_by_email(db, "hisham@banafa.com.sa") @@ -114,7 +114,12 @@ def init_user_connections(): crud.record_user_connection(db, users[i], datetime.now()) crud.record_user_connection(db, users[i], datetime.now()) crud.record_user_connection(db, users[i], datetime.now()) - + +def init_link_room_monitor(): + monitor = crud.get_monitor(db, 1) + door = crud.get_iot_entity(db, 1) + monitor.door = door + crud.update_monitor(db, monitor) def init(): init_user() @@ -124,4 +129,5 @@ def init(): init_sensor_data() init_open_close_requests() init_user_connections() + init_link_room_monitor() \ No newline at end of file diff --git a/sql_app/main.py b/sql_app/main.py index 2f46730..1bf1b70 100644 --- a/sql_app/main.py +++ b/sql_app/main.py @@ -135,22 +135,23 @@ def get_iot_access_list_for_user(db: Session = Depends(get_db), current_user: sc user = crud.get_user_by_username(db, current_user.username) access_list = list() for device in user.authorized_devices: - dev_db : models.IotEntity = device - sensors = crud.get_room_data_now(db) - if not sensors: raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + door : models.IotEntity = device + monitor : models.Monitors = door.monitor + if not monitor: raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="No Room link") entry : schemas.RoomOverview = schemas.RoomOverview( - id=dev_db.id, - description=dev_db.description, - bluetooth_mac=dev_db.bluetooth_mac, - open_request=dev_db.open_request, - time_seconds=dev_db.time_seconds, - acces_list_counter=dev_db.acces_list_counter, - humidity=sensors.humidity, - people=sensors.people, - temperature=sensors.temperature, - smoke_sensor_reading=sensors.smoke_sensor_reading, - force_close=dev_db.force_close + id=door.id, + description=door.description, + bluetooth_mac=door.bluetooth_mac, + open_request=door.open_request, + time_seconds=door.time_seconds, + acces_list_counter=door.acces_list_counter, + humidity=monitor.humidity, + people=monitor.people, + temperature=monitor.temperature, + smoke_sensor_reading=monitor.smoke_sensor_reading, + force_close=door.force_close, + state=door.state ) access_list.append(entry) #crud.record_user_connection(db, user, datetime.now()) @@ -191,12 +192,23 @@ def read_iot_entities(skip: int = 0, limit: int = 100, db: Session = Depends(get iot_entities = crud.get_iot_entities(db, skip=skip, limit=limit) return iot_entities +@app.get("/admin/monitors/", response_model=List[schemas.Monitor], tags=['Admin']) +def read_iot_monitors(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + monitors = crud.get_monitors(db, skip=skip, limit=limit) + return monitors + # TODO: Can duplicate @app.post("/admin/iotentities/create", response_model=schemas.IotEntity, tags=['Admin']) def create_iot_entities(iot_entity: schemas.IotEntityCreate, db: Session = Depends(get_db)): iot_entities = crud.create_iot_entity(db, iot_entity) return iot_entities +@app.post("/admin/monitor/create", response_model=schemas.Monitor, tags=['Admin']) +def create_monitor(iot_entity: schemas.IotEntityBase, + db: Session = Depends(get_db)): + monitor = crud.create_monitor(db, iot_entity) + return monitor + @app.get("/admin/users/{user_id}", response_model=schemas.User, tags=['Admin']) def read_user(user_id: int, db: Session = Depends(get_db)): db_user = crud.get_user(db, user_id=user_id) @@ -275,13 +287,22 @@ def deactiveate_user(user_id: int, db:Session = Depends(get_db)): crud.update_user_status(db, user, True) @app.post("/admin/iotdevice/gentoken/", response_model=schemas.Token, tags=['Admin']) -def generate_token_for_iot_device(bluetooth_mac : schemas.IotBluetoothMac, - api_key: APIKey = Depends(auth_helper.valid_api_key)): +def generate_token_for_iot_device(bluetooth_mac : schemas.IotBluetoothMac): + # api_key: APIKey = Depends(auth_helper.valid_api_key) # We get here after a valid admin key, so send back permenant token data = {"bluetooth_mac": bluetooth_mac.bluetooth_mac} tkn = auth_helper.create_iot_dev_token(data) return {"access_token": tkn, "token_type": "bearer"} +@app.patch("/admin/link/monitor/{monitor_id}/door/{door_id}", tags=['Admin']) +def link_monitor_with_door(monitor_id: int, door_id: int, + db: Session = Depends(get_db)): + monitor = crud.get_monitor(db, monitor_id) + door = crud.get_iot_entity(db, door_id) + monitor.door = door + crud.update_monitor(db, monitor) + return monitor + @app.post("/admin/user/accesslog/email/", tags=['Admin']) def get_access_log_history_for_user(request : schemas.UserAccessLogRequestEmail, db : Session = Depends(get_db)): @@ -296,14 +317,49 @@ def get_access_log_history_for_user(request : schemas.UserAccessLogRequestUserna if not user: raise HTTPException(status.HTTP_404_NOT_FOUND, detail="User not found") return user.access_log -@app.get("/admin/roominfo/now/", tags=['Admin']) -def get_room_data(db: Session = Depends(get_db)): - return crud.get_room_data_now(db) +@app.get("/admin/roominfo/{door_id}/now", tags=['Admin']) +def get_room_data(door_id: int, db: Session = Depends(get_db)): + door = crud.get_iot_entity(db, door_id) + if not door: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail="Door not found") + monitor : models.Monitors = door.monitor + if not monitor: + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="No Room link") + data = monitor.sensor_history + if not data or len(data) == 0: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail="No Sensor data") + return data[-1] -@app.get("/admin/roominfo/history/sensors", tags=['Admin']) -def get_all_sensor_history(skip: int = 0, limit: int = 100, +@app.get("/admin/roominfo/{monitor_id}/now", tags=['Admin']) +def get_room_data(monitor_id: int, db: Session = Depends(get_db)): + monitor = crud.get_monitor(db, monitor_id) + if not monitor: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail="Monitor not found") + if not monitor.door_id: + raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Monitor not linked") + + data = crud.get_room_data_now(db, monitor.door_id) + if data == -1: raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="No Room link") + if data == -2: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail="No Sensor data") + return data + +@app.get("/admin/roominfo/{monitor_id}/last/{count}", tags=['Admin']) +def get_all_sensor_history(monitor_id: int, count: int, db: Session = Depends(get_db)): - return crud.get_sensor_data_for_room(db, skip, limit) + monitor = crud.get_monitor(db, monitor_id) + if not monitor: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail="Monitor not found") + data = monitor.sensor_history + if not data or len(data) == 0: + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, + detail="No Sensor data") + return data[-count:] @app.post("/admin/roominfo/accesslog",response_model=List[schemas.DoorAccessLog], tags=['Admin']) def get_access_log_for_door(request : schemas.AccessLogRequest, @@ -335,14 +391,14 @@ def polling_method_for_iot_entity(request: schemas.IotDoorPollingRequest, return response @app.post("/iotdevice/monitor/status", tags=['Iot']) -def polling_method_for_room_monitor(request: schemas.IotMonitorRoomInfo, +def polling_method_for_room_monitor(request: schemas.MonitorUpdateReadings, db: Session = Depends(get_db)): - device : schemas.IotEntity = auth_helper.valid_iot_token(request.token, db) + device : schemas.Monitor = auth_helper.valid_monitor_token(request.token, db) if not device: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Could not validate credentials") - crud.record_room_sensor_data(db, request) + crud.record_room_sensor_data(db, request, device) return request @app.post("/iotdevice/door/users", response_class=PlainTextResponse, tags=['Iot']) @@ -375,4 +431,10 @@ def get_allowed_usernames(request: schemas.IotDoorPollingRequest, db_user : models.User = user tkns = tkns + db_user.last_token + '\n' - return tkns \ No newline at end of file + return tkns + +@app.get("/test") +def get(db: Session = Depends(get_db)): + mon = crud.get_monitor(db, "ff:ff:ff:ff") + + return mon.door \ No newline at end of file diff --git a/sql_app/models.py b/sql_app/models.py index aee9f9c..168da0f 100644 --- a/sql_app/models.py +++ b/sql_app/models.py @@ -23,7 +23,7 @@ class IotEntity(Base): __tablename__ = "iot_entities" id = Column(Integer, primary_key=True, index=True) - bluetooth_mac = Column(String(512)) + bluetooth_mac = Column(String(512), index=True, unique=True) description = Column(String(512)) open_request = Column(Boolean, default=False) time_seconds = Column(Integer, default=10) @@ -31,7 +31,22 @@ class IotEntity(Base): force_close = Column(Boolean, default=False) state = Column(Boolean, default=False) # True is open, False is closed authorized_users = relationship("User", secondary="user_iot_link", back_populates="authorized_devices") - access_log = relationship("DoorAccessLog", back_populates="iot_device") + access_log = relationship("DoorAccessLog", back_populates="iot_device") # one-to-many + monitor = relationship("Monitors", back_populates="door", uselist=False) # one-to-one + +class Monitors(Base): + __tablename__ = "monitors" + + id = Column(Integer, primary_key=True) + bluetooth_mac = Column(String(512), index=True, unique=True) + description = Column(String(512)) + humidity = Column(Integer, default=0) + people = Column(Integer, default=0) + temperature = Column(Integer, default=0) + smoke_sensor_reading = Column(Integer, default=0) + door_id = Column(Integer, ForeignKey("iot_entities.id")) + door = relationship("IotEntity", back_populates="monitor") + sensor_history = relationship("RoomSensorData", back_populates="monitor") class UserAuthToIoTDev(Base): __tablename__ = "user_iot_link" @@ -61,6 +76,8 @@ class RoomSensorData(Base): temperature = Column(Integer) smoke_sensor_reading = Column(Integer) timestamp = Column(DateTime) + monitor_id = Column(Integer, ForeignKey("monitors.id"), index=True) + monitor = relationship("Monitors", back_populates="sensor_history") class UserConnectionHistory(Base): __tablename__ = "user_connection_history" diff --git a/sql_app/schemas.py b/sql_app/schemas.py index 081548c..5f8b038 100644 --- a/sql_app/schemas.py +++ b/sql_app/schemas.py @@ -28,12 +28,33 @@ class IotEntity(IotEntityBase): time_seconds: int force_close: bool acces_list_counter: int + state: bool class Config: orm_mode = True class IotBluetoothMac(BaseModel): bluetooth_mac : str +class Monitor(IotEntityBase): + # bluetooth_mac: str + # description: str + id: int + bluetooth_mac: str + description: str + humidity: int + people: int + temperature: int + smoke_sensor_reading: int + class Config: + orm_mode = True + +class MonitorUpdateReadings(BaseModel): + humidity : int + people : int + temperature : int + smoke_sensor_reading : int + token: str # Contains mac + class User(UserBase): id: int is_active: bool @@ -90,8 +111,8 @@ class IotMonitorRoomInfo(BaseModel): temperature : int smoke_sensor_reading : int token: str - class Config: - orm_mode = True + # class Config: + # orm_mode = True class IotMonitorRoomInfoTimestamped(IotMonitorRoomInfo): time: datetime