See More

# -*- coding: utf-8 -*- """This module contains all the classes relating to projects.""" from json import dumps from . import exceptions from . import models from . import pulls from . import users from .decorators import requires_auth from .issues import issue class Project(models.GitHubCore): """Object representing a single project from the API. See http://developer.github.com/v3/projects/ for more details. .. attribute:: body The Markdown formatted text describing the project. .. attribute:: created_at A :class:`~datetime.datetime` representing the date and time when this project was created. .. attribute:: creator A :class:`~github3.users.ShortUser` instance representing the user who created this project. .. attribute:: id The unique identifier for this project on GitHub. .. attribute:: name The name given to this project. .. attribute:: number The repository-local identifier of this project. .. attribute:: owner_url The URL of the resource in the API of the owning resource - either a repository or an organization. .. attribute:: updated_at A :class:`~datetime.datetime` representing the date and time when this project was last updated. """ CUSTOM_HEADERS = {"Accept": "application/vnd.github.inertia-preview+json"} def _update_attributes(self, project): self._api = project["url"] self.body = project["body"] self.created_at = self._strptime(project["created_at"]) self.creator = users.ShortUser(project["creator"], self) self.id = project["id"] self.name = project["name"] self.number = project["number"] self.owner_url = project["owner_url"] self.updated_at = self._strptime(project["updated_at"]) def _repr(self): return "".format(self.id) def column(self, id): """Get a project column with the given ID. :param int id: (required), the column ID :returns: the desired column in the project :rtype: :class:`~github3.projects.ProjectColumn` """ url = self._build_url("projects", "columns", str(id)) json = self._json(self._get(url, headers=Project.CUSTOM_HEADERS), 200) return self._instance_or_null(ProjectColumn, json) def columns(self, number=-1, etag=None): """Iterate over the columns in this project. :param int number: (optional), number of columns to return. Default: -1 returns all available columns. :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of columns :rtype: :class:`~github3.project.ProjectColumn` """ # TODO(sigmaviurs24): Determine if we need to construct from scratch # or if we can use `self._api` with 'columns' to build the URL url = self._build_url("projects", str(self.id), "columns") return self._iter( int(number), url, ProjectColumn, headers=Project.CUSTOM_HEADERS, etag=etag, ) @requires_auth def create_column(self, name): """Create a column in this project. :param str name: (required), name of the column :returns: the created project column :rtype: :class:`~github3.projects.ProjectColumn` """ url = self._build_url("columns", base_url=self._api) json = None if name: json = self._json( self._post( url, data={"name": name}, headers=Project.CUSTOM_HEADERS ), 201, ) return self._instance_or_null(ProjectColumn, json) @requires_auth def delete(self): """Delete this project. :returns: True if successfully deleted, False otherwise :rtype: bool """ return self._boolean( self._delete(self._api, headers=self.CUSTOM_HEADERS), 204, 404 ) @requires_auth def update(self, name=None, body=None): """Update this project. :param str name: (optional), new name of the project :param str body: (optional), new body of the project :returns: True if successfully updated, False otherwise :rtype: bool """ data = {"name": name, "body": body} json = None self._remove_none(data) if data: json = self._json( self._patch( self._api, data=dumps(data), headers=self.CUSTOM_HEADERS ), 200, ) if json: self._update_attributes(json) return True return False class ProjectColumn(models.GitHubCore): """Object representing a column in a project. See http://developer.github.com/v3/projects/columns/ .. attribute:: created_at A :class:`~datetime.datetime` object representing the date and time when the column was created. .. attribute:: id The unique identifier for this column across GitHub. .. attribute:: name The name given to this column. .. attribute:: project_url The URL used to retrieve the project that owns this column via the API. .. attribute:: updated_at A :class:`~datetime.datetime` object representing the date and time when the column was last updated. """ def _update_attributes(self, project_column): self.created_at = self._strptime(project_column["created_at"]) self.id = project_column["id"] self.name = project_column["name"] self.project_url = project_column["project_url"] self.updated_at = self._strptime(project_column["updated_at"]) def _repr(self): return "".format(self.id) def card(self, id): """Get a project card with the given ID. :param int id: (required), the card ID :returns: the card identified by the provided id :rtype: :class:`~github3.projects.ProjectCard` """ url = self._build_url("projects", "columns", "cards", str(id)) json = self._json(self._get(url, headers=Project.CUSTOM_HEADERS), 200) return self._instance_or_null(ProjectCard, json) def cards(self, number=-1, etag=None): """Iterate over the cards in this column. :param int number: (optional), number of cards to return. Default: -1 returns all available cards. :param str etag: (optional), ETag from a previous request to the same endpoint :returns: generator of cards :rtype: :class:`~github3.project.ProjectCard` """ url = self._build_url("projects", "columns", str(self.id), "cards") return self._iter( int(number), url, ProjectCard, headers=Project.CUSTOM_HEADERS, etag=etag, ) @requires_auth def create_card_with_content_id(self, content_id, content_type): """Create a content card in this project column. :param int content_id: (required), the ID of the content :param str content_type: (required), the type of the content :returns: the created card :rtype: :class:`~github3.projects.ProjectCard` """ if not content_id or not content_type: return None url = self._build_url("projects", "columns", str(self.id), "cards") json = None data = {"content_id": content_id, "content_type": content_type} json = self._json( self._post(url, data=data, headers=Project.CUSTOM_HEADERS), 201 ) return self._instance_or_null(ProjectCard, json) @requires_auth def create_card_with_issue(self, issue): """Create a card in this project column linked with an Issue. :param issue: (required), an issue with which to link the card. Can also be :class:`~github3.issues.ShortIssue`. :type issue: :class:`~github3.issues.Issue` :returns: the created card :rtype: :class:`~github3.projects.ProjectCard` """ if not issue: return None return self.create_card_with_content_id(issue.id, "Issue") @requires_auth def create_card_with_note(self, note): """Create a note card in this project column. :param str note: (required), the note content :returns: the created card :rtype: :class:`~github3.projects.ProjectCard` """ url = self._build_url("projects", "columns", str(self.id), "cards") json = None if note: json = self._json( self._post( url, data={"note": note}, headers=Project.CUSTOM_HEADERS ), 201, ) return self._instance_or_null(ProjectCard, json) @requires_auth def delete(self): """Delete this column. :returns: True if successful, False otherwise :rtype: bool """ url = self._build_url("projects", "columns", str(self.id)) return self._boolean( self._delete(url, headers=Project.CUSTOM_HEADERS), 204, 404 ) @requires_auth def move(self, position): """Move this column. :param str position: (required), can be one of `first`, `last`, or `after:`, where `` is the id value of a column in the same project. :returns: True if successful, False otherwise :rtype: bool """ if not position: return False url = self._build_url("projects", "columns", str(self.id), "moves") data = {"position": position} return self._boolean( self._post(url, data=data, headers=Project.CUSTOM_HEADERS), 201, 404, ) @requires_auth def update(self, name=None): """Update this column. :param str name: (optional), name of the column :returns: True if successful, False otherwise :rtype: bool """ data = {"name": name} json = None self._remove_none(data) if data: url = self._build_url("projects", "columns", str(self.id)) json = self._json( self._patch( url, data=dumps(data), headers=Project.CUSTOM_HEADERS ), 200, ) if json: self._update_attributes(json) return True return False class ProjectCard(models.GitHubCore): """Object representing a "card" on a project. See http://developer.github.com/v3/projects/cards/ .. attribute:: column_url The URL to retrieve this card's column via the API. .. attribute:: content_url The URl to retrieve this card's body content via the API. .. attribute:: created_at A :class:`~datetime.datetime` object representing the date and time when the column was created. .. attribute:: id The globally unique identifier for this card. .. attribute:: note The body of the note attached to this card. .. attribute:: updated_at A :class:`~datetime.datetime` object representing the date and time when the column was last updated. """ def _update_attributes(self, project_card): #: The URL of this card's parent column self.column_url = project_card["column_url"] #: The URL of this card's associated content self.content_url = project_card.get("content_url") #: datetime object representing the last time the object was created self.created_at = project_card["created_at"] #: The ID of this card self.id = project_card["id"] #: The note attached to the card self.note = project_card["note"] #: datetime object representing the last time the object was changed self.updated_at = project_card["updated_at"] def _repr(self): return "".format(self.id) @requires_auth def delete(self): """Delete this card. :returns: True if successfully deleted, False otherwise :rtype: bool """ url = self._build_url("projects", "columns", "cards", str(self.id)) return self._boolean( self._delete(url, headers=Project.CUSTOM_HEADERS), 204, 404 ) @requires_auth def move(self, position, column_id): """Move this card. :param str position: (required), can be one of `top`, `bottom`, or `after:`, where `` is the id value of a card in the same column, or in the new column specified by `column_id`. :param int column_id: (required), the id value of a column in the same project. :returns: True if successfully moved, False :rtype: bool """ if not position or not column_id: return False url = self._build_url( "projects", "columns", "cards", str(self.id), "moves" ) data = {"position": position, "column_id": column_id} return self._boolean( self._post(url, data=data, headers=Project.CUSTOM_HEADERS), 201, 404, ) @requires_auth def update(self, note=None): """Update this card. :param str note: (optional), the card's note content. Only valid for cards without another type of content, so this cannot be specified if the card already has a content_id and content_type. :returns: True if successfully updated, False otherwise :rtype: bool """ data = {"note": note} json = None self._remove_none(data) if data: url = self._build_url( "projects", "columns", "cards", str(self.id) ) json = self._json( self._patch( url, data=dumps(data), headers=Project.CUSTOM_HEADERS ), 200, ) if json: self._update_attributes(json) return True return False def retrieve_issue_from_content(self): """Attempt to retrieve an Issue from the content url. :returns: The issue that backs up this particular project card if the card has a content_url. .. note:: Cards can be created from Issues and Pull Requests. Pull Requests are also technically Issues so this method is always safe to call. :rtype: :class:`~github3.issues.issue.Issue` :raises: :class:`~github3.exceptions.CardHasNoContentUrl` """ if self.content_url is None: raise exceptions.CardHasNoContentUrl( "Card {} has no content_url".format(self.id) ) parsed = self._uri_parse(self.content_url) _, owner, repository, _, number = parsed.path[1:].split("/", 5) resp = self._get( self._build_url("repos", owner, repository, "issues", number) ) json = self._json(resp, 200) return self._instance_or_null(issue.Issue, json) def retrieve_pull_request_from_content(self): """Attempt to retrieve an PullRequest from the content url. :returns: The pull request that backs this particular project card if the card has a content_url. .. note:: Cards can be created from Issues and Pull Requests. :rtype: :class:`~github3.issues.issue.Issue` :raises: :class:`~github3.exceptions.CardHasNoContentUrl` """ if self.content_url is None: raise exceptions.CardHasNoContentUrl( "Card {} has no content_url".format(self.id) ) parsed = self._uri_parse(self.content_url) _, owner, repository, _, number = parsed.path[1:].split("/", 5) resp = self._get( self._build_url("repos", owner, repository, "pulls", number) ) json = self._json(resp, 200) return self._instance_or_null(pulls.PullRequest, json)