Ejemplo n.º 1
0
    def _select_should_download_linked_files(self):
        """
        Asks the user if linked files should be downloaded
        """
        download_linked_files = self.config_helper.get_download_linked_files()

        self.section_seperator()
        Log.info('In Moodle courses the teacher can also link to external' +
                 ' files. This can be audio, video, text or anything else.' +
                 ' In particular, the teacher can link to Youtube videos.')
        Log.debug('To download videos correctly you have to install ffmpeg. ')

        Log.error('These files can increase the download volume considerably.')

        Log.info('If you want to filter the external links by their domain,' +
                 ' you can manually set a whitelist and a blacklist' +
                 ' (https://github.com/C0D3D3V/Moodle-Downloader-2/' +
                 'wiki/Download-(external)-linked-files' +
                 ' for more details).')
        Log.warning(
            'Please note that the size of the external files is determined during the download, so the total size'
            + ' changes during the download.')
        print('')

        download_linked_files = cutie.prompt_yes_or_no(
            Log.special_str(
                'Would you like to download linked files of the courses you have selected?'
            ),
            default_is_yes=download_linked_files,
        )

        self.config_helper.set_property('download_linked_files',
                                        download_linked_files)
Ejemplo n.º 2
0
    def _select_should_download_descriptions(self):
        """
        Asks the user if descriptions should be downloaded
        """
        download_descriptions = self.config_helper.get_download_descriptions()

        self.section_seperator()
        Log.info(
            'In Moodle courses, descriptions can be added to all kinds' +
            ' of resources, such as files, tasks, assignments or simply' +
            ' free text. These descriptions are usually unnecessary to' +
            ' download because you have already read the information or' +
            ' know it from context. However, there are situations where' +
            ' it might be interesting to download these descriptions. The' +
            ' descriptions are created as Markdown files and can be' +
            ' deleted as desired.')
        Log.debug(
            'Creating the description files does not take extra time, but they can be annoying'
            + ' if they only contain unnecessary information.')

        print('')

        download_descriptions = cutie.prompt_yes_or_no(
            Log.special_str(
                'Would you like to download descriptions of the courses you have selected?'
            ),
            default_is_yes=download_descriptions,
        )

        self.config_helper.set_property('download_descriptions',
                                        download_descriptions)
    def _select_sections_to_download(self, sections: [{}],
                                     excluded: [int]) -> [int]:
        """
        Asks the user for the sections that should be downloaded.
        @param sections: All available sections
        @param excluded sections currently excluded
        """

        choices = []
        defaults = []
        for i, section in enumerate(sections):
            section_id = section.get("id")
            choices.append(('%5i\t%s' % (section_id, section.get("name"))))

            if ResultsHandler.should_download_section(section_id, excluded):
                defaults.append(i)

        Log.special('Which of the sections should be downloaded?')
        Log.info(
            '[You can select with the space bar and confirm your selection with the enter key]'
        )
        print('')
        selected_sections = cutie.select_multiple(options=choices,
                                                  ticked_indices=defaults)

        dont_download_section_ids = []
        for i, section in enumerate(sections):
            if i not in selected_sections:
                dont_download_section_ids.append(section.get("id"))

        return dont_download_section_ids
    def _select_should_userid_and_version_be_saved(self, userid, version):
        """
        Asks the user if the userid and version should be saved in the configuration
        """

        print('')
        Log.info('The user id and version number of Moodle are downloaded' +
                 ' at the beginning of each run of the downloader.' +
                 ' Since this data rarely changes, it can be saved in the' +
                 ' configuration.')

        Log.critical(
            f'Your user id is `{userid}` and the moodle version is `{version}`'
        )

        print('')

        save_userid_and_version = cutie.prompt_yes_or_no(
            Log.special_str(
                'Do you want to store the user id and version number of Moodle in the configuration?'
            ),
            default_is_yes=False,
        )

        if save_userid_and_version:
            Log.warning('Remember to delete the version number from the' +
                        ' configuration once Moodle has been updated' +
                        ' and then run the configurator again!')

            self.config_helper.set_property('userid', userid)
            self.config_helper.set_property('version', version)

        self.section_seperator()
Ejemplo n.º 5
0
    def _select_should_load_default_filename_character_map(self):
        """
        Asks the user if the default filename character map should be loaded
        """
        filename_character_map = self.config_helper.get_filename_character_map(
        )

        if os.name != 'nt':
            self.section_seperator()

            Log.info(
                'On Windows many characters are forbidden in filenames and paths, if you want, these characters can be'
                + ' automatically removed from filenames.')

            Log.warning(
                'If you want to view the downloaded files on Windows this is important!'
            )

            print('Current filename character map: {}'.format(
                filename_character_map))
            Log.special(
                'Do you want to load the default filename character map for Windows?'
            )

            choices = [
                'No, leave it as it was.',
                'No, load the default linux filename character map.',
                'Yes, load the default windows filename character map.',
            ]

            print('[Confirm your selection with the Enter key]')
            print('')

            selected_map = cutie.select(options=choices)

            if selected_map == 0:
                return
            elif selected_map == 1:
                self.config_helper.set_default_filename_character_map(False)
            elif selected_map == 2:
                self.config_helper.set_default_filename_character_map(True)
        else:
            if filename_character_map != ConfigHelper.windows_map:

                self.section_seperator()
                Log.warning(
                    'Warning: Your current filename character map does not match the standard Windows'
                    + ' filename character map!')
                print('Current filename character map: {}'.format(
                    filename_character_map))
                load_default_map = cutie.prompt_yes_or_no(
                    Log.special_str(
                        'Do you want to load the default filename character map for Windows?'
                    ),
                    default_is_yes=False,
                )
                if load_default_map:
                    self.config_helper.set_default_filename_character_map(True)
Ejemplo n.º 6
0
def _max_path_length_workaround(path):
    # Working around MAX_PATH limitation on Windows (see
    # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx)
    if os.name == 'nt':
        absfilepath = os.path.abspath(path)
        path = '\\\\?\\' + absfilepath
        Log.debug("Using absolute paths")
    else:
        Log.info("You are not on Windows, you don't need to use this workaround")
    return path
    def _select_should_download_forums(self):
        """
        Asks the user if forums should be downloaded
        """
        download_forums = self.config_helper.get_download_forums()

        self.section_seperator()
        Log.info(
            'In forums, students and teachers can discuss and exchange information together.'
        )
        print('')

        download_forums = cutie.prompt_yes_or_no(
            Log.special_str('Do you want to download forums of your courses?'),
            default_is_yes=download_forums)

        self.config_helper.set_property('download_forums', download_forums)
Ejemplo n.º 8
0
def run_init(storage_path, use_sso=False, skip_cert_verify=False):
    config = ConfigHelper(storage_path)

    if config.is_present():
        do_override_input = cutie.prompt_yes_or_no(Log.error_str('Do you want to override the existing config?'))

        if not do_override_input:
            sys.exit(0)

    MailService(config).interactively_configure()
    TelegramService(config).interactively_configure()
    XmppService(config).interactively_configure()

    do_sentry = cutie.prompt_yes_or_no('Do you want to configure Error Reporting via Sentry?')
    if do_sentry:
        sentry_dsn = input('Please enter your Sentry DSN:   ')
        config.set_property('sentry_dsn', sentry_dsn)

    moodle = MoodleService(config, storage_path, skip_cert_verify)

    if use_sso:
        moodle.interactively_acquire_sso_token()
    else:
        moodle.interactively_acquire_token()

    Log.success('Configuration finished and saved!')

    if os.name != 'nt':
        working_dir = os.path.abspath(storage_path)
        moodle_dl_path = os.path.abspath(sys.argv[0])
        Log.info(
            '  To set a cron-job for this program on your Unix-System:\n'
            + '    1. `crontab -e`\n'
            + '    2. Add `*/15 * * * * cd "{}" && "{}" >/dev/null 2>&1`\n'.format(working_dir, moodle_dl_path)
            + '    3. Save and you\'re done!'
        )

        Log.info(
            'For more ways to run `moodle-dl` periodically, take a look at the wiki (https://github.com/C0D3D3V/Moodle-Downloader-2/wiki/Start-Moodle-dl-periodically-or-via-Telegram)'
        )
    else:
        Log.info(
            'If you want to run moodle-dl periodically, you can take a look at the wiki (https://github.com/C0D3D3V/Moodle-Downloader-2/wiki/Start-Moodle-dl-periodically-or-via-Telegram)'
        )

    print('')

    Log.info('You can always do the additional configuration later with the --config option.')

    do_config = cutie.prompt_yes_or_no('Do you want to make additional configurations now?')

    if do_config:
        run_configure(storage_path, skip_cert_verify)

    print('')
    Log.success('All set and ready to go!')
    def _select_courses_to_download(self, courses: [Course]):
        """
        Asks the user for the courses that should be downloaded.
        @param courses: All available courses
        """
        download_course_ids = self.config_helper.get_download_course_ids()
        dont_download_course_ids = self.config_helper.get_dont_download_course_ids(
        )

        print('')
        Log.info(
            'To avoid downloading all the Moodle courses you are enrolled in, you can select which ones you want'
            + ' to download here. ')
        print('')

        choices = []
        defaults = []
        for i, course in enumerate(courses):
            choices.append(('%5i\t%s' % (course.id, course.fullname)))

            if ResultsHandler.should_download_course(course.id,
                                                     download_course_ids,
                                                     dont_download_course_ids):
                defaults.append(i)

        Log.special('Which of the courses should be downloaded?')
        Log.info(
            '[You can select with the space bar and confirm your selection with the enter key]'
        )
        print('')
        selected_courses = cutie.select_multiple(options=choices,
                                                 ticked_indices=defaults)

        download_course_ids = []
        for i, course in enumerate(courses):
            if i in selected_courses:
                download_course_ids.append(course.id)

        self.config_helper.set_property('download_course_ids',
                                        download_course_ids)

        self.config_helper.remove_property('dont_download_course_ids')
        return download_course_ids
    def _select_should_download_quizzes(self):
        """
        Asks the user if quizzes should be downloaded
        """
        download_quizzes = self.config_helper.get_download_quizzes()

        self.section_seperator()
        Log.info(
            'Quizzes are tests that a student must complete in a course and are graded on.'
            +
            ' Only quizzes that are in progress or have been completed will be downloaded.'
        )
        print('')

        download_quizzes = cutie.prompt_yes_or_no(
            Log.special_str(
                'Do you want to download quizzes of your courses?'),
            default_is_yes=download_quizzes)

        self.config_helper.set_property('download_quizzes', download_quizzes)
    def _select_should_download_workshops(self):
        """
        Asks the user if workshops should be downloaded
        """
        download_workshops = self.config_helper.get_download_workshops()

        self.section_seperator()
        Log.info(
            'Workshops function according to the peer review process.' +
            ' Students can make submissions and have to assess submissions of other students. '
        )
        print('')

        download_workshops = cutie.prompt_yes_or_no(
            Log.special_str(
                'Do you want to download workshops of your courses?'),
            default_is_yes=download_workshops)

        self.config_helper.set_property('download_workshops',
                                        download_workshops)
    def _select_should_download_lessons(self):
        """
        Asks the user if lessons should be downloaded
        """
        download_lessons = self.config_helper.get_download_lessons()

        self.section_seperator()
        Log.info(
            'Lessons are a kind of self-teaching with pages of information and other pages with questions to answer.'
            +
            ' A student can be graded on their answers after completing a lesson. Currently, only lessons without'
            +
            ' the answers are downloaded. The answers are potentially also available for download,'
            + ' but this has not been implemented.')
        print('')

        download_lessons = cutie.prompt_yes_or_no(
            Log.special_str(
                'Do you want to download lessons of your courses?'),
            default_is_yes=download_lessons)

        self.config_helper.set_property('download_lessons', download_lessons)
Ejemplo n.º 13
0
def run_init(storage_path, use_sso=False, skip_cert_verify=False):
    config = ConfigHelper(storage_path)

    if config.is_present():
        do_override_input = cutie.prompt_yes_or_no(
            Log.error_str('Do you want to override the existing config?'))

        if not do_override_input:
            sys.exit(0)

    MailService(config).interactively_configure()
    TelegramService(config).interactively_configure()
    XmppService(config).interactively_configure()

    do_sentry = cutie.prompt_yes_or_no(
        'Do you want to configure Error Reporting via Sentry?')
    if do_sentry:
        sentry_dsn = input('Please enter your Sentry DSN:   ')
        config.set_property('sentry_dsn', sentry_dsn)

    moodle = MoodleService(config, storage_path, skip_cert_verify)

    if use_sso:
        moodle.interactively_acquire_sso_token()
    else:
        moodle.interactively_acquire_token()

    Log.success('Configuration finished and saved!')

    if os.name != 'nt':
        if storage_path == '.':
            Log.info(
                '  To set a cron-job for this program on your Unix-System:\n' +
                '    1. `crontab -e`\n' +
                '    2. Add `*/15 * * * * cd "{}" && moodle-dl`\n'.format(
                    os.getcwd()) + '    3. Save and you\'re done!')
        else:
            Log.info(
                '  To set a cron-job for this program on your Unix-System:\n' +
                '    1. `crontab -e`\n' +
                '    2. Add `*/15 * * * * cd "{}" && moodle-dl -p "{}"`\n'.
                format(os.getcwd(), storage_path) +
                '    3. Save and you\'re done!')

    print('')

    Log.info(
        'You can always do the additional configuration later with the --config option.'
    )

    do_config = cutie.prompt_yes_or_no(
        'Do you want to make additional configurations now?')

    if do_config:
        run_configure(storage_path, skip_cert_verify)

    print('')
    Log.success('All set and ready to go!')
Ejemplo n.º 14
0
    def _select_should_download_links_in_descriptions(self):
        """
        Asks the user if links in descriptions should be downloaded
        """
        download_links_in_descriptions = self.config_helper.get_download_links_in_descriptions(
        )

        self.section_seperator()
        Log.info(
            'In the descriptions of files, sections, assignments or courses the teacher can add links to webpages,'
            +
            ' files or videos. That links can pont to a internal page on moodle or to an external webpage.'
        )
        print('')

        download_links_in_descriptions = cutie.prompt_yes_or_no(
            Log.special_str(
                'Would you like to download links in descriptions?'),
            default_is_yes=download_links_in_descriptions,
        )

        self.config_helper.set_property('download_links_in_descriptions',
                                        download_links_in_descriptions)
Ejemplo n.º 15
0
    def _select_should_download_databases(self):
        """
        Asks the user if databases should be downloaded
        """
        download_databases = self.config_helper.get_download_databases()

        self.section_seperator()
        Log.info('In the database module of Moodle data can be stored' +
                 ' structured with information. Often it is also' +
                 ' possible for students to upload data there.  Because' +
                 ' the implementation of the downloader has not yet been' +
                 ' optimized at this point, it is optional to download the' +
                 ' databases. Currently only files are downloaded, thumbails' +
                 ' are ignored.')
        print('')

        download_databases = cutie.prompt_yes_or_no(
            Log.special_str(
                'Do you want to download databases of your courses?'),
            default_is_yes=download_databases)

        self.config_helper.set_property('download_databases',
                                        download_databases)
Ejemplo n.º 16
0
    def _select_should_download_submissions(self):
        """
        Asks the user if submissions should be downloaded
        """
        download_submissions = self.config_helper.get_download_submissions()

        self.section_seperator()
        Log.info('Submissions are files that you or a teacher have uploaded' +
                 ' to your assignments. Moodle does not provide an' +
                 ' interface for downloading information from all' +
                 ' submissions to a course at once.')
        Log.warning(
            'Therefore, it may be slow to monitor changes to submissions.')
        print('')

        download_submissions = cutie.prompt_yes_or_no(
            Log.special_str(
                'Do you want to download submissions of your assignments?'),
            default_is_yes=download_submissions,
        )

        self.config_helper.set_property('download_submissions',
                                        download_submissions)
    def _select_should_download_also_with_cookie(self):
        """
        Ask the user whether files for which a cookie is required should be downloaded.
        """
        download_also_with_cookie = self.config_helper.get_download_also_with_cookie(
        )

        self.section_seperator()
        Log.info(
            'Descriptions may contain links to files that require a browser cookie so they can be downloaded.'
            +
            ' There are also several Moodle plugins that cannot be displayed in the Moodle app,'
            + ' so you need a browser cookie to download these plugin files.')

        Log.debug(
            'The Moodle browser cookie is created using your private token and stored in the `Configs.txt` file.'
            +
            ' As long as this option is activated, the file always contains a valid cookie.'
        )

        if self.config_helper.get_privatetoken() is None:
            Log.error(
                'Currently no private token is stored in the configuration.' +
                ' Create a private token with moodle-dl --new-token (if necessary with --sso)'
            )

        print('')

        download_also_with_cookie = cutie.prompt_yes_or_no(
            Log.special_str(
                'Would you like to download files for which a cookie is required?'
            ),
            default_is_yes=download_also_with_cookie,
        )

        self.config_helper.set_property('download_also_with_cookie',
                                        download_also_with_cookie)
Ejemplo n.º 18
0
def run_main(
    storage_path,
    verbose=False,
    skip_cert_verify=False,
    ignore_ytdl_errors=False,
    without_downloading_files=False,
    log_responses=False,
):

    log_formatter = logging.Formatter(
        '%(asctime)s  %(levelname)s  {%(module)s}  %(message)s',
        '%Y-%m-%d %H:%M:%S')
    log_file = os.path.join(storage_path, 'MoodleDownloader.log')
    log_handler = RotatingFileHandler(log_file,
                                      mode='a',
                                      maxBytes=1 * 1024 * 1024,
                                      backupCount=2,
                                      encoding='utf-8',
                                      delay=0)

    log_handler.setFormatter(log_formatter)
    if verbose:
        log_handler.setLevel(logging.DEBUG)
    else:
        log_handler.setLevel(logging.INFO)

    app_log = logging.getLogger()
    if verbose:
        app_log.setLevel(logging.DEBUG)
    else:
        app_log.setLevel(logging.INFO)
    app_log.addHandler(log_handler)

    logging.info('--- moodle-dl started ---------------------')
    Log.info('Moodle Downloader starting...')
    if verbose:
        logging.debug('moodle-dl version: %s', __version__)
        logging.debug('python version: %s',
                      ".".join(map(str, sys.version_info[:3])))
        ffmpeg_available = which('ffmpeg') is not None
        logging.debug('Is ffmpeg available: %s', ffmpeg_available)

    if IS_DEBUG:
        logging.info('Debug-Mode detected. Errors will be re-risen.')
        app_log.addHandler(ReRaiseOnError())

    try:
        msg_load_config = 'Loading config...'
        logging.debug(msg_load_config)
        Log.debug(msg_load_config)

        config = ConfigHelper(storage_path)
        config.load()
    except BaseException as e:
        logging.error(
            'Error while trying to load the Configuration! %s Exiting...',
            e,
            extra={'exception': e})
        Log.error('Error while trying to load the Configuration!')
        sys.exit(1)

    r_client = False
    try:
        sentry_dsn = config.get_property('sentry_dsn')
        if sentry_dsn:
            sentry_sdk.init(sentry_dsn)
    except BaseException:
        pass

    mail_service = MailService(config)
    tg_service = TelegramService(config)
    xmpp_service = XmppService(config)
    console_service = ConsoleService(config)

    PathTools.restricted_filenames = config.get_restricted_filenames()

    try:
        if not IS_DEBUG:
            process_lock.lock(storage_path)

        moodle = MoodleService(config, storage_path, skip_cert_verify,
                               log_responses)

        msg_checking_for_changes = 'Checking for changes for the configured Moodle-Account....'
        logging.debug(msg_checking_for_changes)
        Log.debug(msg_checking_for_changes)
        changed_courses = moodle.fetch_state()

        if log_responses:
            msg_responses_logged = (
                "All JSON-responses from Moodle have been written to the responses.log file. Exiting..."
            )
            logging.debug(msg_responses_logged)
            Log.success(msg_responses_logged)
            process_lock.unlock(storage_path)
            return

        msg_start_downloading = 'Start downloading changed files...'
        logging.debug(msg_start_downloading)
        Log.debug(msg_start_downloading)

        if without_downloading_files:
            downloader = FakeDownloadService(changed_courses, moodle,
                                             storage_path)
        else:
            downloader = DownloadService(changed_courses, moodle, storage_path,
                                         skip_cert_verify, ignore_ytdl_errors)
        downloader.run()
        failed_downloads = downloader.get_failed_url_targets()

        changed_courses_to_notify = moodle.recorder.changes_to_notify()

        if len(changed_courses_to_notify) > 0:
            console_service.notify_about_changes_in_moodle(
                changed_courses_to_notify)
            mail_service.notify_about_changes_in_moodle(
                changed_courses_to_notify)
            tg_service.notify_about_changes_in_moodle(
                changed_courses_to_notify)
            xmpp_service.notify_about_changes_in_moodle(
                changed_courses_to_notify)

            moodle.recorder.notified(changed_courses_to_notify)

        else:
            msg_no_changes = 'No changes found for the configured Moodle-Account.'
            logging.info(msg_no_changes)
            Log.warning(msg_no_changes)

        if len(failed_downloads) > 0:
            console_service.notify_about_failed_downloads(failed_downloads)
            mail_service.notify_about_failed_downloads(failed_downloads)
            tg_service.notify_about_failed_downloads(failed_downloads)
            xmpp_service.notify_about_failed_downloads(failed_downloads)

        process_lock.unlock(storage_path)

        logging.debug('All done. Exiting...')
        Log.success('All done. Exiting..')
    except BaseException as e:
        print('\n')
        if not isinstance(e, process_lock.LockError):
            process_lock.unlock(storage_path)

        error_formatted = traceback.format_exc()
        logging.error(error_formatted, extra={'exception': e})

        if r_client:
            sentry_sdk.capture_exception(e)

        if verbose:
            Log.critical('Exception:\n%s' % (error_formatted))

        short_error = str(e)
        if not short_error or short_error.isspace():
            short_error = traceback.format_exc(limit=1)

        console_service.notify_about_error(short_error)
        mail_service.notify_about_error(short_error)
        tg_service.notify_about_error(short_error)
        xmpp_service.notify_about_error(short_error)

        logging.debug('Exception-Handling completed. Exiting...')

        sys.exit(1)
Ejemplo n.º 19
0
    def _set_options_of_courses(self, courses: [Course]):
        """
        Let the user set special options for every single course
        """
        download_course_ids = self.config_helper.get_download_course_ids()
        dont_download_course_ids = self.config_helper.get_dont_download_course_ids(
        )

        self.section_seperator()
        Log.info(
            'You can set special settings for every single course.\n' +
            'You can set these options:\n' +
            ' - A different name for the course\n' +
            ' - If a directory structure should be created for the course' +
            ' [create_directory_structure (cfs)].')
        print('')

        while True:

            choices = []
            choices_courses = []

            options_of_courses = self.config_helper.get_options_of_courses()

            choices.append('None')

            for course in courses:
                if ResultsHandler.should_download_course(
                        course.id, download_course_ids,
                        dont_download_course_ids):

                    current_course_settings = options_of_courses.get(
                        str(course.id), None)

                    # create default settings
                    if current_course_settings is None:
                        current_course_settings = {
                            'original_name': course.fullname,
                            'overwrite_name_with': None,
                            'create_directory_structure': True,
                        }

                    # create list of options
                    overwrite_name_with = current_course_settings.get(
                        'overwrite_name_with', None)

                    create_directory_structure = current_course_settings.get(
                        'create_directory_structure', True)

                    if overwrite_name_with is not None and overwrite_name_with != course.fullname:
                        choices.append(
                            ('%5i\t%s (%s) cfs=%s' %
                             (course.id, overwrite_name_with, course.fullname,
                              create_directory_structure)))

                    else:
                        choices.append(
                            ('%5i\t%s  cfs=%s' % (course.id, course.fullname,
                                                  create_directory_structure)))

                    choices_courses.append(course)

            print('')
            Log.special(
                'For which of the following course do you want to change the settings?'
            )
            print('[Confirm your selection with the Enter key]')
            print('')

            selected_course = cutie.select(options=choices)
            if selected_course == 0:
                break
            else:
                self._change_settings_of(choices_courses[selected_course - 1],
                                         options_of_courses)
Ejemplo n.º 20
0
def run_init(storage_path, use_sso=False, skip_cert_verify=False):
    config = ConfigHelper(storage_path)

    if config.is_present():
        do_override_input = cutie.prompt_yes_or_no(Log.error_str('Do you want to override the existing config?'))

        if not do_override_input:
            sys.exit(0)

    MailService(config).interactively_configure()
    TelegramService(config).interactively_configure()

    do_sentry = cutie.prompt_yes_or_no('Do you want to configure Error Reporting via Sentry?')
    if do_sentry:
        sentry_dsn = input('Please enter your Sentry DSN:   ')
        config.set_property('sentry_dsn', sentry_dsn)

    moodle = MoodleService(config, storage_path, skip_cert_verify)

    if use_sso:
        moodle.interactively_acquire_sso_token()
    else:
        moodle.interactively_acquire_token()

    if os.name != 'nt':
        Log.info(
            'On Windows many characters are forbidden in filenames and paths, if you want, these characters can be'
            + ' automatically removed from filenames.'
        )

        Log.warning('If you want to view the downloaded files on Windows this is important!')

        default_windows_map = cutie.prompt_yes_or_no(
            'Do you want to load the default filename character map for windows?'
        )
        if default_windows_map:
            config.set_default_filename_character_map(True)
        else:
            config.set_default_filename_character_map(False)
    else:
        config.set_default_filename_character_map(True)

    Log.success('Configuration finished and saved!')

    if os.name != 'nt':
        if storage_path == '.':
            Log.info(
                '  To set a cron-job for this program on your Unix-System:\n'
                + '    1. `crontab -e`\n'
                + '    2. Add `*/15 * * * * cd %s && python3 %smain.py`\n'
                % (os.getcwd(), os.path.join(os.path.dirname(os.path.realpath(__file__)), ''))
                + '    3. Save and you\'re done!'
            )
        else:
            Log.info(
                '  To set a cron-job for this program on your Unix-System:\n'
                + '    1. `crontab -e`\n'
                + '    2. Add `*/15 * * * *'
                + ' cd %s && python3 %smain.py --path %s`\n'
                % (os.getcwd(), os.path.join(os.path.dirname(os.path.realpath(__file__)), ''), storage_path)
                + '    3. Save and you\'re done!'
            )

    print('')

    Log.info('You can always do the additional configuration later with the --config option.')

    do_config = cutie.prompt_yes_or_no('Do you want to make additional configurations now?')

    if do_config:
        run_configure(storage_path, skip_cert_verify)

    print('')
    Log.success('All set and ready to go!')
    def interactively_add_all_visible_courses(self):
        """
        Guides the user through the process of adding all visible courses
        to the list of courses to download in the configuration
        """

        token = self.config_helper.get_token()
        moodle_domain = self.config_helper.get_moodle_domain()
        moodle_path = self.config_helper.get_moodle_path()
        use_http = self.config_helper.get_use_http()

        request_helper = RequestHelper(moodle_domain,
                                       moodle_path,
                                       token,
                                       self.skip_cert_verify,
                                       use_http=use_http)
        first_contact_handler = FirstContactHandler(request_helper)

        print('')
        Log.info(
            'It is possible to automatically complete the moodle-dl configuration'
            +
            ' with all the courses you can see on your moodle. These are either'
            + ' courses to which you have the appropriate rights to see the' +
            ' course or the course is visible without enrollment.')

        Log.critical(
            'This process can take several minutes for large Moodels, as is common at'
            + ' large universities. Timeout is set to 20 minutes.')

        print('')

        add_all_visible_courses = cutie.prompt_yes_or_no(
            Log.special_str(
                'Do you want to add all visible courses of your Moodle to the configuration?'
            ),
            default_is_yes=False,
        )

        if not add_all_visible_courses:
            return
        else:
            Log.warning(
                'Please wait for the result, this may take several minutes.' +
                ' In addition to adding the courses to the configuration,' +
                ' it will also create an `all_courses.json` file with all' +
                ' the courses available on your Moodle.')

        courses = []
        all_visible_courses = []
        try:
            userid, version = self.config_helper.get_userid_and_version()
            if userid is None or version is None:
                userid, version = first_contact_handler.fetch_userid_and_version(
                )
            else:
                first_contact_handler.version = version

            courses = first_contact_handler.fetch_courses(userid)
            log_all_courses_to = str(
                Path(self.storage_path) / 'all_courses.json')
            all_visible_courses = first_contact_handler.fetch_all_visible_courses(
                log_all_courses_to)

        except (RequestRejectedError, ValueError, RuntimeError,
                ConnectionError) as error:
            Log.error(
                'Error while communicating with the Moodle System! (%s)' %
                (error))
            sys.exit(1)

        # Filter out courses the user is enroled in
        filtered_all_courses = []
        for visible_course in all_visible_courses:
            add_to_final_list = True
            for course in courses:
                if visible_course.id == course.id:
                    add_to_final_list = False
                    break
            if add_to_final_list:
                filtered_all_courses.append(visible_course)

        # Update Public Courses IDs
        download_public_course_ids = self.config_helper.get_download_public_course_ids(
        )
        # Update Course settings List for all new Courses
        options_of_courses = self.config_helper.get_options_of_courses()
        for course in filtered_all_courses:
            current_course_settings = options_of_courses.get(
                str(course.id), None)

            # create default settings
            if current_course_settings is None:
                current_course_settings = {
                    'original_name': course.fullname,
                    'overwrite_name_with': None,
                    'create_directory_structure': True,
                }

                options_of_courses.update(
                    {str(course.id): current_course_settings})

            if course.id not in download_public_course_ids:
                download_public_course_ids.append(course.id)

        self.config_helper.set_property('options_of_courses',
                                        options_of_courses)
        self.config_helper.set_property('download_public_course_ids',
                                        download_public_course_ids)