diff --git a/.gitignore b/.gitignore index 5200d18..a3d4aab 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,4 @@ __pycache__/ # for workflow docker_and_azure.txt +eirgrid_api.ipynb \ No newline at end of file diff --git a/main.py b/main.py index d8e15bf..de93ae5 100644 --- a/main.py +++ b/main.py @@ -11,6 +11,7 @@ ConversationHandler, CallbackContext, ) +from elevenlabs import generate from subs.energy_api import * from subs.openai_script import * from dotenv import load_dotenv @@ -20,6 +21,8 @@ load_dotenv() Telegram_energy_api = os.environ.get("Telegram_energy_api") CHANNEL_ID_FOR_FEEDBACK = os.environ.get("CHANNEL_ID_FOR_FEEDBACK") +ELEVEN_API_KEY = os.environ.get("ELEVEN_API_KEY") + # Set up logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) @@ -123,16 +126,28 @@ async def energy_api_func(update: Update, context: CallbackContext): summary_text, df_with_trend = find_optimized_relative_periods(df_) today_date = df_with_trend.index[0].strftime("%d/%m/%Y") eu_summary_text = optimize_categorize_periods(df_with_trend) - quantile_summary_text, _ = find_optimized_relative_periods( + quantile_summary_text, df_with_trend_ = find_optimized_relative_periods( df_with_trend ) # Generate this based on your DataFrame prompt = create_combined_gpt_prompt( today_date, eu_summary_text, quantile_summary_text ) + + # get generated prompt gpt_recom = opt_gpt_summarise(prompt) + # slice the energy saving actions part + energy_saving_actions = get_energy_actions(gpt_recom) + audio_msg = generate_voice(energy_saving_actions) + await context.bot.send_voice( + update.effective_chat.id, + audio_msg, + caption="Here's your energy-saving tips 🎙️", + ) await update.message.reply_text(gpt_recom) - await send_co2_intensity_plot(update, context, df_with_trend) + if len(df_with_trend) > 1: + await send_co2_intensity_plot(update, context, df_with_trend) + del audio_msg else: await update.message.reply_text( @@ -172,7 +187,7 @@ async def energy_status(update: Update, context: ContextTypes.DEFAULT_TYPE): # send the list of options and ask the user to select one await update.message.reply_text( - "I'm here to help you with energy insights. Which category would you like more information about?", + "I'm here to help you with energy insights. Which category would you like more information about?💡🌍🔍", reply_markup=reply_markup, ) # Set the conversation state to SELECT_COLUMN diff --git a/requirements.txt b/requirements.txt index c1bde89..3cea50b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,4 +5,5 @@ matplotlib==3.7.0 requests==2.31.0 openai==1.11.1 python-dotenv==1.0.1 -seaborn==0.13.2 \ No newline at end of file +seaborn==0.13.2 +elevenlabs==0.2.27 \ No newline at end of file diff --git a/subs/openai_script.py b/subs/openai_script.py index 77b3a3b..6f50de9 100644 --- a/subs/openai_script.py +++ b/subs/openai_script.py @@ -5,10 +5,12 @@ import numpy as np import pandas as pd from dotenv import load_dotenv +from elevenlabs import generate # Load environment variables from .env file load_dotenv() OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY") +ELEVEN_API_KEY = os.environ.get("ELEVEN_API_KEY") def optimize_categorize_periods(df): @@ -55,52 +57,60 @@ def optimize_categorize_periods(df): def find_optimized_relative_periods(df): - # Normalize CO2 values to a 0-1 scale - df["normalized"] = (df["Value"] - df["Value"].min()) / ( - df["Value"].max() - df["Value"].min() - ) - - # Define thresholds for relative categorization - low_threshold = df["normalized"].quantile(0.33) - high_threshold = df["normalized"].quantile(0.66) - - # Categorize each timestamp - df["category"] = pd.cut( - df["normalized"], - bins=[-np.inf, low_threshold, high_threshold, np.inf], - labels=["Low", "Medium", "High"], - ) - - # Find consecutive periods with the same category - df["group"] = (df["category"] != df["category"].shift()).cumsum() - # Prepare summary text - # summary_text = "Considering absolute CO2 emission values, determined by data trends, distinct periods are identified as:\n\n" - summary_text = "" - - # Initialize a dictionary to store concatenated periods for each category - period_summary = {"Low": [], "Medium": [], "High": []} + if len(df) > 1: + # Normalize CO2 values to a 0-1 scale + df["normalized"] = (df["Value"] - df["Value"].min()) / ( + df["Value"].max() - df["Value"].min() + ) - # Define emojis for each category - emoji_dict = {"Low": "🟢", "Medium": "🟡", "High": "🔴"} + # Define thresholds for relative categorization + low_threshold = df["normalized"].quantile(0.33) + high_threshold = df["normalized"].quantile(0.66) - # Group by category and group to concatenate periods - for (category, group), data in df.groupby(["category", "group"]): - start_time = data.index.min().strftime("%H:%M") - end_time = data.index.max().strftime("%H:%M") - # For periods that start and end at the same time, just show one time - period_str = ( - f"{start_time} to {end_time}" if start_time != end_time else start_time + # Categorize each timestamp + df["category"] = pd.cut( + df["normalized"], + bins=[-np.inf, low_threshold, high_threshold, np.inf], + labels=["Low", "Medium", "High"], ) - period_summary[category].append(period_str) - # Format the summary text for each category - for category in ["Low", "Medium", "High"]: - if period_summary[category]: - periods = ", ".join(period_summary[category]) - summary_text += f"- {emoji_dict[category]} {category} Emission: {periods}\n" - else: - summary_text += f"- {emoji_dict[category]} {category} Emission: No specific periods identified.\n" + # Find consecutive periods with the same category + df["group"] = (df["category"] != df["category"].shift()).cumsum() + + # Prepare summary text + # summary_text = "Considering absolute CO2 emission values, determined by data trends, distinct periods are identified as:\n\n" + summary_text = "" + + # Initialize a dictionary to store concatenated periods for each category + period_summary = {"Low": [], "Medium": [], "High": []} + + # Define emojis for each category + emoji_dict = {"Low": "🟢", "Medium": "🟡", "High": "🔴"} + + # Group by category and group to concatenate periods + for (category, group), data in df.groupby(["category", "group"]): + start_time = data.index.min().strftime("%H:%M") + end_time = data.index.max().strftime("%H:%M") + # For periods that start and end at the same time, just show one time + period_str = ( + f"{start_time} to {end_time}" if start_time != end_time else start_time + ) + period_summary[category].append(period_str) + + # Format the summary text for each category + for category in ["Low", "Medium", "High"]: + if period_summary[category]: + periods = ", ".join(period_summary[category]) + summary_text += ( + f"- {emoji_dict[category]} {category} Emission: {periods}\n" + ) + else: + summary_text += f"- {emoji_dict[category]} {category} Emission: No specific periods identified.\n" + else: + summary_text = ( + "Sorry, we do not have enough data to process data trend analysis." + ) return summary_text, df @@ -120,9 +130,9 @@ def create_combined_gpt_prompt(date, eu_summary_text, quantile_summary_text): "- 🔍 Data Trend Schedule: ONLY report it\n" f"- 💡 Energy-Saving Actions: Give an example of energy-saving actions for each category of CO2 emission trend, " f"considering the current season ({date}). Examples should cover:\n" - " - Low Emission Periods: [Your Example Here]\n" - " - Medium Emission Periods: [Your Example Here]\n" - " - High Emission Periods: [Your Example Here]\n" + " -🟢 Low Emission Periods: [Your Example Here]\n" + " -🟡 Medium Emission Periods: [Your Example Here]\n" + " -🔴High Emission Periods: [Your Example Here]\n" ) prompt_text = ( @@ -131,7 +141,7 @@ def create_combined_gpt_prompt(date, eu_summary_text, quantile_summary_text): "advice, utilizing specific data trends.\n\n" f"{prompt_data}" "💡 In periods of low emissions, feel free to use energy-intensive appliances without much concern for reduction.\n\n" - f"👉 Please use the following format for your response: \n\n {structure_example}\n" + f"👉 Please use the following format for your response and avoid using * in your response: \n\n {structure_example}\n" ) return prompt_text @@ -166,3 +176,35 @@ def opt_gpt_summarise(prompt): return generated_text except Exception as e: return str(e) + + +def get_energy_actions(text): + start_keyword = "- 💡 Energy-Saving Actions:" + end_keywords = [ + "📋", + "- 🇪🇺", + "- 🔍", + "- 💡", + ] # Add possible start of next sections if format varies + end_keyword = next( + (kw for kw in end_keywords if kw in text[text.find(start_keyword) :]), + None, + ) + + # Find start and end positions + start_pos = text.find(start_keyword) + end_pos = text.find(end_keyword, start_pos + 1) if end_keyword else len(text) + + # Extract the section + energy_saving_actions = text[start_pos:end_pos].strip() + return energy_saving_actions + + +def generate_voice(text): + return generate( + text=text, + voice="Callum", + model="eleven_multilingual_v1", + output_format="mp3_44100_128", + api_key=ELEVEN_API_KEY, + )