diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..b4a361e
Binary files /dev/null and b/.DS_Store differ
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/devtest.iml b/.idea/devtest.iml
new file mode 100644
index 0000000..6ecad54
--- /dev/null
+++ b/.idea/devtest.iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..146ab09
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..784420d
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..901b03a
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/chatgpt/.DS_Store b/chatgpt/.DS_Store
new file mode 100644
index 0000000..931ec65
Binary files /dev/null and b/chatgpt/.DS_Store differ
diff --git a/chatgpt/__pycache__/app_tests.cpython-312-pytest-6.2.5.pyc b/chatgpt/__pycache__/app_tests.cpython-312-pytest-6.2.5.pyc
new file mode 100644
index 0000000..f91fa54
Binary files /dev/null and b/chatgpt/__pycache__/app_tests.cpython-312-pytest-6.2.5.pyc differ
diff --git a/chatgpt/__pycache__/main.cpython-312.pyc b/chatgpt/__pycache__/main.cpython-312.pyc
new file mode 100644
index 0000000..26390a7
Binary files /dev/null and b/chatgpt/__pycache__/main.cpython-312.pyc differ
diff --git a/chatgpt/app_tests.py b/chatgpt/app_tests.py
index 258a8a6..76bed03 100644
--- a/chatgpt/app_tests.py
+++ b/chatgpt/app_tests.py
@@ -1,10 +1,181 @@
+import pytest
+from main import app, db
+
+
+# Set up test client with in-memory SQLite database
+@pytest.fixture
+def client():
+ app.config['TESTING'] = True
+ app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
+ with app.test_client() as client:
+ with app.app_context():
+ db.create_all()
+ yield client
+ with app.app_context():
+ db.drop_all()
+
+
+# Tests elevator_demands
def test_create_demand(client):
+ # Test creating a demand with a valid floor and dynamic timestamp
response = client.post('/demand', json={'floor': 3})
+ response_data = response.get_json()
+ demand_data = response_data['demand']
+ actual_id = demand_data['id']
assert response.status_code == 201
- assert response.get_json() == {'message': 'Demand created'}
+ assert response.get_json() == {'message': 'Demand created', 'demand': {
+ 'id': actual_id,
+ 'timestamp': response.get_json()['demand']['timestamp'], # Handle dynamic timestamp
+ 'floor': 3,
+ 'day_of_week': response.get_json()['demand']['day_of_week'],
+ 'hour_of_day': response.get_json()['demand']['hour_of_day']
+ }}
+
+
+def test_create_demand_invalid_floor(client):
+ # Check that we get a 400 error if floor isn't an integer
+ response = client.post('/demand', json={'floor': 'hola'})
+ assert response.status_code == 400
+ assert response.get_json() == {'error': 'Floor must be an integer'}
+
+
+def test_get_demand(client):
+ client.post('/demand', json={'floor': 1})
+ response = client.get('/demand/1')
+ assert response.status_code == 200
+ assert response.get_json()['floor'] == 1
+
+
+def test_get_demand_not_found(client):
+ # Test that requesting a non-existent demand returns 404
+ response = client.get('/demand/hola')
+ assert response.status_code == 404
+
+def test_get_all_demands(client):
+ client.post('/demand', json={'floor': 3})
+ client.post('/demand', json={'floor': -1})
+ response = client.get('/demands')
+ assert response.status_code == 200
+ assert len(response.get_json()) == 2
+
+def test_update_demand(client):
+ client.post('/demand', json={'floor': 1})
+ response = client.put('/demand/1', json={'floor': 50})
+ assert response.status_code == 200
+ assert response.get_json()['demand']['floor'] == 50
+
+
+def test_update_demand_invalid_floor(client):
+ # Test that updating with an invalid floor gives a 400 error
+ client.post('/demand', json={'floor': 1})
+ response = client.put('/demand/1', json={'floor': 'invalid'})
+ assert response.status_code == 400
+ assert response.get_json() == {'error': 'Floor must be an integer'}
+
+
+def test_delete_demand(client):
+ client.post('/demand', json={'floor': -5})
+ response = client.delete('/demand/1')
+ assert response.status_code == 200
+ assert response.get_json() == {'message': 'Demand deleted'}
+ response = client.get('/demand/1')
+ assert response.status_code == 404
+
+
+# Test elevator_states
def test_create_state(client):
+ client.post('/demand', json={'floor': -3}) # Set up a demand for other test
response = client.post('/state', json={'floor': 5, 'vacant': True})
assert response.status_code == 201
- assert response.get_json() == {'message': 'State created'}
+ assert response.get_json()['state']['floor'] == 5
+ assert response.get_json()['state']['vacant'] is True
+
+
+def test_create_state_non_vacant_valid_demand(client):
+ # Test creating a non-vacant state with a valid demand_id
+ client.post('/demand', json={'floor': 3})
+ response = client.post('/state', json={'floor': 3, 'vacant': False, 'demand_id': 1})
+ assert response.status_code == 201
+ assert response.get_json()['state']['demand_id'] == 1
+
+
+def test_create_state_non_vacant_invalid_demand(client):
+ # Check that a non-vacant state with an invalid demand_id fails
+ response = client.post('/state', json={'floor': 3, 'vacant': False, 'demand_id': 999})
+ assert response.status_code == 400
+ assert response.get_json() == {'error': 'Invalid demand_id'}
+
+
+def test_create_state_non_vacant_no_demand_id(client):
+ # Test that a non-vacant state without demand_id returns an error
+ response = client.post('/state', json={'floor': 3, 'vacant': False})
+ assert response.status_code == 400
+ assert response.get_json() == {'error': 'Non-vacant state requires a valid demand_id'}
+
+
+def test_create_state_invalid_floor(client):
+ # Test that an invalid floor in state creation gives a 400 error
+ response = client.post('/state', json={'floor': 'invalid', 'vacant': True})
+ assert response.status_code == 400
+ assert response.get_json() == {'error': 'Floor must be an integer'}
+
+
+def test_create_state_invalid_vacant(client):
+ # Check that an invalid vacant value fails with a 400
+ response = client.post('/state', json={'floor': 5, 'vacant': 'invalid'})
+ assert response.status_code == 400
+ assert response.get_json() == {'error': 'Vacant must be a boolean'}
+
+
+def test_get_state(client):
+ # Test read to obtain a 200
+ client.post('/demand', json={'floor': 3})
+ client.post('/state', json={'floor': 5, 'vacant': True})
+ response = client.get('/state/1')
+ assert response.status_code == 200
+ assert response.get_json()['floor'] == 5
+
+
+def test_get_state_not_found(client):
+ # Test that requesting a non-existent state returns 404
+ response = client.get('/state/999')
+ assert response.status_code == 404
+
+
+def test_get_all_states(client):
+ # Test to obtain all the states and get a 200
+ client.post('/demand', json={'floor': 3})
+ client.post('/state', json={'floor': 5, 'vacant': True})
+ client.post('/state', json={'floor': -1, 'vacant': False, 'demand_id': 1})
+ response = client.get('/states')
+ assert response.status_code == 200
+ assert len(response.get_json()) == 2
+
+
+def test_update_state(client):
+ # Test updtae to obtain a 200
+ client.post('/demand', json={'floor': 3})
+ client.post('/state', json={'floor': 5, 'vacant': True})
+ response = client.put('/state/1', json={'floor': -1, 'vacant': False, 'demand_id': 1})
+ assert response.status_code == 200
+ assert response.get_json()['state']['floor'] == -1
+ assert response.get_json()['state']['vacant'] is False
+
+
+def test_update_state_invalid_demand_id(client):
+ # Test updating a state with an invalid demand_id and obtain a 400
+ client.post('/state', json={'floor': 5, 'vacant': True})
+ response = client.put('/state/1', json={'floor': 5, 'vacant': False, 'demand_id': 999})
+ assert response.status_code == 400
+ assert response.get_json() == {'error': 'Invalid demand_id'}
+
+
+def test_delete_state(client):
+ client.post('/state', json={'floor': 5, 'vacant': True})
+ response = client.delete('/state/1')
+ assert response.status_code == 200
+ assert response.get_json() == {'message': 'State deleted'}
+ response = client.get('/state/1')
+ assert response.status_code == 404
\ No newline at end of file
diff --git a/chatgpt/db.sql b/chatgpt/db.sql
index 1555ffe..70386f3 100644
--- a/chatgpt/db.sql
+++ b/chatgpt/db.sql
@@ -1,12 +1,29 @@
+-- Create table to storage all the demands of the elevator --
CREATE TABLE elevator_demands (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
- floor INTEGER
+ floor INTEGER NOT NULL,
+ day_of_week INTEGER NOT NULL,
+ hour_of_day INTEGER NOT NULL,
);
+-- Create table to storage the states of the elevator --
CREATE TABLE elevator_states (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
- floor INTEGER,
- vacant BOOLEAN
+ floor INTEGER NOT NULL,
+ vacant BOOLEAN NOT NULL,
+ day_of_week INTEGER NOT NULL,
+ hour_of_day INTEGER NOT NULL,
+ demand_id INTEGER,
+ FOREIGN KEY (demand_id) REFERENCES elevator_demands(id)
);
+
+-- Indexes to optimize frequent queries --
+-- Note: These indexes are not actively used in the current API endpoints,
+-- but are included to support future machine learning tasks that may require
+-- efficient filtering or grouping by timestamp or floor --
+CREATE INDEX idx_demands_timestamp ON elevator_demands(timestamp);
+CREATE INDEX idx_states_timestamp ON elevator_states(timestamp);
+CREATE INDEX idx_demands_floor ON elevator_demands(floor);
+CREATE INDEX idx_states_floor ON elevator_states(floor);
\ No newline at end of file
diff --git a/chatgpt/instance/.DS_Store b/chatgpt/instance/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/chatgpt/instance/.DS_Store differ
diff --git a/chatgpt/instance/elevator.db b/chatgpt/instance/elevator.db
new file mode 100644
index 0000000..7377da5
Binary files /dev/null and b/chatgpt/instance/elevator.db differ
diff --git a/chatgpt/main.py b/chatgpt/main.py
index 7f97d98..1c7679b 100644
--- a/chatgpt/main.py
+++ b/chatgpt/main.py
@@ -1,43 +1,194 @@
+# Import necessary libraries
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
+from sqlalchemy.sql import text
+# Set up Flask app and connect to SQLite database
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///elevator.db'
+app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # Turn off warnings for SQLAlchemy
db = SQLAlchemy(app)
+# Class model for elevator demands
class ElevatorDemand(db.Model):
+ __tablename__ = 'elevator_demands' # Set table name and ForeignKey reference
id = db.Column(db.Integer, primary_key=True)
- timestamp = db.Column(db.DateTime, default=datetime.utcnow)
- floor = db.Column(db.Integer, nullable=False)
+ timestamp = db.Column(db.DateTime, default=datetime.now()) # Store when the demand was made
+ floor = db.Column(db.Integer, nullable=False) # Floor number, can be positive or negative
+ day_of_week = db.Column(db.Integer, nullable=False) # Day of the week (0-6, Mon-Sun)
+ hour_of_day = db.Column(db.Integer, nullable=False) # Hour of the day (0-23)
+ def __init__(self, floor):
+ self.floor = floor
+ self.day_of_week = datetime.now().weekday() # Save the current day of the week
+ self.hour_of_day = datetime.now().hour # Save the current hour
+ # Convert demand to a dictionary for JSON response
+ def to_dict(self):
+ return {
+ 'id': self.id,
+ 'timestamp': self.timestamp.isoformat(),
+ 'floor': self.floor,
+ 'day_of_week': self.day_of_week,
+ 'hour_of_day': self.hour_of_day
+ }
+
+
+# Class model for elevator states
class ElevatorState(db.Model):
+ __tablename__ = 'elevator_states' # Set table name
id = db.Column(db.Integer, primary_key=True)
- timestamp = db.Column(db.DateTime, default=datetime.utcnow)
- floor = db.Column(db.Integer, nullable=False)
- vacant = db.Column(db.Boolean, nullable=False)
+ timestamp = db.Column(db.DateTime, default=datetime.now()) # Store when the state was recorded
+ floor = db.Column(db.Integer, nullable=False) # Current floor of the elevator
+ vacant = db.Column(db.Boolean, nullable=False) # True if elevator is empty, False if not
+ day_of_week = db.Column(db.Integer, nullable=False) # Day of the week (0-6)
+ hour_of_day = db.Column(db.Integer, nullable=False) # Hour of the day (0-23)
+ # Link to a demand if the elevator is not vacant
+ demand_id = db.Column(db.Integer, db.ForeignKey('elevator_demands.id'), nullable=True)
+ # Set up relationship to access demand data easily
+ demand = db.relationship('ElevatorDemand', backref=db.backref('states', lazy=True))
+
+ def __init__(self, floor, vacant, demand_id=None):
+ self.floor = floor
+ self.vacant = vacant
+ self.demand_id = demand_id
+ self.day_of_week = datetime.now().weekday() # Save current day
+ self.hour_of_day = datetime.now().hour # Save current hour
+ # Convert state to a dictionary for JSON response
+ def to_dict(self):
+ return {
+ 'id': self.id,
+ 'timestamp': self.timestamp.isoformat(),
+ 'floor': self.floor,
+ 'vacant': self.vacant,
+ 'day_of_week': self.day_of_week,
+ 'hour_of_day': self.hour_of_day,
+ 'demand_id': self.demand_id
+ }
+
+# Create a demand when someone calls the elevator
@app.route('/demand', methods=['POST'])
def create_demand():
data = request.get_json()
- new_demand = ElevatorDemand(floor=data['floor'])
- db.session.add(new_demand)
- db.session.commit()
- return jsonify({'message': 'Demand created'}), 201
+ if not isinstance(data.get('floor'), int):
+ return jsonify({'error': 'Floor must be an integer'}), 400 # Check if floor is valid
+ new_demand = ElevatorDemand(floor=data['floor']) # Create new demand
+ db.session.add(new_demand) # Add to database
+ db.session.commit() # Save changes
+ # Return the created demand with its details
+ return jsonify({'message': 'Demand created', 'demand': new_demand.to_dict()}), 201
+
+
+# Get a specific demand by ID
+@app.route('/demand/', methods=['GET'])
+def get_demand(id):
+ demand = ElevatorDemand.query.get_or_404(id) # Find demand or return 404
+ return jsonify(demand.to_dict()), 200 # Return demand details
+
+
+# Get all demands
+@app.route('/demands', methods=['GET'])
+def get_all_demands():
+ demands = ElevatorDemand.query.all() # Grab all demands from the database
+ return jsonify([demand.to_dict() for demand in demands]), 200 # Return list of demands
+
+
+# Update an existing demand
+@app.route('/demand/', methods=['PUT'])
+def update_demand(id):
+ demand = ElevatorDemand.query.get_or_404(id) # Find demand or 404
+ data = request.get_json()
+ if not isinstance(data.get('floor'), int):
+ return jsonify({'error': 'Floor must be an integer'}), 400 # Validate floor
+ demand.floor = data['floor'] # Update floor
+ db.session.commit() # Save changes
+ return jsonify({'message': 'Demand updated', 'demand': demand.to_dict()}), 200 # Return updated demand
+# Delete a demand
+@app.route('/demand/', methods=['DELETE'])
+def delete_demand(id):
+ demand = ElevatorDemand.query.get_or_404(id) # Find demand or 404
+ db.session.delete(demand) # Remove it
+ db.session.commit() # Save changes
+ return jsonify({'message': 'Demand deleted'}), 200 # Confirm deletion
+
+
+# Create a new elevator state
@app.route('/state', methods=['POST'])
def create_state():
data = request.get_json()
- new_state = ElevatorState(floor=data['floor'], vacant=data['vacant'])
- db.session.add(new_state)
- db.session.commit()
- return jsonify({'message': 'State created'}), 201
+ if not isinstance(data.get('floor'), int):
+ return jsonify({'error': 'Floor must be an integer'}), 400 # Check floor is an integer
+ if not isinstance(data.get('vacant'), bool):
+ return jsonify({'error': 'Vacant must be a boolean'}), 400 # Check vacant is boolean
+ demand_id = data.get('demand_id')
+ if not data['vacant'] and demand_id is None:
+ return jsonify({'error': 'Non-vacant state requires a valid demand_id'}), 400 # Check non-vacant states
+ if demand_id is not None and ElevatorDemand.query.get(demand_id) is None:
+ return jsonify({'error': 'Invalid demand_id'}), 400 # Validate demand_id
+ new_state = ElevatorState(floor=data['floor'], vacant=data['vacant'], demand_id=demand_id) # Create new state
+ db.session.add(new_state) # Add to database
+ db.session.commit() # Save changes
+ return jsonify({'message': 'State created', 'state': new_state.to_dict()}), 201 # Return created state
+
+
+# Get a specific state by its ID
+@app.route('/state/', methods=['GET'])
+def get_state(id):
+ state = ElevatorState.query.get_or_404(id) # Find state or 404
+ return jsonify(state.to_dict()), 200 # Return state details
+
+
+# Get all states
+@app.route('/states', methods=['GET'])
+def get_all_states():
+ states = ElevatorState.query.all() # Grab all states
+ return jsonify([state.to_dict() for state in states]), 200 # Return list of states
+
+
+# Update an existing state
+@app.route('/state/', methods=['PUT'])
+def update_state(id):
+ state = ElevatorState.query.get_or_404(id) # Find state or 404
+ data = request.get_json()
+ if not isinstance(data.get('floor'), int):
+ return jsonify({'error': 'Floor must be an integer'}), 400 # Validate floor
+ if not isinstance(data.get('vacant'), bool):
+ return jsonify({'error': 'Vacant must be a boolean'}), 400 # Validate vacant
+ demand_id = data.get('demand_id')
+ if not data['vacant'] and demand_id is None:
+ return jsonify({'error': 'Non-vacant state requires a valid demand_id'}), 400 # Check non-vacant states
+ if demand_id is not None and ElevatorDemand.query.get(demand_id) is None:
+ return jsonify({'error': 'Invalid demand_id'}), 400 # Validate demand_id
+ state.floor = data['floor'] # Update floor
+ state.vacant = data['vacant'] # Update vacant status
+ state.demand_id = demand_id # Update demand_id
+ db.session.commit() # Save changes
+ return jsonify({'message': 'State updated', 'state': state.to_dict()}), 200 # Return updated state
+
+
+# Delete a state
+@app.route('/state/', methods=['DELETE'])
+def delete_state(id):
+ state = ElevatorState.query.get_or_404(id) # Find state or 404
+ db.session.delete(state) # Remove it
+ db.session.commit() # Save changes
+ return jsonify({'message': 'State deleted'}), 200 # Confirm deletion
+# Start the app and create database tables
if __name__ == '__main__':
- db.create_all()
- app.run(debug=True)
+ with app.app_context():
+ db.create_all() # Set up the database tables
+ with db.engine.connect() as connection:
+ connection.execute(text('CREATE INDEX IF NOT EXISTS idx_demands_timestamp ON elevator_demands(timestamp)'))
+ connection.execute(text('CREATE INDEX IF NOT EXISTS idx_states_timestamp ON elevator_states(timestamp)'))
+ connection.execute(text('CREATE INDEX IF NOT EXISTS idx_demands_floor ON elevator_demands(floor)'))
+ connection.execute(text('CREATE INDEX IF NOT EXISTS idx_states_floor ON elevator_states(floor)'))
+ print("Database tables created!")
+ app.run(debug=True) # Run the app in debug mode
diff --git a/chatgpt/requirements.txt b/chatgpt/requirements.txt
index 14d1bb0..f6f7710 100644
--- a/chatgpt/requirements.txt
+++ b/chatgpt/requirements.txt
@@ -1,4 +1,4 @@
-Flask==2.0.2
-Flask-SQLAlchemy==2.5.1
-pytest==6.2.5
-pytest-flask==1.2.0
+Flask
+Flask-SQLAlchemy
+pytest
+pytest-flask