Esempio n. 1
0
    def create_event(self, title, start_time):
        service = self.__create_sevice()
        calender_id = self.__get_calendar_id()

        event = {
            'summary': title,
            'start': {
                'date': start_time.strftime("%Y-%m-%d"),
            },
            'end': {
                'date': start_time.strftime("%Y-%m-%d"),
            },
            'reminders': {
                'useDefault': False,
                'overrides': [
                    {
                        'method': 'popup',
                        'minutes': 1440
                    },
                ],
            },
        }
        signal.alarm(30)
        event = service.events().insert(calendarId=calender_id,
                                        body=event).execute()
        signal.alarm(0)

        event_id = event["id"]
        write_log("Event created.", "Event-ID: " + event_id,
                  "Event-Title: " + title)
        return event_id
Esempio n. 2
0
 def run(self):
     while True:
         try:
             # Threading for elternportal crawling while doing the google calendar crawler in the main thread.
             with concurrent.futures.ThreadPoolExecutor() as executor:
                 future = executor.submit(crawler.fetch_exams)
                 previous_exams = calendar.fetch_events()
                 exams = future.result()
             # If the calendar is empty it means that this is the first time creating it.
             # So it needs to add all the exams to the calendar
             if self.__is_calendar_empty(previous_exams):
                 self.__add_exams_to_calendar(exams)
             # Otherwise it checks for updates.
             else:
                 write_log("Checking for updates")
                 self.__check_for_updates(exams, previous_exams)
             write_log(f"Waiting for {config.wait_between_check} seconds before continuing.")
             # wait for the number of seconds specified in the settings before proceeding.
             time.sleep(config.wait_between_check)
         except KeyboardInterrupt:
             write_log("Application terminated.")
             exit(0)
         except Exception as exception:
             try:
                 write_log("An Error Occurred.", exception,
                           f"Waiting for {config.wait_between_error} seconds before continuing.")
                 # After an error occurred the app will wait for the specified number of seconds before proceeding
                 # and trying it again.
                 time.sleep(config.wait_between_error)
             except KeyboardInterrupt:
                 write_log("Application terminated.")
                 exit(0)
Esempio n. 3
0
    def __check_for_updates(self, exams, previous_exams):
        exam_shift = 0
        previous_exam_shift = 0
        index = 0
        # The "+ (exam_shift + previous_exam_shift)" in the next line is making it so that if there are two wrong
        # entries checking each other that the list is extended by one so that the program can find both the errors and
        # correct them. That only works if the two wrong entries checking themselves happen to be the last ones and if
        # exactly as many events get deleted as created.
        while index < len(self.__longest_list(exams, previous_exams)) + (exam_shift + previous_exam_shift):
            # The exam and previous exam shifts balance out the list if something new or old was found.
            # Because that means that if for example a new exam is inserted into the exam list the following exams will
            # be out of order by one.
            exam = self.__safe_list_get(exams, index - exam_shift)
            previous_exam = self.__safe_list_get(previous_exams, index - previous_exam_shift)

            # Whenever something is added or deleted the length of the list is expand by one.
            # When something is added and deleted the length stays the same but it will still continue counting.
            # So it breaks if both times nothing is returned.
            if exam is None and previous_exam is None:
                break

            # If the last part of the list contains a old exam but not a new one it gets deleted.
            # (Just if it is the last one in the list tough.)
            elif exam is None:
                write_log("Outdated Exam detected.")
                calendar.delete_event(previous_exam.event_id)

            # If the last part of the list contains a new exam but not a old one it gets created.
            # (Just if it is the last one in the list tough.)
            elif previous_exam is None:
                write_log("New Exam detected.")
                calendar.create_event(exam.title, exam.start_date)

            # If the titles are the same but the date changed, the date will be updated.
            elif exam.title == previous_exam.title and exam.start_date != previous_exam.start_date:
                write_log("Exam date change detected")
                calendar.update_event(previous_exam.event_id, exam.title, exam.start_date)

            # If the names don't madge each other it is either a new exam or a outdated one.
            elif exam.title != previous_exam.title:
                # If the number of titles in exams is bigger than the one in the previous exams it means that a new
                # exam must be created.
                if self.__title_counter(exams, exam.title) > self.__title_counter(previous_exams, exam.title):
                    write_log("New Exam detected.")
                    calendar.create_event(exam.title, exam.start_date)
                    # A new exam was detected so the previous exams need to be moved back by one so that everything
                    # stays the same
                    previous_exam_shift += 1

                # If the number of titles in exams is smaller than the one in the previous exams it means that an
                # old title was found and it must be deleted.
                elif self.__title_counter(exams, previous_exam.title) < self.__title_counter(previous_exams,
                                                                                             previous_exam.title):
                    write_log("Outdated Exam detected.")
                    calendar.delete_event(previous_exam.event_id)
                    # A old exam was detected so the exams need to be moved back by one so that everything stays the
                    # same
                    exam_shift += 1
            index += 1
Esempio n. 4
0
 def delete_event(self, event_id):
     service = self.__create_sevice()
     calendar_id = self.__get_calendar_id()
     signal.alarm(30)
     service.events().delete(calendarId=calendar_id,
                             eventId=event_id).execute()
     signal.alarm(0)
     write_log("Event deleted.", "Deleted-Event-ID: " + event_id)
Esempio n. 5
0
 def __save_credentials(self, credentials):
     with open(self.__token_path, "wb") as file:
         pickle.dump(credentials, file)
         write_log("authorization token saved in " + self.__token_path)
         print("")
         print(
             "The token has been created. You can now exit the app and run it with:"
         )
         print("sudo nohup python3 main.py &")
         print("Or wait until the rest of the calendar was updated")
         print("Read the log for information on the current process")
         print("")
    def __init__(self):
        try:
            # import Variables from the settings file
            cfg_path = os.path.join(os.path.dirname(os.path.dirname(__file__)),
                                    "SETTINGS.ini")
            cfg = configparser.ConfigParser()
            cfg.read(cfg_path)

            self.url = str(cfg.get("Settings", "url"))
            self.post_url = urljoin(self.url,
                                    "/includes/project/auth/login.php")
            self.end_url = urljoin(self.url, "/service/termine/liste")
            self.calendar_name = str(cfg.get("Settings", "calendar_name"))
            self.email = str(cfg.get("Settings", "email"))
            self.password = str(cfg.get("Settings", "password"))
            self.client_secret_path = str(
                cfg.get("Settings", "client_secret.json_path"))
            self.wait_between_check = int(
                cfg.get("Settings", "wait_between_check"))
            self.wait_between_error = int(
                cfg.get("Settings", "wait_between_error"))

            # Is the email correct
            if "@" not in self.email:
                raise Exception("Email is incorrect.")

            # raises an exception if the path to the client secret file was entered incorrectly
            if os.path.isfile(self.client_secret_path) is False:
                raise Exception("No such file: " + self.client_secret_path)

            # raises an exception if the time between the checks is negative or zero
            if self.wait_between_check <= 0 or self.wait_between_error <= 0:
                raise Exception(
                    "A Negative int or zero was set in the SETTINGS.ini file under the wait_between_... "
                    "section.")

            # raises an exception if the time is over a year = over 31622400 seconds
            if self.wait_between_check > 31622400 or self.wait_between_error > 31622400:
                raise Exception(
                    "The waiting time is over a year. It needs to be under, or 31622400 seconds."
                )

        except Exception as exception:
            print()
            print("An Error Occurred with your Settings:")
            print(exception)
            write_log("An Error Occurred with your Settings", exception,
                      "Application terminated.")
            exit(0)
Esempio n. 7
0
    def __create_new_calendar(self):
        service = self.__create_sevice()
        calendar_body = {
            'summary': self.__calendar_name,
        }
        signal.alarm(30)
        created_calendar = service.calendars().insert(
            body=calendar_body).execute()
        signal.alarm(0)

        calendar_id = created_calendar["id"]
        self.__save_calendar_id(calendar_id)
        write_log("Created new Calendar.", "Calendar-ID: " + calendar_id,
                  "Calendar-ID has been stored in " + self.__calendar_id_path)
        return calendar_id
Esempio n. 8
0
 def __google_authentication(self):
     if self.__is_already_authenticated() is True:
         return self.__load_credentials()
     else:
         try:
             write_log("Authorizing the application.")
             flow = InstalledAppFlow.from_client_secrets_file(
                 self.__client_secret_path, scopes=self.__scopes)
             flow.run_console(access_type='offline')
             self.__save_credentials(flow.credentials)
             return flow.credentials
         except InvalidGrantError:
             write_log("Invalid authorization code.",
                       "Application terminated.")
             exit(0)
Esempio n. 9
0
    def fetch_exams(self):
        write_log("Crawling all exams from Elternportal.")
        soup = self.__fetch_soup()
        exams = []

        for row in soup.find_all("tr"):
            # Because the elternportal site contains "<tr><tr>" I need to ignore them.
            if str(row).startswith("<tr><tr>") or "<h2>" in str(row) or "<h4>" in str(row):
                continue
            title = row.find_all("td")[-1].text.strip()
            date_str = row.find_all("td")[0].text.strip()
            start_date = self.__format_date(date_str)
            exams.append(CrawledExam(title, start_date))

        # Sorts the list after the names first and then after the date.
        exams.sort(key=lambda exam: (exam.title.lower(), exam.start_date))
        return exams
Esempio n. 10
0
    def fetch_events(self):
        write_log("Crawling all exams from the google calendar.")
        calendar_events = []
        page_token = None
        service = self.__create_sevice()
        while True:
            signal.alarm(30)
            events = service.events().list(calendarId=self.__get_calendar_id(),
                                           pageToken=page_token).execute()
            signal.alarm(0)
            for event in events['items']:
                title = event["summary"]
                start_time = self.__turn_into_date_obj(event['start']["date"])
                event_id = event["id"]
                calendar_events.append(CrawledExam(title, start_time,
                                                   event_id))
            page_token = events.get('nextPageToken')
            if not page_token:
                break

        calendar_events.sort(
            key=lambda exam: (exam.title.lower(), exam.start_date))
        return calendar_events
 def do_the_check(self):
     try:
         if self.__check_internet_connection() is False:
             print("There appears to be no internet connection!")
             write_log("There appears to be no internet connection!")
             return False
         if self.__check_response_code() is False:
             print(f"{config.url} could not be reached.")
             write_log(f"{config.url} could not be reached.")
             return False
         if self.__check_elternportal_logindata() is False:
             print("The login data for the elternportal website is wrong.")
             write_log("The login data for the elternportal website is wrong.")
             return False
         return True
     except Exception as e:
         print("An Error Occurred.")
         print(e)
         write_log("An Error Occurred.", e, "Application terminated.")
         exit(0)
Esempio n. 12
0
                    self.__add_exams_to_calendar(exams)
                # Otherwise it checks for updates.
                else:
                    write_log("Checking for updates")
                    self.__check_for_updates(exams, previous_exams)
                write_log(f"Waiting for {config.wait_between_check} seconds before continuing.")
                # wait for the number of seconds specified in the settings before proceeding.
                time.sleep(config.wait_between_check)
            except KeyboardInterrupt:
                write_log("Application terminated.")
                exit(0)
            except Exception as exception:
                try:
                    write_log("An Error Occurred.", exception,
                              f"Waiting for {config.wait_between_error} seconds before continuing.")
                    # After an error occurred the app will wait for the specified number of seconds before proceeding
                    # and trying it again.
                    time.sleep(config.wait_between_error)
                except KeyboardInterrupt:
                    write_log("Application terminated.")
                    exit(0)


if __name__ == "__main__":
    write_log("Application started.")
    # Checks the login data and the internet connection before proceeding.
    if check.do_the_check():
        app = App()
        app.run()
    write_log("Application terminated.")