def run(): configuration.set_preffered_dbms("mysql") print("BOT STARTED...") updater = Updater( token=configuration.get_bot_token( configuration.get_file_location("config.yaml")), use_context=True, ) dispatcher = updater.dispatcher job_queue_manager = updater.job_queue start_handler = CommandHandler("start", start) moodle_epn_handler = CommandHandler("url", get_moodle_epn_url) get_tasks_handler = CommandHandler("update", get_tasks) save_subject_handler = CommandHandler("subject", save_subject_command) get_jobs_handler = CommandHandler("jobs", get_jobs) dispatcher.add_handler(start_handler) dispatcher.add_handler(moodle_epn_handler) dispatcher.add_handler(get_tasks_handler) dispatcher.add_handler(save_subject_handler) dispatcher.add_handler(get_jobs_handler) job_queue_manager.run_repeating(get_new_tasks, interval=60 * 60 * 1, first=10) dispatcher.add_error_handler(error_handler) updater.start_polling() updater.idle()
def write_todo_tasks(tasks_list: list): """This function writes all tasks from a list. Args: tasks_list (list): List containing tasks to be added to the todo.txt file """ with open(configuration.get_file_location("todo.txt"), "w") as todo: todo.writelines(tasks_list)
def get_todo_tasks() -> list: """This function gets a all tasks that exits on todo.txt. Returns: tasks_list (List): List of strings from all tasks that exits on todo.txt """ with open(configuration.get_file_location("todo.txt"), "r") as todo: tasks_list = todo.readlines() return tasks_list
def get_done_tasks() -> list: """This function gets a all tasks that exits on todo.txt. Returns: tasks_list (List): List of strings from all tasks that exits on todo.txt """ with open(configuration.get_file_location("done.txt"), "r") as done: readed_lines = done.readlines() done_list = [] for done_item in readed_lines: done_list.append(done_item.split(" ", 2)) return done_list
def get_db(): """This function returns the database connection. Selects between sqlite3 or mysql Returns: db (Connection): Database connection that access to tasks and subjects. """ if (configuration.get_preferred_dbms( configuration.get_file_location("config.yaml")) == "default"): db = sqlite3.connect(configuration.get_file_location("tasks.db")) return db elif (configuration.get_preferred_dbms( configuration.get_file_location("config.yaml")) == "mysql"): mysql_credentials = configuration.get_mysql_credentials( configuration.get_file_location("config.yaml")) if mysql_credentials: db = MySQLConnection( host=mysql_credentials["host"], database=mysql_credentials["database"], user=mysql_credentials["user"], password=mysql_credentials["password"], ) return db
def convert_ics_to_csv(url: str): """This function downloads the moodle calendar and addEvents to a CSV file. Args: url (str): URL to download moodle calendar """ print("Descargando calendario desde Aula Virtual...") logging.info("Descargando calendario desde Aula Virtual...") filename = configuration.get_file_location("mycalendar.ics") if os.path.exists(filename): os.remove(filename) wget.download(url, filename) add_event(find_header(filename), filename) print("\nEspere...") logging.info("Descarga de calendario finalizada.")
def load_csv_tasks_to_db(username: str, user_dict: dict): """This function loads csv tasks to the database Args: username (str): The username for the current task. user_dict (dict): User dictionary with keys to acces to trello. Raises: FileNotFoundError """ try: with open(configuration.get_file_location("calendar.csv")) as csv_file: logging.info("CSV abierto.") csv_reader = csv.reader(csv_file, delimiter=";") line_count = 0 for row in csv_reader: if line_count == 0: line_count += 1 elif len(row) > 9 and not line_count == 0: # print(len(row)) configuration.create_subject( get_subject_name_from_csv(row[9]), row[2], user_dict) subject_id = connectSQLite.get_subject_id( get_subject_name_from_csv(row[9])) # print(row[0]) # Siempre se extraera la fecha aun cuando pueda tener un # formato YMDTXXX task = TareaClass.Tarea( row[1], row[2], row[3], datetime.strptime(row[7][0:8], "%Y%m%d"), subject_id, ) connectSQLite.save_user_task(task, username) # print("Las tareas nuevas se agregaron a la BD") logging.info("Las tareas nuevas se agregaron a la BD") except (FileNotFoundError): print("!FNF")
def main(argv): configuration.set_preffered_dbms("default") args = define_args() if args.add_user: Get_Trello_MoodleEPN_Keys.onboard(False) elif args.todo: todo_generator.generate_todo_txt() elif args.bot: policalbot.run() elif args.load_subjects_from_csv: MateriasLoaderToDB.load_subjects_to_db() elif args.update_subjects_from_csv: MateriasLoaderToDB.update_subjects_to_db() elif args.show_directory: print("Working directory in: " + configuration.get_working_directory()) elif args.set_telegram_token: token = configuration.get_bot_token("config.yaml") if token: answer = input( "Found existing Telegram Token: " + token + ", overwrite? (y/n): " ) if answer == "y" or answer == "yes" or answer == "Y" or answer == "YES": configuration.set_bot_token( configuration.get_file_location("config.yaml"), args.set_telegram_token, ) else: print("Operation Canceled") else: users = None while users is None: users = configuration.load_config_file("polical.yaml") if users is None: Get_Trello_MoodleEPN_Keys.onboard(False) for user in users.keys(): tasks_processor.save_tasks_to_db( users[user]["calendar_url"], user, users[user] ) tasks_processor.send_tasks_to_trello(user, users[user])
.. module:: SendTaskToTrello :platform: Unix, Windows :synopsis: This module sends tasks from the database to trello. .. moduleauthor:: Luis Andrade <*****@*****.**> """ from trello import TrelloClient from polical import connectSQLite from polical import configuration from datetime import datetime import logging logging.basicConfig( filename=configuration.get_file_location("Running.log"), level=logging.INFO, format="%(asctime)s:%(levelname)s:%(message)s", ) def send_task_to_trello(username: str, user_dict: dict): """This function sends tasks from database that are stored as not sended to trello. Args: username (str): The username for the current task. user_dict (dict): User dictionary with keys to acces to trello. """ client = TrelloClient( api_key=user_dict["api_key"], api_secret=user_dict["api_secret"],
def add_event(header: list, filename: str): """This function adds a event as a new line in csv file. Args: header (list): Header for the csv. filename (str): The filename where file would be written. Returns: None. If not has headers """ if not header: return None f = open(configuration.get_file_location(filename), "r", encoding="utf-8") f2 = open(configuration.get_file_location(EXPORT_FILENAME), "w+") f1 = f.readlines() # print(header) for x in header: # Write the defenitive header f2.write(x) if x != "END": f2.write(";") f2.write("\n") begin_writing_bool = False # Flag to detect the line BEGIN:VEVENT # , and start saving the parameters of the event # in this case is for getting the headers for the CSV normal_writing_bool = ( False # Flag to say that is writting everything but no description ) description_writing_bool = False # Flag to say that is writting a description for x in f1: # Read all of the lines between BEGIN:VEVENT and END:VEVENT # Separate the tags from the content, the results are limited to 2 line_list = x.split(":", 1) # list for looking the first character on the line chars = [c for c in line_list[0]] if ( line_list[0] == "BEGIN" and line_list[1] == VEVENT_WITH_LINE_BREAK ): # Here an event begins normal_writing_bool = True # Flag activated, for ONE EVENT begin_writing_bool = True # Flag activated, for the whole set of events line_list[1] = "VEVENT" # ERASED the "\n" character # Here a DESCRIPTION begins, can have a lot of lines. elif line_list[0] == "DESCRIPTION": description_writing_bool = True # Flag activated, for the DESCRIPTION # A description can be conformed by a lot of especial characters so # the description will be between "" f2.write('"') # If the lines are not beginning with an space or an "\t", means that # is another tag and not currently a decription, but it can change elif ( chars[0] != " " and chars[0] != "\t" and chars[0] != "\n" and description_writing_bool ): description_writing_bool = ( False # Flag deactivated, it means a description content ends ) f2.write('"' + ";") # Also finished with a semicolon elif ( line_list[0] == "END" and line_list[1] == VEVENT_WITH_LINE_BREAK ): # An event fisnish normal_writing_bool = False # Flag deactivated, a event ends if normal_writing_bool and description_writing_bool == False: # If normal_writing_bool is activated and description_writing_bool deactivated means that # is every posible tag except a description # print(list) try: # Delete the especial character removebsn = line_list[1].split("\n", 1) f2.write(removebsn[0].replace(";", "") + ";") # Adds semicolon and avoids double ";" except Exception as e: print(e) elif normal_writing_bool and description_writing_bool: # If normal_writing_bool and description_writing_bool are # activated it is a description and can have differentes kind of # lines new_list = { x.replace("\n", "").replace("\t", "").replace("DESCRIPTION", "") for x in line_list } for x in new_list: # A for loop to add the contents to the description f2.write(x) elif normal_writing_bool == False and begin_writing_bool: # if normal_writing_bool is deactivated and begin_writing_bool activated, that means that # a event has ended so a "\n" is added f2.write("\n")
def find_header(filename: str) -> list: """This function looks for all the file to get the most longest header. Args: icsCal (str): The ics file location. Returns: (list): List containing the largest header list. """ f = open(configuration.get_file_location(filename), "r", encoding="utf-8") f2 = open(configuration.get_file_location(EXPORT_FILENAME), "w+") f1 = f.readlines() begin_writing_bool = False # Flag to detect the line BEGIN:VEVENT # , and start saving the parameters of the event # in this case is for getting the headers for the CSV normal_writing_bool = False description_writing_bool = False # Saving a description that could be large for x in f1: line_list = x.split(":", 1) chars = [c for c in line_list[0]] # Looking for the line that begins an event if line_list[0] == "BEGIN" and line_list[1] == VEVENT_WITH_LINE_BREAK: normal_writing_bool = True begin_writing_bool = True # Looking for the line where the description begins to activate its # flag elif line_list[0] == "DESCRIPTION": description_writing_bool = True # If the lines are not beginning with an space or an "\t", means that # is another tag and not currently a decription, but it can change elif ( chars[0] != " " and chars[0] != "\t" and chars[0] != "\n" and description_writing_bool ): description_writing_bool = False if line_list[0] == "END": # If the event comes to its end the flag # deactivates normal_writing_bool = False else: "" # If all of the headers are reached it only writes and \n and stops the for loop if normal_writing_bool == False and begin_writing_bool == True: f2.write("END\n") elif normal_writing_bool and description_writing_bool == False: # Everything that can be and tag inside an event is appended to the # list, but if the events are irregulars this can cause errors f2.write(line_list[0].replace(";", "") + ";") elif ( normal_writing_bool and description_writing_bool and line_list[0] == "DESCRIPTION" ): # If the tag is description this is added, but can be lines that # are part of the description f2.write(line_list[0].replace(";", "") + ";") f2.close() # File calendar.csv is closed headers_list = [] # A list of every posible header if the ics file is # IRREGULAR # Reading the calendar.csv temporary to get the header with the most number # of tags with open(configuration.get_file_location(EXPORT_FILENAME)) as csv_file: csv_reader = csv.reader(csv_file, delimiter=";") for row in csv_reader: headers_list.append(row) # File name to erase the calendar.csv that is temporary filename = configuration.get_file_location(EXPORT_FILENAME) if os.path.exists(filename): os.remove(filename) # Get the header with the most number of tags if len(headers_list) > 1: return max(headers_list, key=len) else: return []
import trello import requests from polical import configuration import logging logging.basicConfig(filename=configuration.get_file_location('Running.log'), level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s') class GTDException(Exception): '''single parameter indicates exit code for the interpreter, because this exception typically results in a return of control to the terminal''' def __init__(self, errno): self.errno = errno class TrelloConnection: '''this one handles connection, retry attempts, stuff like that so it doesn't bail out each time we lose connection creating a connection requires configuration to be parsed because we need the api keys- so this will need to be invoked after the config parser is done doing its thing, with a parameter perhaps being the config :param bool autoconnect: should we make a network connection to Trello immediately? ''' def __init__(self, config, autoconnect=True): self.autoconnect = autoconnect self.config = config self.trello = self.__connect(config) if autoconnect else None def __connect(self, config): trello_client = self.initialize_trello(config)
def onboard(no_open: bool, output_path="polical.yaml"): """Obtain Trello API credentials and put them into your config file. This is invoked automatically the first time you attempt to do an operation which requires authentication. The configuration file is put in an appropriate place for your operating system. """ username = "" output_file = configuration.get_file_location( output_path) # Use platform detection user_api_key_url = "https://trello.com/app-key" request_token_url = "https://trello.com/1/OAuthGetRequestToken" authorize_url = "https://trello.com/1/OAuthAuthorizeToken" access_token_url = "https://trello.com/1/OAuthGetAccessToken" calendar_moodle_epn_url = "https://aulasvirtuales.epn.edu.ec/calendar/export.php?" # Nuevo link https://aulasvirtuales.epn.edu.ec/calendar/export.php? # First, open the URL that allows the user to get an auth token. Tell them to copy both into the program logging.info("Mostrando print-board al usuario") print( "Bienvenido a PoliCal!\n Recuerde que antes de iniciar el proceso de obtención de credenciales ud debe tener una cuenta en Trello y en el Aula Virtual, y deben estar iniciadas las sesiones en el navegador predeterminado" ) print("") print("\n\n") username = input("Ingrese su nombre:") print("PASO 1: Acceso a Trello") print("Recuerde ingresar e iniciar sesion previamente a www.trello.com") print("En su navegador web se cargará el siguiente URL:") print(" " + user_api_key_url) print( 'Cuando llegue a esa página, inicie sesión y copie la "Tecla" o "Key" que se muestra en un cuadro de texto.' ) print( "Si es la primera vez que realiza este proceso debe aceptar los terminos y condiciones de Trello" ) input("Presione ENTER para ir al enlace") if not no_open: with DevNullRedirect(): webbrowser.open_new_tab(user_api_key_url) api_key = input('Por favor, introduzca el valor de "Tecla" o "Key":') print( 'Ahora desplácese hasta la parte inferior de la página y copie el "Secreto" o "Secret" que se muestra en un cuadro de texto.' ) api_secret = input('Por favor, introduzca el valor de "Secret":') # Then, work on getting OAuth credentials for the user so they are permanently authorized to use this program print("\n\n") print("PASO 2: Permitir acceso de Polical a Trello") print( "Ahora obtendremos las credenciales de OAuth necesarias para usar este programa..." ) # The following code is cannibalized from trello.util.create_oauth_token from the py-trello project. # Rewriting because it does not support opening the auth URLs using webbrowser.open and since we're using # click, a lot of the input methods used in that script are simplistic compared to what's available to us. # Thank you to the original authors! """Step 1: Get a request token. This is a temporary token that is used for having the user authorize an access token and to sign the request to obtain said access token.""" session = OAuth1Session(client_key=api_key, client_secret=api_secret) try: response = session.fetch_request_token(request_token_url) except TokenRequestDenied: print("Invalid API key/secret provided: {0} / {1}".format( api_key, api_secret)) sys.exit(1) resource_owner_key, resource_owner_secret = ( response.get("oauth_token"), response.get("oauth_token_secret"), ) """Step 2: Redirect to the provider. Since this is a CLI script we do not redirect. In a web application you would redirect the user to the URL below.""" user_confirmation_url = "{authorize_url}?oauth_token={oauth_token}&scope={scope}&expiration={expiration}&name={name}".format( authorize_url=authorize_url, oauth_token=resource_owner_key, expiration="never", scope="read,write", name="PoliCal", ) print( "Visite la siguiente URL en su navegador web para autorizar a PoliCal acceso a su cuenta:" ) print(" " + user_confirmation_url) print( "Concedale los permisos a PoliCal para acceder a sus datos de Trello, estas credenciales se mantendran de manera local" ) input("Presione ENTER para ir al enlace") if not no_open: with DevNullRedirect(): webbrowser.open_new_tab(user_confirmation_url) """After the user has granted access to you, the consumer, the provider will redirect you to whatever URL you have told them to redirect to. You can usually define this in the oauth_callback argument as well.""" confirmation = input( "¿Has autorizado a PoliCal? Escriba n para no y S para si:") while confirmation == "n": confirmation = input( "¿Has autorizado a PoliCal? Escriba n para no y S para si:") oauth_verifier = input("¿Cuál es el código de verificación?:").strip() """Step 3: Once the consumer has redirected the user back to the oauth_callback URL you can request the access token the user has approved. You use the request token to sign this request. After this is done you throw away the request token and use the access token returned. You should store this access token somewhere safe, like a database, for future use.""" session = OAuth1Session( client_key=api_key, client_secret=api_secret, resource_owner_key=resource_owner_key, resource_owner_secret=resource_owner_secret, verifier=oauth_verifier, ) access_token = session.fetch_access_token(access_token_url) """Step 4: Ahora se debe buscar el calendario en formato ICS generado por el Aula Virtual""" print("\n\n") print("PASO 3: Obtención del calendario del Aula Virtual") print( "Recuerde ingresar e iniciar sesion previamente a https://aulasvirtuales.epn.edu.ec/" ) print( "A continuación se abrirá un link hacia el Aula Virtual EPN, en proximos eventos para: elija Todos los cursos" ) print( "y a continuación desplácese a la parte más inferior de la página y de clic en el botón Exportar Calendario" ) print( 'Luego, en la opción Exportar seleccione todos los eventos y después en "para" seleccione los 60 días recientes y próximos' ) print("Finalmente de clic en el boton Obtener URL del calendario") print( "Visite la siguiente URL en su navegador web para obtener el calendario del aula virtual EPN:" ) print(" " + calendar_moodle_epn_url) input( "Presione ENTER para ir al enlace e iniciar el proceso, no olvide verificar si su sesión del Aula Virtual se encuentra activa" ) if not no_open: with DevNullRedirect(): webbrowser.open_new_tab(calendar_moodle_epn_url) calendar_url = calendar_moodle_epn_url while not (configuration.check_for_url(calendar_url)): calendar_url = input( "Por favor, introduzca el url generado por el Aula Virtual, si este es erróneo se volverá a solicitar:" ) final_output_data = { "oauth_token": access_token["oauth_token"], "oauth_token_secret": access_token["oauth_token_secret"], "api_key": api_key, "api_secret": api_secret, "calendar_url": calendar_url, } print("\n\n") # Ensure we have a folder to put this in, if it's in a nested config location """ output_folder = os.path.dirname(output_file) if not os.path.isdir(output_folder): os.makedirs(output_folder) print('Created folder {0} to hold your configuration'.format(output_folder)) # Try to be safe about not destroying existing credentials """ board_id, owner_id = get_working_board_id( api_key, api_secret, access_token["oauth_token"], access_token["oauth_token_secret"], ) final_output_data["board_id"] = board_id final_output_data["owner_id"] = owner_id if check_file_existence(output_file): check_user_on_file(output_file, username + owner_id) with open(output_file) as file: # use safe_load instead load super_final = yaml.safe_load(file) super_final[username + owner_id] = final_output_data connectSQLite.save_user(username + owner_id) else: super_final = {username + owner_id: final_output_data} connectSQLite.save_user(username + owner_id) with open(output_file, "w") as f: f.write(yaml.safe_dump(super_final, default_flow_style=False))