Skip to content

Project

CLASS DESCRIPTION
Project

Python interface for Planka Projects

Project

Project(schema: Schema, session: Planka)

Bases: PlankaModel[Project]

Python interface for Planka Projects

METHOD DESCRIPTION
add_project_manager

Add a User to the Project as a ProjectManager

copy

Create a deepcopy of the model and its associated schema.

create_base_custom_field_group

Create a BaseCustomFieldGroup in the Project

create_board

Create a new Board in the Project

delete

Delete the Project

diff

Get a schema diff between two model schemas.

import_board

Import a board from a file (currently supports trello imports only)

remove_background

Reset the Project background to the default grey

remove_project_manager

Remove a ProjectManager from the Project

update

Update the Project

update_background_image

Update the Project Background Image,

ATTRIBUTE DESCRIPTION
__formatter__

Formatter func that allows overriding str behavior for models

TYPE: ModelFormatter[Self]

background_gradient

Gradient background for the project

TYPE: BackgroundGradient | None

background_image

The current BackgroundImage of the Project

TYPE: BackgroundImage | None

background_images

Get BackgroundImages associated with the Project

TYPE: list[BackgroundImage]

background_type

Type of background for the project

base_custom_field_groups

Get BaseCustomFieldGroups associated with the Project

TYPE: list[BaseCustomFieldGroup]

board_memberships

Get BoardMemberships associated with the Project

TYPE: list[BoardMembership]

boards

Get Boards associated with the Project

TYPE: list[Board]

created_at

When the project was created

TYPE: datetime

custom_fields

Get CustomFields associated with the Project

TYPE: list[CustomField]

description

Detailed description of the Project

TYPE: str

favorite

Whether the project is in the current User's favorites

TYPE: bool

hidden

Whether the project is hidden

TYPE: bool

name

Name/title of the Project

TYPE: str

notification_services

Get NotificationServices associated with the Project

TYPE: list[NotificationService]

owner

The User who owns the project (Raises LookupError if the User cannot be found)

TYPE: User | None

project_managers

Get project manager Users associated with the Project

TYPE: list[ProjectManager]

updated_at

When the project was last updated

TYPE: datetime

users

Get Users associated with the Project

TYPE: list[User]

Source code in src/plankapy/v2/models/_base.py
30
31
32
33
34
35
36
def __init__(self, schema: Schema, session: Planka) -> None:
    self._schema = schema
    self.session = session
    self.endpoints = session.endpoints
    self.client = session.client
    self.current_role = session.current_role
    self.current_id = session.current_id

__formatter__ class-attribute instance-attribute

__formatter__: ModelFormatter[Self] = DEFAULT_FORMATTER

Formatter func that allows overriding str behavior for models

background_gradient property writable

background_gradient: BackgroundGradient | None

Gradient background for the project

background_image property writable

background_image: BackgroundImage | None

The current BackgroundImage of the Project

background_images property

background_images: list[BackgroundImage]

Get BackgroundImages associated with the Project

background_type property writable

background_type

Type of background for the project

base_custom_field_groups property

base_custom_field_groups: list[BaseCustomFieldGroup]

Get BaseCustomFieldGroups associated with the Project

board_memberships property

board_memberships: list[BoardMembership]

Get BoardMemberships associated with the Project

boards property

boards: list[Board]

Get Boards associated with the Project

created_at property

created_at: datetime

When the project was created

custom_fields property

custom_fields: list[CustomField]

Get CustomFields associated with the Project

description property writable

description: str

Detailed description of the Project

favorite property writable

favorite: bool

Whether the project is in the current User's favorites

hidden property writable

hidden: bool

Whether the project is hidden

name property writable

name: str

Name/title of the Project

notification_services property

notification_services: list[NotificationService]

Get NotificationServices associated with the Project

owner property

owner: User | None

The User who owns the project (Raises LookupError if the User cannot be found)

project_managers property

project_managers: list[ProjectManager]

Get project manager Users associated with the Project

updated_at property

updated_at: datetime

When the project was last updated

users property

users: list[User]

Get Users associated with the Project

add_project_manager

add_project_manager(user: User) -> ProjectManager

Add a User to the Project as a ProjectManager

Source code in src/plankapy/v2/models/project.py
183
184
185
def add_project_manager(self, user: User) -> ProjectManager:
    """Add a User to the Project as a ProjectManager"""
    return ProjectManager(self.endpoints.createProjectManager(self.id, userId=user.id)['item'], self.session)

copy

copy() -> Self

Create a deepcopy of the model and its associated schema.

Note

Since the endpoints for both instances of the Model are the same, any calls to update will restore the state and bring both copies into sync. copies like this are meant more for comparing changes when running a sync or update/assignemnt operation.

Example:

    >>> card_copy = card.copy()
    >>> card.name = 'Updated Name'
    >>> card_copy.name
    'Original Name'
    >>> card.name
    'Updated Name'
    >>> # This update may have had side effects
    >>> print(card_copy.diff(card))
    {'name': ('Original Name', 'Updated Name'), 'updatedAt': ('...2:00pm', '...2:45pm'), ...}

Source code in src/plankapy/v2/models/_base.py
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def copy(self) -> Self:
    """Create a deepcopy of the model and its associated schema.

    Note:
        Since the endpoints for both instances of the Model are the same, any 
        calls to update will restore the state and bring both copies into sync. 
        copies like this are meant more for comparing changes when running a sync 
        or update/assignemnt operation.

    Example:
    ```python
        >>> card_copy = card.copy()
        >>> card.name = 'Updated Name'
        >>> card_copy.name
        'Original Name'
        >>> card.name
        'Updated Name'
        >>> # This update may have had side effects
        >>> print(card_copy.diff(card))
        {'name': ('Original Name', 'Updated Name'), 'updatedAt': ('...2:00pm', '...2:45pm'), ...}
    ```
    """
    return copy.deepcopy(self)

create_base_custom_field_group

create_base_custom_field_group(*, name: str) -> BaseCustomFieldGroup

Create a BaseCustomFieldGroup in the Project

PARAMETER DESCRIPTION

name

The name of the new BaseCustomFieldGroup

TYPE: str

Source code in src/plankapy/v2/models/project.py
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
def create_base_custom_field_group(self, 
                                   *, 
                                   name: str) -> BaseCustomFieldGroup:
    """Create a BaseCustomFieldGroup in the Project

    Args:
        name: The name of the new BaseCustomFieldGroup
    """
    return BaseCustomFieldGroup(
        self.endpoints.createBaseCustomFieldGroup(
            self.id, 
            name=name,
        )['item'], 
        self.session
    )

create_board

create_board(*, name: str, position: Position | int = 'top') -> Board

Create a new Board in the Project

PARAMETER DESCRIPTION

name

The name of the Board

TYPE: str

position

The position of the board within the project

TYPE: Position | int DEFAULT: 'top'

Source code in src/plankapy/v2/models/project.py
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
def create_board(self, 
                 *, 
                 name: str, 
                 position: Position | int ='top') -> Board:
    """Create a new Board in the Project

    Args:
        name: The name of the Board
        position: The position of the board within the project
    """
    return Board(
        self.endpoints.createBoard(
            self.id, 
            name=name, 
            position=get_position(self.boards, position)
        )['item'], 
        self.session
    )

delete

delete() -> None

Delete the Project

Source code in src/plankapy/v2/models/project.py
179
180
181
def delete(self) -> None:
    """Delete the Project"""
    self.endpoints.deleteProject(self.id)

diff

diff(other: PlankaModel[Schema]) -> Diff

Get a schema diff between two model schemas.

Note

Only matching keys are diffed. Any schema keys that are not in the source schema will not be checked in the target schema

Source code in src/plankapy/v2/models/_base.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
def diff(self, other: PlankaModel[Schema]) -> Diff:
    """Get a schema diff between two model schemas.

    Note:
        Only matching keys are diffed. Any schema keys that are not in the source schema 
        will not be checked in the target schema
    """
    return {
        k: (source, delta) 
        for k, source in self.schema
        if k in other.schema
        and (delta := other.schema[k]) 
        and delta != source
    }

import_board

import_board(*, name: str, import_file: bytes, position: Position | int = 'top', import_type: BoardImportType = 'trello', request_id: str | None = None) -> Board

Import a board from a file (currently supports trello imports only)

PARAMETER DESCRIPTION

name

The name of the imported Board

TYPE: str

position

The position of the imported Board within the Project

TYPE: Position | int DEFAULT: 'top'

import_type

The type of the Bord import (currently trello only)

TYPE: BoardImportType DEFAULT: 'trello'

request_id

An optional request ID for tracking upload progress (default: now in iso8601)

TYPE: str | None DEFAULT: None

Source code in src/plankapy/v2/models/project.py
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
def import_board(self, 
                 *, 
                 name: str, 
                 import_file: bytes, 
                 position: Position | int='top', 
                 import_type: BoardImportType='trello',
                 request_id: str|None=None) -> Board:
    """Import a board from a file (currently supports trello imports only)

    Args:
        name: The name of the imported Board
        position: The position of the imported Board within the Project
        import_type: The type of the Bord import (currently `trello` only)
        request_id: An optional request ID for tracking upload progress (default: `now in iso8601`)
    """
    return Board(
        self.endpoints.createBoard(
            self.id,
            name=name,
            position=get_position(self.boards, position),
            importType=import_type,
            importFile=import_file,
            requestId=request_id or datetime.now().isoformat(),
        )['item'],
        self.session
    )

remove_background

remove_background() -> None

Reset the Project background to the default grey

Source code in src/plankapy/v2/models/project.py
256
257
258
def remove_background(self) -> None:
    """Reset the Project background to the default grey"""
    self.update(backgroundType=None)

remove_project_manager

remove_project_manager(project_manager: ProjectManager | User) -> None

Remove a ProjectManager from the Project

Source code in src/plankapy/v2/models/project.py
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
def remove_project_manager(self, project_manager: ProjectManager | User) -> None:
    """Remove a ProjectManager from the Project"""
    if isinstance(project_manager, User):
        # Get the ProjectManager object for the User
        for pm in self.project_managers:
            if pm.user == project_manager:
                project_manager = pm
                break
        else:
            # If User not in managers, do nothing
            return

    if project_manager.project == self:
        # Only delete ProjectManager if it is for this Project
        # Defer deletion to the ProjectManager object
        project_manager.delete()

update

update(**project: Unpack[Request_updateProject]) -> None

Update the Project

Source code in src/plankapy/v2/models/project.py
175
176
177
def update(self, **project: Unpack[paths.Request_updateProject]) -> None:
    """Update the Project"""
    self.schema = self.endpoints.updateProject(self.id, **project)['item']

update_background_image

update_background_image(background: BackgroundImage | Path | str | bytes | None) -> BackgroundImage | None

Update the Project Background Image,

Only Admins and ProjectManagers can update the Background Image

PARAMETER DESCRIPTION

background

Existing Image or filepath/url or raw bytes or None to unset

TYPE: BackgroundImage | Path | str | bytes | None

RETURNS DESCRIPTION
BackgroundImage | None

If a backround image was set or created

Source code in src/plankapy/v2/models/project.py
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def update_background_image(self, background: BackgroundImage | Path | str | bytes | None) -> BackgroundImage | None:
    """Update the Project Background Image,

    Only Admins and ProjectManagers can update the Background Image

    Args:
        background (BackgroundImage | Path | str | bytes | None): Existing Image or filepath/url or raw bytes or None to unset

    Returns:
        (BackgroundImage | None): If a backround image was set or created
    """
    # Force a PermissionError early if this user isn't the current user or an admin
    if self.id != self.session.current_id and self.session.current_role != 'admin':
        self.endpoints.createBackgroundImage(self.id, **{'file': b'NO_PERMISSION'})

    if background is None:
        self.remove_background()
        return

    if isinstance(background, BackgroundImage):
        # Assign the image if it is in this project
        if background in self.background_images:
            self.background_image = background

        # Re-Upload to this project
        else:
            self.update_background_image(background.url)
        return background

    if isinstance(background, Path):
        # Convert Path to a string so it can be handled normally
        background = str(background.resolve())

    # Deferred import of mimetypes that is only used here
    # This function takes so long anyways so the import delay 
    # isn't noticable
    import mimetypes

    # Handle filepath or URL
    mime_type = None
    if isinstance(background, str):
        # Guess URL file type
        if background.startswith('http'):
            mime_type, *_ = mimetypes.guess_type(background)
            mime_type = mime_type or 'application/octet-stream'
            try:
                req = self.client.get(background)
                req.raise_for_status()
                background = req.content
            except HTTPStatusError as status_error:
                status_error.add_note(f'Unable to download attachment from {background}')
                raise
        # Guess local file type
        # And read Bytes
        else:
            mime_type, *_ = mimetypes.guess_file_type(background)
            mime_type = mime_type or 'application/octet-stream'
            background = open(background, 'rb').read()

    mime_type = mime_type or 'application/octet-stream'
    return BackgroundImage(
        self.endpoints.createBackgroundImage(
            self.id, 
            file=bytes(background), 
            mime_type=mime_type,
        )['item'],
        self.session
    )