summaryrefslogtreecommitdiffstats
path: root/papis/commands/init.py
blob: f88b9b6e07100f0505a3fff6b0640dca92df1ec2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
r"""
This command initializes a Papis library interactively.

.. warning::

    The command will modify your configuration file, if it exists. Unfortunately,
    :mod:`configparser` does not preserve whitespace or comments when reading
    and writing a file, so these will be lost.

Examples
^^^^^^^^

- To create a new library at a given directory, just run

    .. code:: sh

        papis init /path/to/my/library

  and answer the questions interactively.

- To create a library in the current directory, just run

    .. code:: sh

        papis init

Command-line Interface
^^^^^^^^^^^^^^^^^^^^^^

.. click:: papis.commands.init:cli
    :prog: papis init
"""

from typing import List, NamedTuple, Optional
import os

import click

import papis.utils
import papis.config
import papis.logging

logger = papis.logging.get_logger(__name__)


class Prompt(NamedTuple):
    #: The name of the configuration option being suggested, e.g. ``opentool``.
    name: str
    #: A help message (in the form of a question) that describes the option.
    message: str


INIT_PROMPTS = [
    Prompt("opentool",
           "Which program should be used to open files?"),
    Prompt("editor",
           "Which program should be used when editing documents?"),
    Prompt("use-git",
           "Should papis automatically commit changes to a git repository?"),
    Prompt("notes-name",
           "What name should document attached note files have?"),
    Prompt("database-backend",
           "What database backend do you want to use?"),
    Prompt("bibtex-unicode",
           "Do you want to allow unicode in an exported BibTeX entries?"),
    Prompt("ref-format",
           "How would you like the reference string for a document be built?"),
    Prompt("multiple-authors-format",
           "What format should newly added document author lists have?"),
    Prompt("citations-file-name",
           "What name should document attached citation files have?"),
]


def _get_known_libraries() -> List[str]:
    # FIXME: this is necessary because incorrectly configured libraries still
    # show up in `papis.config.get_libs()`. We need better handling of missing
    # libraries or ill-configured libraries.
    known_libraries = []
    for libname in papis.config.get_libs():
        lib = papis.config.get_lib_from_name(libname)
        if lib.paths and all(os.path.exists(path) for path in lib.paths):
            known_libraries.append(libname)

    return known_libraries


def _is_git_repository(path: str) -> bool:
    path = os.path.abspath(path)

    while True:
        # check if a '.git' subdirectory exists
        git_path = os.path.join(path, ".git")
        if os.path.exists(git_path):
            return True

        # check if we reached the root
        parent = os.path.dirname(path)
        if parent == path:
            break

        path = parent

    return False


@click.command("init")
@click.help_option("--help", "-h")
@click.argument(
    "dir_path",
    required=False,
    type=click.Path(file_okay=False),
    default=None,
    metavar="<LIBRARY DIRECTORY>",
    nargs=1,
)
def cli(dir_path: Optional[str]) -> None:
    """Initialize a papis library"""

    from papis.tui.utils import confirm, prompt

    config = papis.config.get_configuration()
    general_settings_name = papis.config.get_general_settings_name()
    defaults = papis.config.get_default_settings()[general_settings_name]
    config_file = papis.config.get_config_file()

    if os.path.exists(config_file):
        logger.warning("Config file '%s' already exists!", config_file)
        logger.warning(
            "This command may change some of its contents, e.g. whitespace and "
            "comments are not preserved.")

        if not confirm("Do you want to continue?"):
            return
    else:
        logger.info("Initializing a new config file: '%s'.", config_file)

    logger.info("Setting library location:")
    dir_path = os.getcwd() if dir_path is None else dir_path

    known_libraries = _get_known_libraries()
    library_name = prompt(
        "Name of the library",
        default=os.path.basename(dir_path),
        bottom_toolbar=(
            "Known libraries: '{}'".format("', '".join(known_libraries))
            if known_libraries else "No currently configured libraries")
        )

    if library_name not in config:
        config.add_section(library_name)
    local = config[library_name]
    glob = config[general_settings_name]

    library_path = prompt(
        "Path of the library",
        default=local.get("dir", dir_path),
        bottom_toolbar="Give an existing folder for the library location")

    if not os.path.exists(library_path):
        if confirm(f"Library path '{library_path}' does not exist. Create it?"):
            os.makedirs(library_path)

    if confirm(f"Make '{library_name}' the default library?", yes=not known_libraries):
        glob["default-library"] = library_name

    local["dir"] = library_path

    logger.info("Setting library custom options.")
    if confirm("Want to add some standard settings?", yes=False):
        for setting, help_string in INIT_PROMPTS:
            local[setting] = prompt(
                setting,
                default=str(local.get(setting, defaults.get(setting))),
                bottom_toolbar=help_string)

    if confirm("Do you want to save?"):
        config_folder = papis.config.get_config_folder()
        if not os.path.exists(config_folder):
            os.makedirs(config_folder)

        with open(config_file, "w") as configfile:
            config.write(configfile)

    logger.info("Check out more information about papis!")
    logger.info("   Configuration options: "
                "https://papis.readthedocs.io/en/latest/configuration.html")
    logger.info("   Library structure:     "
                "https://papis.readthedocs.io/en/latest/library_structure.html")
    logger.info("   Ask questions:         "
                "https://github.com/papis/papis/discussions")