Skip to content

config

>>> from ncal.config import load_settings
>>> configuration = load_settings(
...     notion_api_token="secret_123456",
...     database_id="1234567",
...     url_root="https://www.notion.so/exampleuser/afeeec3434177a5aefde223455?v=1462bb2343466546ae54e&p=",
... )
>>> type(configuration)
<class 'ncal.config.Settings'>
>>> configuration.default_calendar_name
'Notion Events'

Functions and classes to manage configuration are in this file.

Configuration can operate through toml, .env, environment variables, or the command line.

Settings

Bases: pydantic.BaseModel

Class for storing settings.

Attributes:

Name Type Description
notion_api_token str

API token for Notion integration

credentials_location Path

file containing GCal User Credentials

database_id str

ID of the Notion database

Source code in ncal/config.py
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
class Settings(pydantic.BaseModel):
    """Class for storing settings.

    Attributes:
        notion_api_token: API token for Notion integration
        credentials_location: file containing GCal User Credentials
        database_id: ID of the Notion database
    """

    notion_api_token: str

    # get the mess of numbers before the "?" on your dashboard URL (no need to split
    # into dashes)
    database_id: str
    # open up a task and then copy the URL root up to the "p="
    url_root: str
    # json file containing GCal User Credentials
    credentials_location: Path = Path("user_token.json")
    # gcal API client secrets file
    client_secret_location: Path = Path("client_secret.json")

    default_event_length: int = 60  # Default event length in minutes
    # http://www.timezoneconverter.com/cgi-bin/zonehelp.tzc  TODO: make this unnecessary
    timezone: str = "Europe/London"

    # 8 would be 8 am. 16 would be 4 pm. Only whole numbers
    default_event_start: int = 8

    # 0 if you want dates on your Notion dashboard to be treated as an all-day event
    all_day_event_option: int = 0

    # ^^ 1 if you want dates on your Notion dashboard to be created at whatever hour you
    # defined in the DEFAULT_EVENT_START variable

    # MULTIPLE CALENDAR PART:
    #  - VERY IMPORTANT: For each 'key' of the dictionary, make sure that you make that
    # EXACT thing in the Notion database first before running the code. You WILL have
    # an error and your dashboard/calendar will be messed up

    # The GCal calendar id. The format is something like
    # "sldkjfliksedjgodsfhgshglsj@group.calendar.google.com"
    default_calendar_id: str = "cv9aoe96819t4bt5f5jars9b10@group.calendar.google.com"

    default_calendar_name: str = "Notion Events"

    # leave the first entry as is
    # the structure should be as follows:
    # WHAT_THE_OPTION_IN_NOTION_IS_CALLED: GCAL_CALENDAR_ID
    calendar_dictionary = {
        default_calendar_name: default_calendar_id,
        # "Cal Name": "asdfadsf234asef21134@group.calendar.google.com",
    }

    # doesn't delete the Notion task (yet), I'm waiting for the Python API to be
    # updated to allow deleting tasks
    delete_option: bool = False
    # set at True if you want the delete column being checked off to mean that the
    # gCal event and the Notion Event will be checked off.
    # set at False if you want nothing deleted

    # DATABASE SPECIFIC EDITS
    # There needs to be a few properties on the Notion Database for this to work.
    # Replace the values of each variable with the string of what the variable is
    # called on your Notion dashboard
    # The Last Edited Time column is a property of the notion pages themselves,
    # you just have to make it a column
    # The NeedGCalUpdate column is a formula column that works as such
    # "if(prop("Last Edited Time") > prop("Last Updated Time"), true, false)"
    # Please refer to the Template if you are confused:
    # https://www.notion.so/akarri/2583098dfd32472ab6ca1ff2a8b2866d?v=3a1adf60f15748f08ed925a2eca88421

    task_notion_name: str = "Name"
    date_notion_name: str = "Do Date"
    initiative_notion_name: str = "Project"
    initiative_notion_type: Literal["relation", "select"] = "relation"
    extrainfo_notion_name: str = "Description"
    on_gcal_notion_name: str = "On GCal?"
    need_gcal_update_notion_name: str = "NeedGCalUpdate"
    gcal_event_id_notion_name: str = "GCal Event Id"
    lastupdatedtime_notion_name: str = "Last Updated Time"
    calendar_notion_name: str = "Calendar"
    current_calendar_id_notion_name: str = "Current Calendar Id"
    delete_notion_name: str = "Done"

env_var_names_dict(prefix)

Parameters:

Name Type Description Default
prefix str

string to add to the start of each environment variable name

required

Returns:

Type Description
Dict[str, str]

Dict[str, str]: A dictionary in the form of original_name: new_name

>>> env_var_names_dict("PREFIX_")
{'notion_api_token': 'prefix_notion_api_token', 'database_id': 'prefix_database_id', 'url_root': 'prefix_url_root', 'credentials_location': 'prefix_credentials_location', 'client_secret_location': 'prefix_client_secret_location', 'default_event_length': 'prefix_default_event_length', 'timezone': 'prefix_timezone', 'default_event_start': 'prefix_default_event_start', 'all_day_event_option': 'prefix_all_day_event_option', 'default_calendar_id': 'prefix_default_calendar_id', 'default_calendar_name': 'prefix_default_calendar_name', 'delete_option': 'prefix_delete_option', 'task_notion_name': 'prefix_task_notion_name', 'date_notion_name': 'prefix_date_notion_name', 'initiative_notion_name': 'prefix_initiative_notion_name', 'initiative_notion_type': 'prefix_initiative_notion_type', 'extrainfo_notion_name': 'prefix_extrainfo_notion_name', 'on_gcal_notion_name': 'prefix_on_gcal_notion_name', 'need_gcal_update_notion_name': 'prefix_need_gcal_update_notion_name', 'gcal_event_id_notion_name': 'prefix_gcal_event_id_notion_name', 'lastupdatedtime_notion_name': 'prefix_lastupdatedtime_notion_name', 'calendar_notion_name': 'prefix_calendar_notion_name', 'current_calendar_id_notion_name': 'prefix_current_calendar_id_notion_name', 'delete_notion_name': 'prefix_delete_notion_name', 'calendar_dictionary': 'prefix_calendar_dictionary'}
Source code in ncal/config.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
def env_var_names_dict(prefix: str) -> Dict[str, str]:
    """
    Args:
        prefix: string to add to the start of each environment variable name
    Returns:
        Dict[str, str]: A dictionary in the form of `original_name: new_name`

    ```python
    >>> env_var_names_dict("PREFIX_")
    {'notion_api_token': 'prefix_notion_api_token', 'database_id': 'prefix_database_id', 'url_root': 'prefix_url_root', 'credentials_location': 'prefix_credentials_location', 'client_secret_location': 'prefix_client_secret_location', 'default_event_length': 'prefix_default_event_length', 'timezone': 'prefix_timezone', 'default_event_start': 'prefix_default_event_start', 'all_day_event_option': 'prefix_all_day_event_option', 'default_calendar_id': 'prefix_default_calendar_id', 'default_calendar_name': 'prefix_default_calendar_name', 'delete_option': 'prefix_delete_option', 'task_notion_name': 'prefix_task_notion_name', 'date_notion_name': 'prefix_date_notion_name', 'initiative_notion_name': 'prefix_initiative_notion_name', 'initiative_notion_type': 'prefix_initiative_notion_type', 'extrainfo_notion_name': 'prefix_extrainfo_notion_name', 'on_gcal_notion_name': 'prefix_on_gcal_notion_name', 'need_gcal_update_notion_name': 'prefix_need_gcal_update_notion_name', 'gcal_event_id_notion_name': 'prefix_gcal_event_id_notion_name', 'lastupdatedtime_notion_name': 'prefix_lastupdatedtime_notion_name', 'calendar_notion_name': 'prefix_calendar_notion_name', 'current_calendar_id_notion_name': 'prefix_current_calendar_id_notion_name', 'delete_notion_name': 'prefix_delete_notion_name', 'calendar_dictionary': 'prefix_calendar_dictionary'}

    ```
    """  # noqa
    return {i: prefix.lower() + i.lower() for i in Settings.__fields__.keys()}

get_env_vars_case_insensitive(env_var_names)

Get environment variables matching the given dictionary.

Parameters:

Name Type Description Default
env_var_names Dict[str, str]

{desired label: environment variable label}

required

Returns:

Type Description
Dict[str, str]

Dictionary of environment variables

Source code in ncal/config.py
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
def get_env_vars_case_insensitive(env_var_names: Dict[str, str]) -> Dict[str, str]:
    """Get environment variables matching the given dictionary.

    Args:
        env_var_names: {desired label: environment variable label}
    Returns:
        Dictionary of environment variables
    """
    load_dotenv()  # uses .env file if available
    ncal_env_vars: dict[str, str] = {}
    # populate env_vars with values which match the dictionary
    for key, value in env_var_names.items():
        if (x := value.upper()) in environ:
            ncal_env_vars[key] = environ[x]
        if (x := value.lower()) in environ:
            ncal_env_vars[key] = environ[x]
    return ncal_env_vars

load_config_file(path_to_file)

Load a toml config file into a dictionary.

Parameters:

Name Type Description Default
path_to_file Path required

Returns:

Type Description
Dict[str, Any]

Settings (unfiltered)

Source code in ncal/config.py
101
102
103
104
105
106
107
108
109
110
111
def load_config_file(path_to_file: Path) -> Dict[str, Any]:
    """Load a toml config file into a dictionary.

    Args:
        path_to_file:
    Returns:
        Settings (unfiltered)
    """
    with open(path_to_file, "rb") as f:
        tomli_dictionary = tomli.load(f)
        return tomli_dictionary

load_settings(config_file_path=None, *, use_env_vars=True, use_toml_file=True, **kwargs)

Load settings from multiple sources.

This function falls back to defaults, so only a few settings are required. Priority (high-low): cli, env, config_file.

Parameters:

Name Type Description Default
config_file_path Optional[Path]

Path to a .toml config file

None
use_env_vars bool True
use_toml_file bool True
**kwargs Any

Other settings to provide to the Settings object

{}

Returns:

Type Description
Settings

A ncal.config.Settings object

Source code in ncal/config.py
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
def load_settings(
    config_file_path: Optional[Path] = None,
    *,
    use_env_vars: bool = True,
    use_toml_file: bool = True,
    **kwargs: Any,
) -> Settings:
    """Load settings from multiple sources.

    This function falls back to defaults, so only a few settings are required.
    Priority (high-low): cli, env, config_file.

    Args:
        config_file_path: Path to a .toml config file
        use_env_vars:
        use_toml_file:
        **kwargs: Other settings to provide to the Settings object

    Returns:
        A ncal.config.Settings object
    """
    default_settings = Settings.construct().dict()  # type: ignore

    if use_env_vars:
        prefix = "NCAL_"
        env_var_names = {i: prefix + i for i in Settings.__fields__.keys()}
        env_settings = get_env_vars_case_insensitive(env_var_names)
    else:
        env_settings = {}

    if config_file_path is not None:
        try:
            toml_settings = load_config_file(config_file_path)
        except FileNotFoundError:
            print(f"no config file found at {config_file_path}")
            toml_settings = {}
    else:
        toml_settings = {}

    passed_settings = {key: value for key, value in kwargs.items() if value is not None}

    settings = {**default_settings, **toml_settings, **env_settings, **passed_settings}
    return Settings(**settings)
Back to top