예제 #1
0
    def run(self):
        self.logger.debug('Meta command generate started')
        filename = self.options['filename']
        result = None
        with spinner(f'Generating metadata', self.logger, self.quiet,
                     self.debug):
            self._gen_meta()
            with open(filename, 'w', encoding='utf8') as f:
                yaml.dump(self.meta.dump(),
                          f,
                          default_flow_style=False,
                          allow_unicode=True,
                          sort_keys=False)
            result = filename

        if result:
            self.logger.info(f'Result: {result}')

            if not self.quiet:
                print('─' * 20)
                print(f'Result: {result}')
            else:
                print(result)

        self.logger.debug('Meta command generate finished')
예제 #2
0
    def apply_preprocessor(self, preprocessor: str or dict):
        '''Apply preprocessor.

        :param preprocessor: Preprocessor name or a dict of the preprocessor name and its options
        '''

        if isinstance(preprocessor, str):
            preprocessor_name, preprocessor_options = preprocessor, {}
        elif isinstance(preprocessor, dict):
            (preprocessor_name,
             preprocessor_options), = (*preprocessor.items(), )

        with spinner(f'Applying preprocessor {preprocessor_name}', self.logger,
                     self.quiet, self.debug):
            try:
                preprocessor_module = import_module(
                    f'foliant.preprocessors.{preprocessor_name}')
                preprocessor_module.Preprocessor(self.context, self.logger,
                                                 self.quiet, self.debug,
                                                 preprocessor_options).apply()

            except ModuleNotFoundError as module_not_found:
                raise ModuleNotFoundError(
                    f'Preprocessor {preprocessor_name} is not installed'
                ) from module_not_found

            except Exception as exception:
                raise RuntimeError(
                    f'Failed to apply preprocessor {preprocessor_name}: {exception}'
                ) from exception
예제 #3
0
    def get_config(self,
                   project_path: Path,
                   config_file_name: str,
                   quiet=False,
                   debug=False) -> dict:
        with spinner('Parsing config', self.logger, quiet, debug):
            try:

                config = Parser(project_path, config_file_name, self.logger,
                                quiet).parse()

            except FileNotFoundError as exception:
                config = None
                raise FileNotFoundError(
                    f'{exception} not found') from exception

            except Exception as exception:
                config = None
                raise RuntimeError(
                    f'Invalid config: {exception}') from exception

        if config is None:
            raise ConfigError('Config parsing failed.')

        return config
예제 #4
0
 def make(self, target: str) -> str:
     self.meta = load_meta(self.config.get('chapters', []), self.working_dir)
     with spinner(f'Making {target} with Pandoc', self.logger, self.quiet, self.debug):
         result = []
         if self._pandoc_config['build_whole_project']:
             result.append(self._build_flat(target))
         result.extend(self._build_separate(target))
         return '\n' + '\n'.join(result)
    def make(self, target: str) -> str:
        with spinner(f'Making {target}', self.logger, self.quiet, self.debug):
            output('', self.quiet)  # empty line for better output
            try:
                if target == 'confluence':
                    return self._build()
                else:
                    raise ValueError(f'Confluence cannot make {target}')

            except Exception as exception:
                raise RuntimeError(f'Build failed: {exception}')
예제 #6
0
    def gupload(self,
                target,
                backend='',
                project_path=Path('.'),
                config_file_name='foliant.yml',
                quiet=False,
                keep_tmp=False,
                debug=False):

        file_to_upload = make.Cli()
        self._filename = file_to_upload.make(target, backend, project_path,
                                             config_file_name, quiet, keep_tmp,
                                             debug)

        print('─────────────────────')

        self._gdoc_config = file_to_upload.get_config(project_path,
                                                      config_file_name,
                                                      quiet=True)['gupload']

        self._gdrive_auth()

        with spinner(f"Uploading '{self._filename}' to Google Drive",
                     self.logger,
                     quiet=False,
                     debug=False):
            try:
                self._create_gdrive_folder()
                self._upload_file(target)

            except Exception as exception:
                raise type(exception)(f'The error occurs: {exception}')

        if self._gdoc_link:
            self.logger.info(
                f'File {self._filename} uploaded to Google Drive: {self._gdoc_link}'
            )

            if not quiet:
                print('─────────────────────')
                print(f"Result:\n\
Doc link: {self._gdoc_link}\n\
Google drive folder ID: {self._gdoc_config['gdrive_folder_id']}\n\
Google document ID: {self._gdoc_config['gdoc_id']}")

            return self._gdoc_link

        else:
            self.logger.critical('Upload failed')
            exit('Upload failed')
            return None
예제 #7
0
    def make(self, target: str) -> str:
        with spinner(f'Making {target} with md-to-pdf', self.logger,
                     self.quiet, self.debug):
            try:
                command = self._get_pdf_command()
                self.logger.debug('Running the command.')

                run(command,
                    shell=True,
                    check=True,
                    stdout=PIPE,
                    stderr=STDOUT)

                return f'{self._slug}.{target}'

            except CalledProcessError as exception:
                raise RuntimeError(
                    f'Build failed: {exception.output.decode()}')

            except Exception as exception:
                raise type(exception)(f'Build failed: {exception}')
예제 #8
0
    def get_config(
            self,
            project_path: Path,
            config_file_name: str,
            quiet=False,
            debug=False
    ) -> dict:
        with spinner('Parsing config', self.logger, quiet, debug):
            try:
                config = Parser(project_path, config_file_name, self.logger).parse()

            except FileNotFoundError as exception:
                config = None
                raise FileNotFoundError(f'{exception} not found')

            except Exception as exception:
                config = None
                raise type(exception)(f'Invalid config: {exception}')

        if config is None:
            raise ConfigError('Config parsing failed.')

        return config
예제 #9
0
    def make(self, target: str) -> str:
        with spinner(f'Making {target}', self.logger, self.quiet, self.debug):
            try:
                img_dir = self._site_dir / 'img'
                shutil.rmtree(self._site_dir, ignore_errors=True)
                img_dir.mkdir(parents=True)
                source_path = self.working_dir / self._flat_src_file_name
                with open(source_path) as f:
                    source = f.read()

                processed_source = self._process_images(source, img_dir)
                with open(source_path, 'w') as f:
                    f.write(processed_source)

                try:
                    command = self._get_command(self._aglio_config,
                                                source_path,
                                                self._site_dir / "index.html")
                    self.logger.debug(f'Constructed command: {command}')

                    r = run(command,
                            shell=True,
                            check=True,
                            stdout=PIPE,
                            stderr=STDOUT)
                except CalledProcessError as e:
                    raise RuntimeError(e.output.decode('utf8',
                                                       errors='ignore'))
                command_output_decoded = r.stdout.decode('utf8',
                                                         errors='ignore')
                output(command_output_decoded, self.quiet)
                return self._site_dir

            except Exception as exception:
                err = traceback.format_exc()
                self.logger.debug(err)
                raise type(exception)(f'Build failed: {err}')
예제 #10
0
    def make(self, target: str) -> str:
        with spinner(f'Making {target} with MkDocs', self.logger, self.quiet):
            try:
                mkdocs_project_path = self.working_dir / self._mkdocs_project_dir_name

                config = self._mkdocs_config.get('mkdocs.yml', {})

                self.logger.debug(f'Backend config: {config}')

                if 'site_name' not in config and self._mkdocs_config.get(
                        'use_title', True):
                    config['site_name'] = self.config['title']

                if 'pages' not in config and self._mkdocs_config.get(
                        'use_chapters', True):
                    config['pages'] = self.config['chapters']

                if self._mkdocs_config.get('use_headings', True):
                    config['pages'] = self._get_pages_with_headings(
                        config['pages'])

                self.logger.debug(f'mkdocs.yml: {config}')

                with open(mkdocs_project_path / 'mkdocs.yml',
                          'w',
                          encoding='utf8') as mkdocs_config:
                    self.logger.debug(
                        f'Saving mkdocs.yml into {mkdocs_project_path}')
                    dump(config, mkdocs_config, default_flow_style=False)

                if target == 'mkdocs':
                    rmtree(self._mkdocs_project_dir_name, ignore_errors=True)
                    copytree(mkdocs_project_path,
                             self._mkdocs_project_dir_name)

                    return self._mkdocs_project_dir_name

                elif target == 'site':
                    try:
                        mkdocs_site_path = Path(
                            self._mkdocs_site_dir_name).absolute()
                        run(self._get_build_command(mkdocs_site_path),
                            shell=True,
                            check=True,
                            stdout=PIPE,
                            stderr=STDOUT,
                            cwd=mkdocs_project_path)

                        return self._mkdocs_site_dir_name

                    except CalledProcessError as exception:
                        raise RuntimeError(
                            f'Build failed: {exception.output.decode()}')

                elif target == 'ghp':
                    try:
                        mkdocs_site_path = Path(
                            self._mkdocs_site_dir_name).absolute()
                        process = run(self._get_ghp_command(),
                                      shell=True,
                                      check=True,
                                      stdout=PIPE,
                                      stderr=STDOUT,
                                      cwd=mkdocs_project_path)
                        ghp_url = process.stdout.decode().splitlines(
                        )[-1].split(': ')[-1]

                        return ghp_url

                    except CalledProcessError as exception:
                        raise RuntimeError(
                            f'GitHub Pages deploy failed: {exception.output.decode()}'
                        )

                else:
                    raise ValueError(f'MkDocs cannot make {target}')

            except Exception as exception:
                raise type(exception)(f'Build failed: {exception}')
예제 #11
0
    def init(self, project_name='', template='base', quiet=False, debug=False):
        '''Generate new Foliant project.'''

        self.logger.setLevel(DEBUG if debug else WARNING)

        self.logger.info('Project creation started.')

        self.logger.debug(f'Template: {template}')

        template_path = Path(template)

        if not template_path.exists():
            self.logger.debug(
                f'Template not found in {template_path}, looking in installed templates.'
            )

            installed_templates_path = Path(
                Path(__file__).parent / 'templates')

            installed_templates = [
                item.name for item in installed_templates_path.iterdir()
                if item.is_dir()
            ]

            self.logger.debug(f'Available templates: {installed_templates}')

            if template in installed_templates:
                self.logger.debug('Template found.')

            else:
                self.logger.debug('Template not found, asking for user input.')

                try:
                    template = prompt(
                        f'Please pick a template from {installed_templates}: ',
                        completer=WordCompleter(installed_templates),
                        validator=BuiltinTemplateValidator(
                            installed_templates))

                except KeyboardInterrupt:
                    self.logger.warning('Project creation interrupted.')
                    return

            template_path = installed_templates_path / template

            self.logger.debug(f'Template path: {template_path}')

        if not project_name:
            self.logger.debug(
                'Project name not specified, asking for user input.')

            try:
                project_name = prompt('Enter the project name: ')

            except KeyboardInterrupt:
                self.logger.warning('Project creation interrupted.')
                return

        project_slug = slugify(project_name)
        project_path = Path(project_slug)

        properties = {'title': project_name, 'slug': project_slug}

        self.logger.debug(f'Project properties: {properties}')

        result = None

        with spinner('Generating project', self.logger, quiet, debug):
            copytree(template_path, project_path)

            text_types = '*.md', '*.yml', '*.txt', '*.py'

            text_file_paths = reduce(lambda acc, matches: acc + [*matches],
                                     (project_path.rglob(text_type)
                                      for text_type in text_types), [])

            for text_file_path in text_file_paths:
                self.logger.debug(f'Processing content of {text_file_path}')
                replace_placeholders(text_file_path, properties)

            for item in project_path.rglob('*'):
                self.logger.debug(f'Processing name of {item}')
                item.rename(
                    Template(item.as_posix()).safe_substitute(properties))

            result = project_path

        if result:
            self.logger.info(f'Result: {result}')

            if not quiet:
                print('─' * 20)
                print(f'Project "{project_name}" created in {result}')
            else:
                print(result)

        else:
            self.logger.critical('Project creation failed.')
            exit(1)