Project Milestone 3 - SENG-350-2024-fall/Team-14 GitHub Wiki

System Description

The Mr. ED application helps healthcare professionals efficiently manage patient triage by displaying a list of incoming tickets with essential patient details. Nurses can view each patient’s symptoms, and other relevant information, assess the severity of their condition, and assign a priority level. Once prioritized, the nurse submits the ticket, which triggers the process for further medical attention. The app is designed to be intuitive, with a simple and responsive interface that makes it easy for healthcare staff to quickly review and prioritize patient needs, ensuring urgent cases are attended to promptly and streamlining the overall workflow in busy medical settings. Furthermore, the application displays the current load and wait times at medical establishments nearby. Overall the system improves the convenience of getting a medical examination for patients and healthcare professionals alike.

Architectural Tactics for Availability

Exception Handling

In the frontend, every time we make a POST request with Axiom, we ensure the frontend doesn't crash if the response isn't successful.

axios
    .post('http://localhost:8000/triage/triage-tickets', data)
    .then(response => {
        setMessage(response.data.message);
        const { symptoms } = response.data;
        // Redirect to homepage using navigate
        navigate('/patientWaiting'); // Replace '/' with the homepage URL if needed
    })
    .catch(error => {
        console.error(error);
        setMessage(error.response?.data?.detail || 'Error registering. Please try again.');
    });

Checklist

  • Does the component request correctly for valid requests?
  • Does the component handle exceptions for invalid requests?

Testing

  1. In Enter Virtual Triage, submit a ticket as normal. Request works as normal.
  2. In EnterVirtualTriage.js, in the address field of the axiom post request in handleEnter(), and change to http://localbadhost:8000/triage/triage-tickets. Submit a ticket as normal. Request is denied, error message "Error registering. Please try again." is issued.

Graceful Degradation

In the frontend, when the user enters invalid information, they are informed via error message that they've entered the wrong input without breaking the application.

Checklist

  • Does the form allow correct inputs?
  • Does the form gracefully handle incorrect inputs?

Testing

  1. In the register form, enter correct inputs for each field. When submitting, the form is accepted.
  2. In the register form, enter incorrect inputs for email and birthday. When submitting, the form displays error messages for the email and birthday fields.

Sanity Checking

While creating/registering a patient account, there are 1-2 sanity checks that occur per field. These include:

  • Full Name: There must be a name input, a first and last name (two words), and all alphabetical letters.
  • Date of Birth: There must be a valid date of birth provided in the format YYYY/MM/DD.
  • Gender: Male, Female, or Prefer not to say is selected.
  • Street Address: A street address must be provided.
  • City: A city must be provided (a word with only alphabetical letters).
  • Province: A province must be provided (a word with only alphabetical letters).
  • Country: A country must be provided (a word with only alphabetical letters).
  • Postal Code: A postal code must be provided in the format A1A 1A1.
  • Health Number: A 9 digit number must be provided.
  • Email: A email must be provided ('@' and '.' must exist within the email).
  • Password: A password must be provided.

Checklist

  • Does the sanity check verify user input exists in every field?
  • Does the sanity check verify that the user provides correct input within the correct parameters in every field?
  • Does the sanity check provide an error message if the user's input is incorrect?
  • Are the error messages descriptive?

Testing

  1. Start the app and go to the Register page.
  2. Starting at the "Full Name" field, input only the first name, full name with only alphabetical letters, and full name with numbers.
  3. All previous fields should have correct input. To test the "Date of Birth", input a valid birthday in the correct format, a valid birthday in an incorrect format, a year later than 2024, an invalid month, "1" as month, an invalid day, and "1" as day.
  4. All previous fields should have correct input. To test the "Gender" field, select an option and don't select any option.
  5. All previous fields should have correct input. To test the "Street Address" field, enter input.
  6. All previous fields should have correct input. To test the "City" field, enter input with only alphabetical letters and with numbers.
  7. All previous fields should have correct input. To test the "Province" field, enter input with only alphabetical letters and with numbers.
  8. All previous fields should have correct input. To test the "Country" field, enter input with only alphabetical letters and with numbers.
  9. All previous fields should have correct input. To test the "Postal Code" field, enter input in the format A1A 1A1 and not in the format.
  10. All previous fields should have correct input. To test the "Health Number" field, enter input with 9 digits, without 9 digits, and with letters.
  11. All previous fields should have correct input. To test the "Email" field, enter input with both '@' and '.', without '@', without '.', without both '@' and '.'.
  12. All previous fields should have correct input. To test the "Password" field, enter input.
  13. In addition to all fields, test if the account isn't created when one of the fields is missing input entirely.

Shadow

In our database hosting service, MongoDB Compass, we maintain replica sets of the data. The database cluster has a primary node and two secondary nodes. The primary node fields read and write calls from our API, and the secondary nodes remain ready to switchover in the event of a failure. The nodes communicate via heartbeat to maintain connectivity.

shadow example

Checklist

  • Are the nodes synchronized?
  • Are the nodes consistent?
  • Are the nodes maintaining communication?
  • Do the nodes failover successfully?
  • Do the nodes failover consistently and quickly enough?

Testing

  1. Issue r.status() in the mongosh terminal to check if the each member is working properly. Each member should have a health value of 1. One member should have stateStr: PRIMARY, and the other two should have stateStr: SECONDARY.
  2. In the terminal, issuedb.ShutdownServer(). This shuts down the primary node. Issue r.status() to confirm.
  3. Attempt to register from mr_ed web app. The API call to the database works despite the primary node being down.
  4. Restart mongoDB service with sudo systemctl start mongod in the terminal to restore default system state.

Transactions

When sending HTTP requests, the system uses Cross-Origin Resource Sharing (CORS) when sending requests. This service restricts access to the API to certain endpoints. CORS prevents downtime by preventing malicious hosts from DDOSing our API.

Checklist

  • Can the allowed addresses access the API?
  • Are unauthorized addresses denied access to the API?

Testing

  1. Issue a POST request from an accepted address. The request is authorized.
  2. Issue a POST request from an unauthorized address. The request is declined.

Design Patterns

Decorator Pattern

The Decorator pattern allows behavior to be added to individual objects, dynamically, without affecting the behavior of other objects from the same class. In our project, this pattern is evident in the use of decorators in FastAPI.

Example in FastAPI:

  • Route Decorators: The @router.get, @router.post, @router.put, and @router.delete decorators in triage_routes.py add routing behavior to the functions they decorate.
@router.get("/tickets", response_model=List[VTTicket])
async def get_all_tickets():
    return await get_tickets()
  • Middleware Decorators: The @app.middleware decorator can be used to add middleware to the FastAPI application.
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

Observer Pattern

The Observer pattern is used to allow an object to notify other objects about changes in its state. In your project, this pattern is evident in the use of React's useEffect hook, which allows components to react to changes in state or props.

Example in React:

  • useEffect Hook: The useEffect hook in NurseWaiting.js observes changes in the component's state and performs side effects like data fetching.
useEffect(() => {
  const fetchTickets = async () => {
    try {
      const response = await fetch('/api/triage-tickets');
      const data = await response.json();
      setTickets(data);
    } catch (error) {
      console.error('Error fetching tickets:', error);
    } finally {
      setLoading(false);
    }
  };

  fetchTickets();
}, []);

Factory Pattern

The Factory pattern is used to create objects without specifying the exact class of object that will be created. In our project, this pattern is evident in the creation of VTTicket instances from the data fetched from the database.

Example in FastAPI:

  • Creating VTTicket Instances: The VTTicket model's constructor acts as a factory method to create instances of the model from the data returned by the database.
async def get_tickets() -> List[VTTicket]:
    try:
        tickets = list(tickets_collection.find({}, {'_id': 0}))  # Exclude the MongoDB _id field
        return [VTTicket(**ticket) for ticket in tickets]
    except Exception as e:
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e))

Model-View-Controller (MVC) Pattern

The MVC pattern separates an application into three main components: Model, View, and Controller. This separation helps in organizing the code and promoting reusability and maintainability.

Example in our Project:

  • Model: The VTTicket model in VirtualTriage.py defines the structure of the data.
class VTTicket(BaseModel):
    ticketID: int
    userID: str
    ED: str
    consent: bool
    timestamp: str
    generalSymptoms: GeneralSymptoms
    respiratorySymptoms: RespiratorySymptoms
    gastrointestinalSymptoms: GastrointestinalSymptoms
    neurologicalSymptoms: NeurologicalSymptoms
    musculoskeletalSymptoms: MusculoskeletalSymptoms
    cardiovascularSymptoms: CardiovascularSymptoms
    skinSymptoms: SkinSymptoms
    psychologicalSymptoms: PsychologicalSymptoms
    substanceHabits: SubstanceHabits
    allergies: List[str]
    medications: List[str]
    durationOfSymptoms: str
    listAllergies: List[str]
    pastMedicalConditions: List[str]
  • View: The NurseWaiting.js React component is responsible for rendering the user interface.
const NurseWaiting = () => {
  const [tickets, setTickets] = useState([]);
  const [loading, setLoading] = useState(true);
  const [selectedTicket, setSelectedTicket] = useState(null);

  useEffect(() => {
    const fetchTickets = async () => {
      try {
        const response = await fetch('/api/triage-tickets');
        const data = await response.json();
        setTickets(data);
      } catch (error) {
        console.error('Error fetching tickets:', error);
      } finally {
        setLoading(false);
      }
    };

    fetchTickets();
  }, []);

  if (loading) {
    return <div>Loading...</div>;
  }

  if (selectedTicket) {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: '20px' }}>
        <h1>Ticket Details</h1>
        <div style={{
          border: '1px solid #ccc',
          padding: '20px',
          borderRadius: '5px',
          backgroundColor: '#f9f9f9',
          width: '80%',
          textAlign: 'center',
          transition: 'transform 0.2s, background-color 0.2s',
        }}>
          <strong>User ID:</strong> {selectedTicket.userID || 'N/A'}
          <div><strong>Timestamp:</strong> {selectedTicket.timestamp}</div>
          <div><strong>Allergies:</strong> {selectedTicket.listAllergies?.join(', ') || 'None'}</div>
          <div><strong>Duration:</strong> {selectedTicket.durationOfSymptoms || 'N/A'}</div>
          
          <h3>Symptoms:</h3>
          <div>
            {Object.entries(selectedTicket).map(([key, value]) => {
              if (typeof value === 'object' && value !== null) {
                return (
                  <div key={key}>
                    <strong>{key}:</strong>
                    <ul>
                      {Object.entries(value).map(([symptom, hasSymptom]) => (
                        <li key={symptom}>{symptom}: {hasSymptom ? 'Yes' : 'No'}</li>
                      ))}
                    </ul>
                  </div>
                );
              }
              return null;
            })}
          </div>

          <button onClick={() => setSelectedTicket(null)} style={{ marginTop: '20px' }}>Back</button>
        </div>
      </div>
    );
  }

  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: '20px' }}>
      <h1 style={{ marginBottom: '20px' }}>Incoming Triage Tickets</h1>
      <div style={{ width: '80%', display: 'flex', flexDirection: 'column', gap: '10px' }}>
        {tickets.map(ticket => (
          <div key={ticket.ticketID} style={{
            border: '1px solid #ccc',
            padding: '10px',
            borderRadius: '5px',
            backgroundColor: '#f9f9f9',
            transition: 'transform 0.2s',
            cursor: 'pointer'
          }}
            onMouseEnter={(e) => {
              e.currentTarget.style.transform = 'scale(1.05)';
              e.currentTarget.style.boxShadow = '0 0 10px rgba(0, 0, 0, 0.1)';
            }}
            onMouseLeave={(e) => {
              e.currentTarget.style.transform = 'scale(1)';
              e.currentTarget.style.boxShadow = 'none';
            }}
            onClick={() => setSelectedTicket(ticket)}
          >
            <strong>User ID:</strong> {ticket.userID}
            <div><strong>Timestamp:</strong> {ticket.timestamp}</div>
            <div><strong>Allergies:</strong> {ticket.listAllergies?.join(', ') || 'None'}</div>
            <div><strong>Duration:</strong> {ticket.durationOfSymptoms || 'N/A'}</div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default NurseWaiting;
  • Controller: The FastAPI routes in triage_routes.py handle the HTTP requests and interact with the data models.
from fastapi import APIRouter, Depends
from models.VirtualTriage import VTTicket
from services.triage_service import get_tickets, create_ticket, update_ticket, delete_ticket
from typing import List

router = APIRouter()

@router.get("/tickets", response_model=List[VTTicket])
async def get_all_tickets():
    return await get_tickets()

@router.post("/tickets", response_model=VTTicket)
async def create_new_ticket(ticket: VTTicket):
    return await create_ticket(ticket)

@router.put("/tickets/{ticket_id}", response_model=VTTicket)
async def update_existing_ticket(ticket_id: int, ticket: VTTicket):
    return await update_ticket(ticket_id, ticket)

@router.delete("/tickets/{ticket_id}", response_model=dict)
async def delete_existing_ticket(ticket_id: int):
    return await delete_ticket(ticket_id)

State Pattern

The state pattern defines the status of the tickets throughout the application. The tickets have two main states: Triage and Medical defined by the presence of a priority.

  • Model: The MedicalTicket model in Medical.py defines the structure of the data
class MedicalTicket( BaseModel ):
    VTticketID: int
    priority: int
    startTime: str

Design Diagrams Modifications

Class Diagram

The class diagram was updated to reflect the attributes in the database for Patients, Nurses, Doctors, VT Tickets, MT Tickets, and Emergency Departments. Operations were updated to closely reflect the class' behaviour in the Mr. ED application. In addition, the VTQueue and MTQueue classes were removed from the class diagram as they were outside the scope of our project.

Screenshot 2024-11-05 at 1 03 35 AM

Sequence Diagrams

For all sequence diagrams, object interactions were updated to accurately reflect the communication between them.

Create/Register Account

image

Account information was expanded to accurately reflect the patient being treated.

Enter Virtual Triage

image

Ticket information was slightly modified to reflect better object orientation.

Patient Login

image

Removed Authentication Server as it was not required.

Patient Appointment

image

Replaced MTQueue with the Database because it is unnecessary.

VTTicket to MedicalTicket

image

Replaced MTQueue with the Database because it is unnecessary.

Entity Relationship Diagram

Screenshot 2024-11-05 at 1 02 17 AM

In order to better reflect the current state of our code, we edited the relationships, removed some entities altogether and updated the attributes. The entities that we removed were both the medical queue and the virtual triage queue as we plan to simply sort these tickets by timestamp in order to avoid the increased complexity that comes with queues. Since these entities were connected to multiple relationships, these relationships also changed. Finally, the attributes of Patient and VT Ticket were updated.

Swimlane Activity Diagram

image

Changed any reference of a Queue to Database due to our decision to remove queues from our design.

Component Diagram

image

Removed Queues and added Tickets to more accurately reflect the state of the system.

Collaboration Diagram

Screenshot 2024-11-05 at 12 22 22 AM

The Collaboration diagram was updated to eliminate queue implementation and the continuous sorting of the medical ticket list as this was decided to be outside the scope of this project.

Data Flow Diagrams

Level 0

image

Removed the Queue object in order to replace with database.

Level 1

image

Removed the Queue object in order to replace with database.

State Machine

Medical Ticket Queue

Removed the diagram as we decided not to dynamically update the priority of each ticket, as it was deemed beyond the scope of this project.

Virtual Triage Ticket

image

Slightly changed wording of transitions to reflect system.

Database

image

Changed from Virtual Triage Queue to reflect new sorting system, using priority from the database instead of a dynamic syste.

Medical Ticket

image

Removed any mention of Queues and replaced them with database.

Contributions

Isaac Northrop

  • Planned design patterns and availability
  • Updated diagrams
  • Implemented Enter Virtual Triage component for front and back end

Ashley McPherson

  • Completed the front-end and back-end development of the Register (Create Account) page on the Mr. ED application
  • Implemented sanity checks
  • Updated diagrams

Liam Tanner

  • Wrote patient waiting route
  • Wrote nurse triage route
  • Wrote the triage_service.py and triage_route.py in the back-end in order to connect to the MongoDB database and establish api endpoints for the nurse triaging.
  • Extensively tested triaging from the nurse perspective
  • Helped improve the triage page from the user perspective
  • Wrote the following design pattern sections: Decorator, Observer, Factory and MVC
  • Updated and wrote change descriptions for: ERD diagram, Swimlane activity diagram and Collaboration diagram

Erich Rueben

  • Completed the back-end development of the Login and Nurse priority feature
  • Helped update diagrams
  • Structured backend and database
⚠️ **GitHub.com Fallback** ⚠️