diff --git a/.gitignore b/.gitignore index 1e53d1362..3f0f6814c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,12 +10,14 @@ dist .editorconfig .DS_Store **/*.pyc +goex/goex-env/ goex/exec_engine/checkpoints/ goex/exec_engine/credentials/* !goex/exec_engine/credentials/credentials_utils.py !goex/exec_engine/credentials/supported.txt goex/docker/*/requirements.txt goex/docker/misc/images.json +goex-env/* ################## Berkley Function Call Leaderboard ########################## diff --git a/goex/authorizations/scripts/spotify_authorization.py b/goex/authorizations/scripts/spotify_authorization.py index b3affd997..4cfbe667c 100644 --- a/goex/authorizations/scripts/spotify_authorization.py +++ b/goex/authorizations/scripts/spotify_authorization.py @@ -10,8 +10,8 @@ import wsgiref.simple_server import wsgiref.util from urllib.parse import quote, urlparse, parse_qs -from .consts import AUTH_URL, CERT_FILE_PATH, KEY_FILE_PATH - +from consts import AUTH_URL, CERT_FILE_PATH, KEY_FILE_PATH +print(CERT_FILE_PATH) SPOTIFY_FOLDER_PATH = os.path.join(os.path.dirname(Path(os.path.realpath(__file__)).parent.parent), 'exec_engine/credentials/spotify') TOKEN_FILE = os.path.join(SPOTIFY_FOLDER_PATH, 'token.pickle') @@ -23,6 +23,7 @@ def main(): creds = pickle.load(token) # If there are no (valid) credentials available, let the user log in creds = run_spotify_flow() + print(creds) # Save the credentials for the next run if not os.path.isdir(SPOTIFY_FOLDER_PATH): os.mkdir(SPOTIFY_FOLDER_PATH) diff --git a/goex/cli.py b/goex/cli.py index ec5df891a..c098b3236 100644 --- a/goex/cli.py +++ b/goex/cli.py @@ -10,7 +10,7 @@ from main import ExecutionEngine, PythonAPIExecutor from exec_engine.utils import SQL_Type, Filesystem_Type -from exec_engine.db_manager import MySQLManager, SQLiteManager +from exec_engine.db_manager import MySQLManager, SQLiteManager, PostgreSQLManager from dotenv import load_dotenv import questionary @@ -213,6 +213,8 @@ def db_callback(prompt, generate_mode): db_manager = MySQLManager(config, docker_sandbox=engine.docker_sandbox) elif db_type == 'sqlite': db_manager = SQLiteManager(config, docker_sandbox=engine.docker_sandbox) + elif db_type == 'postgresql': + db_manager = PostgreSQLManager(config, docker_sandbox=engine.docker_sandbox) except Exception as e: print(f"Error during {db_type} Manager Init: {e}") return diff --git a/goex/exec_engine/db_manager.py b/goex/exec_engine/db_manager.py index 00089a2bd..fc98bfe2c 100644 --- a/goex/exec_engine/db_manager.py +++ b/goex/exec_engine/db_manager.py @@ -300,3 +300,120 @@ def close(self): if self.conn: self.cursor.close() self.conn.close() + +class PostgreSQLManager(DBManager): + """PostgreSQL database manager. + + Attributes: + _postgresql_imported (bool): flag to check if postgresql is imported. + + Methods: + connect: Establish connections to the DB + execute_db_call: Execute SQL call + commit_db_calls: Commit SQL calls + rollback_db_calls: Rollback SQL calls + close: Close the connection to the database + """ + _postgresql_imported = False + db_type = "postgresql" + TEST_CONFIG = "{'host': '127.0.0.1', 'user': 'root', 'password': ''}\n Use psycopg2 and make sure to create the database using subprocess before connection." + def __init__(self, connection_config, docker_sandbox: DockerSandbox = None): + """Initialize the PostgreSQLManager. + + Args: + connection_config (dict): configuration for the database connection, including keys for 'user', 'password', 'host', and 'database'. + """ + if not PostgreSQLManager._postgresql_imported: + global psycopg2 + import psycopg2 + PostgreSQLManager._postgresql_imported = True + + keys = connection_config.keys() + + if any(key not in keys for key in ['host', 'user', 'password', 'database']): + raise ValueError("Failed to initialize PostgreSQL Manager due to bad configs") + elif any([not connection_config['host'], not connection_config['user'], not connection_config['password'], not connection_config['database']]): + raise ValueError("Failed to initialize PostgreSQL Manager due to missing configs") + + self.connection_config = { + 'dbname': connection_config['database'] if 'database' in connection_config else 'postgres', + 'user': connection_config['user'] if 'user' in connection_config else 'postgres', + 'password': connection_config['password'] if 'password' in connection_config else '', + 'host': connection_config['host'] if 'host' in connection_config else '127.0.0.1' + } + + def connect(self): + """Establish connection to the MySQL database and create a cursor.""" + connection = None + try: + connection = psycopg2.connect(**self.connection_config) + self.conn = connection + self.cursor = connection.cursor() + self.update_schema_info() + except Exception as e: + if connection: + connection.close() + print("Failed to connect to the database. Error:", e) + + def update_schema_info(self): + schema_info = {} + get_all_tables_query = """ + SELECT table_name + FROM information_schema.tables + WHERE table_schema = 'public' + """ + self.cursor.execute(get_all_tables_query) + tables = self.cursor.fetchall() + for (table_name,) in tables: + self.cursor.execute(f"SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_name = '{table_name}';") + schema_info[table_name] = self.cursor.fetchall() + + self.schema = schema_info + + def execute_db_call(self, call): + """Execute a SQL call using the cursor.""" + if not self.conn: + self.connect() + try: + self.cursor.execute(call) + self.update_schema_info() + return 0 + except Exception as e: + return 1 + + def fetch_db_call(self, call: str) -> list[dict]: + """Execute a SQL call and return the results. + + Args: + call (str): SQL query to execute. + + Returns: + list[dict]: A list of dictionaries representing each row in the query result. + """ + if not self.conn: + self.connect() + try: + self.cursor.execute(call) + ret_val = self.cursor.fetchall() + self.update_schema_info() + return ret_val + except Exception as e: + return [] + + def commit_db_calls(self): + """Commit SQL calls.""" + if not self.conn: + self.connect() + self.conn.commit() + + def rollback_db_calls(self): + """Rollback SQL calls not committed.""" + if not self.conn: + self.connect() + self.conn.rollback() + + def close(self): + """Close the cursor and the connection to the database.""" + if self.conn: + self.cursor.close() + self.conn.close() diff --git a/goex/function/spotify_add_song.py b/goex/function/spotify_add_song.py new file mode 100644 index 000000000..e69de29bb diff --git a/goex/function/spotify_api_details.py b/goex/function/spotify_api_details.py new file mode 100644 index 000000000..240f51236 --- /dev/null +++ b/goex/function/spotify_api_details.py @@ -0,0 +1,14 @@ +import spotipy +from spotipy.oauth2 import SpotifyOAuth + +def create_spotify_api_object(): + scope = "user-library-read" + + client_id = 'd711dfc0d97440cb898b08fbdc2083c1' + client_secret = '1d4854768be047438a72d48a8c4622ab' + redirect_uri = 'http://localhost:8888/callback' + + sp = spotipy.Spotify(auth_manager=SpotifyOAuth(client_id = client_id, + client_secret = client_secret, + redirect_uri = redirect_uri, + scope=scope)) \ No newline at end of file diff --git a/goex/function/spotify_create_playlist.py b/goex/function/spotify_create_playlist.py new file mode 100644 index 000000000..aa0b744f7 --- /dev/null +++ b/goex/function/spotify_create_playlist.py @@ -0,0 +1,25 @@ +import os +import pickle +import spotipy +from spotipy.oauth2 import SpotifyOAuth +from spotipy.oauth2 import SpotifyClientCredentials + +def spotify_play_song(user,name): + # Load spotify credentials + credentials_path = './credentials/spotify/token.pickle' + if os.path.exists(credentials_path): + with open(credentials_path, 'rb') as token_file: + spotify_token = pickle.load(token_file) + else: + raise FileNotFoundError("Spotify token file not found.") + token_info = SpotifyOAuth(token=spotify_token) + # Initialization + if not token_info: + print("No account found") + return None + else: + sp = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials()) + if sp is None: + return None + # create playlist with inputted name and user ID + sp.user_playlist_create(user, name, public=True, collaborative=False, description='') \ No newline at end of file diff --git a/goex/function/spotify_list_artist_albums.py b/goex/function/spotify_list_artist_albums.py new file mode 100644 index 000000000..7179e946e --- /dev/null +++ b/goex/function/spotify_list_artist_albums.py @@ -0,0 +1,43 @@ +import os +import pickle +import spotipy +from spotipy.oauth2 import SpotifyOAuth +from spotipy.oauth2 import SpotifyClientCredentials + +def spotify_list_artist_albums(artist): + # Load spotify credentials + credentials_path = './credentials/spotify/token.pickle' + if os.path.exists(credentials_path): + with open(credentials_path, 'rb') as token_file: + spotify_token = pickle.load(token_file) + else: + raise FileNotFoundError("Spotify token file not found.") + token_info = SpotifyOAuth(token=spotify_token) + # Initialization + if not token_info: + print("No account found") + return None + else: + sp = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials()) + if sp is None: + return None + # Returns the most popular artist's URI with the same name + results = sp.search(q=artist, type='artist', limit=1) + artista = results['artist']['items'] + if artista: + artistaa = artista[0]['uri'] + else: + print("No artist found for:", artist) + return None + # Uses the artist URI to search through their spotify discography and returns the names of all their albums + + spotify = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials()) + + albums_results = spotify.artist_albums(artistaa, album_type='album') + albums = albums_results['items'] + while albums_results['next']: + albums_results = spotify.next(albums_results) + albums.extend(albums_results['items']) + + for album in albums: + print(album['name']) \ No newline at end of file diff --git a/goex/function/spotify_pause.py b/goex/function/spotify_pause.py new file mode 100644 index 000000000..dc2972e40 --- /dev/null +++ b/goex/function/spotify_pause.py @@ -0,0 +1,29 @@ +import pyautogui +import os +import pickle +import spotipy +from spotipy.oauth2 import SpotifyOAuth +from spotipy.oauth2 import SpotifyClientCredentials + +def spotify_pause(): + # Load spotify credentials + credentials_path = './credentials/spotify/token.pickle' + if os.path.exists(credentials_path): + with open(credentials_path, 'rb') as token_file: + spotify_token = pickle.load(token_file) + else: + raise FileNotFoundError("Spotify token file not found.") + token_info = SpotifyOAuth(token=spotify_token) + # Initialization + if not token_info: + print("No account found") + return None + else: + sp = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials()) + if sp is None: + return None + # Check to see if a song is currently playing + if sp.currently_playing: + pyautogui.press('stop') + else: + print("No song currently playing") \ No newline at end of file diff --git a/goex/function/spotify_play_album.py b/goex/function/spotify_play_album.py new file mode 100644 index 000000000..5172b3d9e --- /dev/null +++ b/goex/function/spotify_play_album.py @@ -0,0 +1,38 @@ +import os +import pickle +import spotipy +from spotipy.oauth2 import SpotifyOAuth +from spotipy.oauth2 import SpotifyClientCredentials + +def spotify_play_album(album_name): + # Load spotify credentials + credentials_path = './credentials/spotify/token.pickle' + if os.path.exists(credentials_path): + with open(credentials_path, 'rb') as token_file: + spotify_token = pickle.load(token_file) + else: + raise FileNotFoundError("Spotify token file not found.") + token_info = SpotifyOAuth(token=spotify_token) + # Initialization + if not token_info: + print("No account found") + return None + else: + sp = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials()) + if sp is None: + return None + # Searches the album name and finds the most popular match and then provides the URI from that + results = sp.search(q=album_name, type='album', limit=1) + albums = results['album']['items'] + if albums: + album = albums[0]['uri'] + else: + print("No tracks found for:", album_name) + return None + # Uses the URI to play the album on Spotify + try: + for track in album: + sp.start_playback(uris=[track]) + print("Playing") + except spotipy.SpotifyException as e: + print("Error", e) \ No newline at end of file diff --git a/goex/function/spotify_play_song.py b/goex/function/spotify_play_song.py new file mode 100644 index 000000000..ee48975ee --- /dev/null +++ b/goex/function/spotify_play_song.py @@ -0,0 +1,38 @@ +import os +import pickle +import spotipy +from spotipy.oauth2 import SpotifyOAuth +from spotipy.oauth2 import SpotifyClientCredentials + +def spotify_play_song(song_name): + # Load spotify credentials + credentials_path = './credentials/spotify/token.pickle' + if os.path.exists(credentials_path): + with open(credentials_path, 'rb') as token_file: + spotify_token = pickle.load(token_file) + else: + raise FileNotFoundError("Spotify token file not found.") + token_info = SpotifyOAuth(token=spotify_token) + # Initialization + if not token_info: + print("No account found") + return None + else: + sp = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials()) + if sp is None: + return None + # Get track uri from the song name + # '''searches song name and finds the most popular match and then provides the URI from that''' + results = sp.search(q=song_name, type='track', limit=1) + tracks = results['tracks']['items'] + if tracks: + song = tracks[0]['uri'] + else: + print("No tracks found for:", song_name) + return None + # Uses the URI to play the song on Spotify + try: + sp.start_playback(uris=[song]) + print("Playing") + except spotipy.SpotifyException as e: + print("Error", e) diff --git a/goex/function/spotify_queue.py b/goex/function/spotify_queue.py new file mode 100644 index 000000000..e4aa4849e --- /dev/null +++ b/goex/function/spotify_queue.py @@ -0,0 +1,34 @@ +import os +import pickle +import spotipy +from spotipy import add_to_queue +from spotipy.oauth2 import SpotifyOAuth +from spotipy.oauth2 import SpotifyClientCredentials +def spotify_queue(song_name): + # Load spotify credentials + credentials_path = './credentials/spotify/token.pickle' + if os.path.exists(credentials_path): + with open(credentials_path, 'rb') as token_file: + spotify_token = pickle.load(token_file) + else: + raise FileNotFoundError("Spotify token file not found.") + token_info = SpotifyOAuth(token=spotify_token) + # Initialization + if not token_info: + print("No account found") + return None + else: + sp = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials()) + if sp is None: + return None + # Get track uri from the song name + # '''searches song name and finds the most popular match and then provides the URI from that''' + results = sp.search(q=song_name, type='track', limit=1) + tracks = results['tracks']['items'] + if tracks: + song = tracks[0]['uri'] + else: + print("No tracks found for:", song_name) + return None + # Uses the URI to play the song on Spotify + add_to_queue(song, device_id=None) \ No newline at end of file diff --git a/goex/function/spotify_retrieve_album.py b/goex/function/spotify_retrieve_album.py new file mode 100644 index 000000000..f99ed6e28 --- /dev/null +++ b/goex/function/spotify_retrieve_album.py @@ -0,0 +1,17 @@ +import spotipy +from spotify_api_details import sp + +def get_album_info(album_id): + album = sp.album(album_id) + + album_info = { + 'name': album['name'], + 'release_date': album['release_date'], + 'total_tracks': album['total_tracks'], + 'artists': [artist['name'] for artist in album['artists']], + 'tracks': [track['name'] for track in album['tracks']['items']] + } + return album_info + +album_id = "4aawyAB9vmqN3uQ7FjRGTy" +print(get_album_info(album_id)) diff --git a/goex/localhost-key.pem b/goex/localhost-key.pem new file mode 100644 index 000000000..2f81f2151 --- /dev/null +++ b/goex/localhost-key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC80kwcaWMrZ9uo +r0xCCNn8aYsV3qEPMmrEqdEHGjV9aEhFkT/6IzOA8n30Sqj9IxW37dhcaPkuO5Tj +RNuftDpO1DkHoiBafvZZKljGEkvzhtQqUpTSrkNn5RtL8ZVfr6J8nYXjwXQv/Pc+ +6AoQeJGXV0MyeKYB1y8QMYNW+nTpzjblKCTyPaJwpjIX2bsRAEhZOTEeBNeUM/F0 +/zViCnAWESHNQDkct5E4GkImqycKuN97mqakdLOtGzTQsOm9mgOL7JSHiLFuEHpx +2Yd6fVXcX73RzGEzXCISue6OOQFEqUHNcnjdJHzi76jJLLK9cixCKfmR60mz0rZL +VOC9Qaq7AgMBAAECggEBAIxsjWP30GPAenCp5bmaYZtBVsmt5vOpWBPoAucYS713 +J4BtNDovoab9METUnXaceBNwlB6thtsmPmUCmZ7c5xwm4j7WCN+kWksXniu4Abob +hH/xKHLCXKjQjwtcn+0oblvkvmUSQvlJVQr4gDudnxEBOZ04cqUdxgdx+y5dV0Nk +7B09YNFiF56AJkKZh+uE4PPZUnCtKcTLQiZu66L55ZXgMTyItIG0rjXtHsqfnmsI +mufmuWqbj4zm6oCfhHlTZHg3ipVGqSqsLsSPiIT+OITV1Iq3SsFrBEBiLd1e/2bt +z4YEAHt/SsIgcIbMz405sLjx5zq3CS6PbtshBXvGruECgYEA7WNDoLktYxzGjXb5 +fspZPCbGL9qFox4vozm9m+iGipX2qVnO+VdCRTVqVS70Op4AKIhTOaKEIBldIV+H +hroXxV0IBfbPWs3P8F2iIP+rZwxrI8YyPrB/RpMFjOxgCnD7uwH1WWX2ed+f1C9j +ppOLYWRJ+ye4m3OPaJ+YaNqoa4sCgYEAy6A7yotP9Fx+fqTWXlvmWmhhUCfdoaZG +vo1F7VtryOLbMC2t7j7g5ObAutLcb7pKM/uNt8kDfWINO9dX0sVyQWZVIPqTXwiP +G9S+d5LjYlW1LRyNfAEWAYj9HGmNYzpInnf6HlbB53Yn7z7Hgba1WetoRKXH0zRK +D6bqQueSY5ECgYEAhHL8CrMHReOKTOfy8PFJtKwVrCGKqqZvAe13g4PLfYuKopa/ +SpOLID5bY8fc8M+zWuolNWUdvyMnuTUp0twF41Ky5VD+BLYMrRoxGaRNKCPfq4gs +BJpJiUECucwxEeJUMdU35XdkfD9vV95RBWIRRHfGZ2GUOPfTLhbN3MMTcV0CgYAp +FmP6MrIndKJxm4FF+PU6OOWp7uFhxNNxj8AUbOYK2vuvdQgrkrR+e7FYAURJpyVh +59hoxT7XBfn1oTYvaXfsimGTDfy+qFN4Ii1so08OgIzWVXSXGQD/vkyUdxf4dcJB +d6QYixkFQ2o7dEd9fQiK5F1dvLXdgCeRkPSkG2YGgQKBgHLlac0GVEd8m3ACmsbk +n1wNWueOVnbdbhoJPdUA59S39Nse6LQnmma6PPoRAs2rzt023zymWizq9vREVNYR +OJvm5wobk5yIWZSPzwvNLDoUlj736K7stYfXpe1MGKAzhrUHYFuIDCgHT5+89ea+ +XfahcBE8Q6FVt3lLmVLlylZQ +-----END PRIVATE KEY----- diff --git a/goex/localhost.pem b/goex/localhost.pem new file mode 100644 index 000000000..fd1885e4c --- /dev/null +++ b/goex/localhost.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEBjCCAm6gAwIBAgIQEumytb68NOI0c3Kilp6wYzANBgkqhkiG9w0BAQsFADBb +MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExGDAWBgNVBAsMD2VyaXMy +OUBWQU5BSEVJTTEfMB0GA1UEAwwWbWtjZXJ0IGVyaXMyOUBWQU5BSEVJTTAeFw0y +NDEwMTIyMjI1MDVaFw0yNzAxMTIyMzI1MDVaMEMxJzAlBgNVBAoTHm1rY2VydCBk +ZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTEYMBYGA1UECwwPZXJpczI5QFZBTkFIRUlN +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvNJMHGljK2fbqK9MQgjZ +/GmLFd6hDzJqxKnRBxo1fWhIRZE/+iMzgPJ99Eqo/SMVt+3YXGj5LjuU40Tbn7Q6 +TtQ5B6IgWn72WSpYxhJL84bUKlKU0q5DZ+UbS/GVX6+ifJ2F48F0L/z3PugKEHiR +l1dDMnimAdcvEDGDVvp06c425Sgk8j2icKYyF9m7EQBIWTkxHgTXlDPxdP81Ygpw +FhEhzUA5HLeROBpCJqsnCrjfe5qmpHSzrRs00LDpvZoDi+yUh4ixbhB6cdmHen1V +3F+90cxhM1wiErnujjkBRKlBzXJ43SR84u+oySyyvXIsQin5ketJs9K2S1TgvUGq +uwIDAQABo14wXDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEw +HwYDVR0jBBgwFoAUpWCLhQL4F/m/s+TlJKjDWdgHDFUwFAYDVR0RBA0wC4IJbG9j +YWxob3N0MA0GCSqGSIb3DQEBCwUAA4IBgQBVya7UENdHk4DPgQR5BZwSdLdN0eso +cFoxiSb/XdkaiU3d5PgqAJo5um1HParFhGJpY7C/Z6wad4ju/fteq5Hlw0fbMTfA +wat+Ggkjg4Bnh0uJY+dZ4v+GydxetXzK7Bg2y1zZG9XAu/lV40EupNO4OAj9abBw +flwCZsEsK2qxXe5JaTZgdpEjJNq1r20nQrvt369+6oJPlB8kf66YzEgL8Hg4l+xq +0yFpE9szxSgLhfBRZSXzNCyAqPxIz/XbmRctcABvs0c/nf6KsStrECBSUbsqKDUw ++1Zql2G3E/oFit153mpK5cWBF7OwKFYXDKBnf+S7I3H6sYpfUWByd3i0S2NKIGj2 +1GaY8kDHvOEzB0nLnPaEtm/BcRF2i5/3Yt7CAyugtnZTL8ffx08NB5HJX2CGygmF +s32N89jXqCslg7Z2aIIEDNYN+J/zgZothFTT6sdEAGHs/eOXhBYSOICKkz42WO/C +IEOoLIzGrn+YRAoZhnQOrwtB8sPYTfgaIIk= +-----END CERTIFICATE-----