예제 #1
0
def revise_plants_status(current_pot: Pot, new_plants_status: dict):
    revised_plants_status = {}
    new_plants_objs: Plants = Plants.parse_obj(new_plants_status)
    old_plants_objs: Plants = current_pot.session.plants

    # Fields are ordered https://pydantic-docs.helpmanual.io/usage/models/#field-ordering
    for old_plant_tuple, new_plant_tuple in zip(old_plants_objs, new_plants_objs):
        old_plant: Plant = old_plant_tuple[1]
        new_plant: Plant = new_plant_tuple[1]
        ring_colour = old_plant.ringColour

        # NOTE: After harvest, slot will be empty so None. For UT, no new seeds after harvest, so keep it at None
        # TODO: Ideally to remove this once UI allows users to indicate to plant new seed
        if old_plant.growthStage == None:
            new_plant = Plant(growthStage=None, ringColour=new_plant.ringColour)

        # TODO: Future work: start time of seed planting based on user indication in app, not session start time
        # NOTE: Add replace(tzinfo=None) to avoid error "can't subtract offset-naive and offset-aware datetimes"
        elif is_seed(datetime.utcnow(), current_pot.session.sessionStartTime.replace(tzinfo=None)):
            new_plant.growthStage = GrowthStage.seed

        elif is_sprouting(new_plant.growthStage, datetime.utcnow(), current_pot.session.sessionStartTime.replace(tzinfo=None)):
            new_plant.growthStage = GrowthStage.sprouting
        
        else:
            logger.info("No revision to plants status needed")

        revised_plants_status[ring_colour] = new_plant.dict()
    return revised_plants_status
예제 #2
0
 async def connect(self, websocket: WebSocket):
     await websocket.accept()
     self.active_connections[websocket.path_params['pot_id']] = websocket
     logger.info("WS connected with Pot {}".format(
         websocket.path_params['pot_id']))
     logger.info("Connected WSs - {}".format(
         self.active_connections.keys()))
예제 #3
0
async def pots_health_check():
    all_pots = pots_collection.get()

    for pot in all_pots:
        try:
            pot_id = pot.to_dict()['potId']
            # Alert Pot
            health_check_msg = MessageToPot(
                action=Action.update,
                potId=pot_id,
                data=[
                    PotSendDataDictStr(field=PotSendDataStr.health_check,
                                       value="health check")
                ])
            await ws_manager.send_personal_message_json(
                health_check_msg.dict(), pot_id)
            logger.info("Health check to pot {} success!".format(pot_id))
            firestore_input = {"connected": True}

        except Exception as e:
            logger.error("Health check to Pot {} failed!".format(pot_id))
            firestore_input = {"connected": False}

        # Update Firebase to alert mobile app
        pots_collection.document(pot_id).update(firestore_input)
예제 #4
0
async def create(new_pot: PotHttpReq):
    try:
        pot_id = new_pot.id
        new_pot = new_pot_registration(pot_id)
        pots_collection.document(pot_id).set(new_pot.dict())
        logger.info("New Pot {} registered".format(pot_id))
        return {"success": True}
    except Exception as e:
        logger.error(e)
        return f"An Error Occured: {e}"
예제 #5
0
 async def send_personal_message_json(self, message: dict, pot_id: str):
     if pot_id in self.active_connections:
         websocket: WebSocket = self.active_connections[pot_id]
         await websocket.send_json(message)
         logger.info(message)
     else:
         message["error_msg"] = "Websocket for Pot {} not found".format(
             pot_id)
         logger.error(message)
         raise Exception("Websocket for Pot {} not found".format(pot_id))
예제 #6
0
 async def broadcast(self, message_dict: be2pot_schemas.PotSendDataDictStr):
     if len(self.active_connections) > 0:
         for pot_id in self.active_connections:
             websocket: WebSocket = self.active_connections[pot_id]
             health_check_msg = be2pot_schemas.MessageToPot(
                 action=be2pot_schemas.Action.read,
                 potId=pot_id,
                 data=[
                     be2pot_schemas.PotSendDataDictStr(
                         field=message_dict.field, value=message_dict.value)
                 ])
             await websocket.send_json(health_check_msg.dict())
             logger.info(message_dict)
     else:
         logger.warning("No websocket connections to broadcast to")
예제 #7
0
async def websocket_endpoint(websocket: WebSocket, pot_id: str):
    await ws_manager.connect(websocket)

    try:
        while True:
            data = await websocket.receive_json()
            logger.info(data)
            responses = await ws_manager.process_message(data)
            for response in responses:
                await ws_manager.send_personal_message_json(
                    response.dict(), pot_id)
            # await manager.broadcast(f"Client #{pot_id} says: {data}")

    # TODO: Maybe can remove this
    except pydantic.error_wrappers.ValidationError as e:
        logger.error(e)
        exc_type, exc_obj, exc_tb = sys.exc_info()
        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
        err_response = be2pot_schemas.MessageToPot(
            potId=pot_id,
            data=[
                be2pot_schemas.PotSendDataDictStr(
                    field=be2pot_schemas.PotSendDataStr.error,
                    value="{}: {}, line {}, {}".format(exc_type, fname,
                                                       exc_tb.tb_lineno, e))
            ])

        await ws_manager.send_personal_message_json(err_response.dict(),
                                                    pot_id)

    except WebSocketDisconnect:
        ws_manager.disconnect(pot_id)

    except Exception as e:
        logger.error(e)
        exc_type, exc_obj, exc_tb = sys.exc_info()
        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
        err_response = be2pot_schemas.MessageToPot(
            potId=pot_id,
            data=[
                be2pot_schemas.PotSendDataDictStr(
                    field=be2pot_schemas.PotSendDataStr.error,
                    value="{}: {}, line {}, {}".format(exc_type, fname,
                                                       exc_tb.tb_lineno, e))
            ])

        await ws_manager.send_personal_message_json(err_response.dict(),
                                                    pot_id)
예제 #8
0
async def quiz_alert():
    current_date = datetime.utcnow().strftime('%Y%m%d')
    # NOTE: For Python, all string fields with an integer value like '1' require ``
    retrieved_pots = pots_collection.where('session.quiz.quizDates',
                                           'array_contains',
                                           current_date).get()

    # TODO: parse this properly to Pot object
    for pot in retrieved_pots:
        try:
            pot_id = pot.to_dict()["potId"]
            quiz_day_number_idx = pot.to_dict(
            )['session']['quiz']['quizDates'].index(current_date)
            quiz_day_number = pot.to_dict(
            )['session']['quiz']['quizDayNumbers'][quiz_day_number_idx]
            current_show_quiz_numbers: list = pot.to_dict(
            )['session']['quiz']['showQuizNumbers']
            current_show_quiz_numbers.append(quiz_day_number)
            firestore_input = {
                "session.quiz.showQuizNumbers": current_show_quiz_numbers,
                "session.quiz.currentQuizDayNumber": quiz_day_number
            }

            # Update Firebase to alert mobile app
            pots_collection.document(pot_id).update(firestore_input)
            logger.info("Updated Quiz {} alert for Pot {} to database".format(
                quiz_day_number, pot_id))

            #TODO: Also alert when previous quiz not yet completed
            # Alert Pot
            alert_message = MessageToPot(
                action=Action.update,
                potId=pot_id,
                data=[
                    PotSendDataDictBool(field=PotSendDataBool.showQuiz,
                                        value=True)
                ])
            await ws_manager.send_personal_message_json(
                alert_message.dict(), pot_id)

            logger.info("Sent Quiz {} alert to Pot {}".format(
                quiz_day_number, pot_id))

        except Exception as e:
            logger.error("Quiz {} alert to Pot {} failed!".format(
                quiz_day_number, pot_id))
예제 #9
0
async def harvest(pot: PotHttpReq):
    try:
        pot_id = pot.id
        response = MessageToPot(action=Action.read,
                                potId=pot_id,
                                data=[
                                    PotSendDataDictStr(
                                        field=PotSendDataStr.image,
                                        value="send image over")
                                ])
        await ws_manager.send_personal_message_json(response.dict(), pot_id)
        logger.info("CV read message sent to pot {}".format(pot_id))
        return {"success": True}

    except Exception as e:
        logger.error(e)
        return {"success": False}
예제 #10
0
async def health(pot: PotHttpReq):
    try:
        pot_id = pot.id
        health_check_msg = MessageToPot(
            potId=pot_id,
            data=[
                PotSendDataDictStr(field=PotSendDataStr.health_check,
                                   value="health check")
            ])
        await ws_manager.send_personal_message_json(health_check_msg.dict(),
                                                    pot_id)
        logger.info("Health check to pot {} success!".format(pot_id))
        firestore_input = {"connected": True}
        pots_collection.document(pot_id).update(firestore_input)
        return {"health check": True}
    except Exception as e:
        logger.error("Health check to pot {} failed!".format(pot_id))
        return {"health check": False}
예제 #11
0
async def daily_check_in_alert():
    all_pots = pots_collection.get()

    for pot in all_pots:
        try:
            pot_id = pot.to_dict()['potId']
            firestore_input = {"session.checkIn.showCheckIn": True}
            # Update Firebase to alert mobile app
            pots_collection.document(pot_id).update(firestore_input)
            # Alert Pot
            alert_message = MessageToPot(
                action=Action.update,
                potId=pot_id,
                data=[
                    PotSendDataDictBool(field=PotSendDataBool.showCheckIn,
                                        value=True)
                ])
            await ws_manager.send_personal_message_json(
                alert_message.dict(), pot_id)
            logger.info("Sent Check In alert to Pot {}".format(pot_id))
            # TODO: Need a message queue for messages not sent to pots with failed websocket connection
        except Exception as e:
            logger.error("Check In alert to Pot {} failed!".format(pot_id))
예제 #12
0
def get_firebase_credentials():
    if os.path.exists(FIREBASE_CRED) and os.path.isfile(FIREBASE_CRED):
        logger.info("Credentials JSON file found")
        return FIREBASE_CRED
    else:
        logger.info("Credentials JSON file not found.")
        encoded_cred = os.getenv('FIREBASE_CRED_ENCODED')
        if encoded_cred != None:
            logger.info("Credentials env var found")

            decoded_cred = json.loads(base64.b64decode(encoded_cred))
            return decoded_cred
        else:
            raise Exception("'FIREBASE_CRED_ENCODED' env var undefined.")
예제 #13
0
from dotenv import load_dotenv
load_dotenv()

import sys
from fastapi import FastAPI
import uvicorn

from lib.custom_logger import logger
from ws import ws_server, firebase_listener
from router import pots, plants
from scheduler import scheduler
from lib import check_in
# Initialize FastAPI app


app = FastAPI()

app.include_router(pots.router)
app.include_router(plants.router)
app.include_router(ws_server.router)

logger.info("Server started")

if __name__ == '__main__':
    sys.exit("Run: `uvicorn main:app --reload --port 8000` instead")
예제 #14
0
 def check_existing_connections(self):
     logger.info("Existing Connections - {}".format(
         list(self.active_connections.keys())))