diff --git a/qfieldcloud_sdk/sdk.py b/qfieldcloud_sdk/sdk.py index 6759b74..71f1d2d 100644 --- a/qfieldcloud_sdk/sdk.py +++ b/qfieldcloud_sdk/sdk.py @@ -30,26 +30,55 @@ DEFAULT_PAGINATION_LIMIT = 20 - +"""int: Defines the default limit for pagination, set to `20`.""" class FileTransferStatus(str, Enum): + """Represents the status of a file transfer. + + Attributes: + PENDING (str): The transfer is pending. + SUCCESS (str): The transfer was successful. + FAILED (str): The transfer failed. + """ PENDING = "PENDING" SUCCESS = "SUCCESS" FAILED = "FAILED" class FileTransferType(Enum): + """Represents the type of file transfer. + + Attributes: + PROJECT (str): Refers to a project file. + PACKAGE (str): Refers to a package Type. + """ PROJECT = "project" PACKAGE = "package" class JobTypes(str, Enum): + """Represents the types of jobs that can be processed on QFieldCloud. + + Attributes: + PACKAGE (str): Refers to a packaging job. + APPLY_DELTAS (str): Refers to applying deltas (differences). + PROCESS_PROJECTFILE (str): Refers to processing a project file. + """ PACKAGE = "package" APPLY_DELTAS = "delta_apply" PROCESS_PROJECTFILE = "process_projectfile" class ProjectCollaboratorRole(str, Enum): + """Defines roles for project collaborators. + + Attributes: + ADMIN (str): Administrator role. + MANAGER (str): Manager role. + EDITOR (str): Editor role. + REPORTER (str): Reporter role. + READER (str): Reader role. + """ ADMIN = "admin" MANAGER = "manager" EDITOR = "editor" @@ -58,11 +87,28 @@ class ProjectCollaboratorRole(str, Enum): class OrganizationMemberRole(str, Enum): + """Defines roles for organization members. + + Attributes: + ADMIN (str): Administrator role. + MEMBER (str): Member role. + """ ADMIN = "admin" MEMBER = "member" class CollaboratorModel(TypedDict): + """Represents the structure of a project collaborator in the QFieldCloud system. + + Attributes: + collaborator (str): The collaborator's identifier. + role (ProjectCollaboratorRole): The role of the collaborator. + project_id (str): The associated project identifier. + created_by (str): The user who created the collaborator entry. + updated_by (str): The user who last updated the collaborator entry. + created_at (datetime.datetime): The timestamp when the collaborator entry was created. + updated_at (datetime.datetime): The timestamp when the collaborator entry was last updated. + """ collaborator: str role: ProjectCollaboratorRole project_id: str @@ -73,11 +119,21 @@ class CollaboratorModel(TypedDict): class OrganizationMemberModel(TypedDict): + """Represents the structure of an organization member in the QFieldCloud system. + + Attributes: + member (str): The member's identifier. + role (OrganizationMemberRole): The role of the member. + organization (str): The associated organization identifier. + is_public (bool): A boolean indicating if the membership is public. + + Todo: + * Future work that can be surely expected, check QF-4535 + """ member: str role: OrganizationMemberRole organization: str is_public: bool - # TODO future work that can be surely expected, check QF-4535 # created_by: str # updated_by: str # created_at: datetime.datetime @@ -85,29 +141,68 @@ class OrganizationMemberModel(TypedDict): class Pagination: + """The Pagination class allows for controlling and managing pagination of results within the QFieldCloud SDK. + + Attributes: + limit (Optional[int]): The maximum number of items to return. + offset (Optional[int]): The starting point from which to return items. + """ + limit = None offset = None def __init__( self, limit: Optional[int] = None, offset: Optional[int] = None ) -> None: + """Initializes the pagination settings. + + Args: + limit (Optional[int]): The maximum number of items to return. Defaults to None. + offset (Optional[int]): The starting point from which to return items. Defaults to None. + """ self.limit = limit self.offset = offset @property def is_empty(self): + """Checks if both limit and offset are None, indicating no pagination settings. + + Returns: + bool: True if both limit and offset are None, False otherwise. + """ return self.limit is None and self.offset is None class Client: + """The core component of the QFieldCloud SDK, providing methods for interacting with the QFieldCloud platform. + + This class handles authentication, project management, file management, and more. + + Attributes: + session (requests.Session): The session object to maintain connections. + url (str): The base URL for the QFieldCloud API. + token (str): The authentication token for API access. + verify_ssl (bool): Whether to verify SSL certificates. + """ + def __init__( self, url: str = None, verify_ssl: bool = None, token: str = None ) -> None: """Prepares a new client. + If the `verify_ssl` is set to `False`, SSL warnings will be disabled. The session is configured with retries for GET requests on specific 5xx HTTP status codes. + If the `url` is not provided, uses `QFIELDCLOUD_URL` from the environment. If the `token` is not provided, uses `QFIELDCLOUD_TOKEN` from the environment. `session` will be reused between requests if the SDK is run as a library. + + Args: + url (Optional[str]): The base URL for the QFieldCloud API. Defaults to `QFIELDCLOUD_URL` environment variable if not provided. + verify_ssl (Optional[bool]): Whether to verify SSL certificates. Defaults to True if not specified. + token (Optional[str]): The authentication token for API access. Defaults to `QFIELDCLOUD_TOKEN` environment variable if not provided. + + Raises: + QfcException: If the `url` is not provided either directly or through the environment variable. """ self.session = requests.Session() # retries should be only on GET and only if error 5xx @@ -138,6 +233,13 @@ def login(self, username: str, password: str) -> Dict: Args: username: the username or the email used to register password: the password associated with that username + + Returns: + Dict: A dictionary containing the authentication token and additional metadata. + + Example: + client = Client(url="https://app.qfield.cloud/api/v1/") + client.login("ninjamaster", "secret_password123") """ resp = self._request( "POST", @@ -156,7 +258,14 @@ def login(self, username: str, password: str) -> Dict: return payload def logout(self) -> None: - """Logout from the current session.""" + """Logout from the current session. + + Returns: + None + + Example: + client.logout() + """ resp = self._request("POST", "auth/logout") return resp.json() @@ -170,6 +279,16 @@ def list_projects( """ Returns a list of projects accessible to the current user, their own and optionally the public ones. + + Args: + include_public (Optional[bool]): Whether to include public projects in the list. Defaults to False. + pagination (Pagination): Pagination settings for the request. Defaults to an empty Pagination instance. + + Returns: + List[Dict[str, Any]]: A list of dictionaries containing project details. + + Example: + client.list_projects() """ params = { "include-public": str(int(include_public)), # type: ignore @@ -183,6 +302,21 @@ def list_projects( def list_remote_files( self, project_id: str, skip_metadata: bool = True ) -> List[Dict[str, Any]]: + """Lists the files available in the specified project. + + Args: + project_id (str): The ID of the project to list files for. + skip_metadata (bool): Whether to skip fetching metadata for the files. Defaults to True. + + Returns: + List[Dict[str, Any]]: A list of dictionaries containing file details. + + Example: + client.list_remote_files("project_id", True) + + Todo: + * Remove this temporary decoration with `etag` key + """ params = {} if skip_metadata: @@ -190,7 +324,6 @@ def list_remote_files( resp = self._request("GET", f"files/{project_id}", params=params) remote_files = resp.json() - # TODO remove this temporary decoration with `etag` key remote_files = list(map(lambda f: {"etag": f["md5sum"], **f}, remote_files)) return remote_files @@ -202,6 +335,20 @@ def create_project( description: str = "", is_public: bool = False, ) -> Dict: + """Creates a new project in QFieldCloud. + + Args: + name (str): The name of the new project. + owner (Optional[str]): The owner of the project. Defaults to None. + description (Optional[str]): A description of the project. Defaults to an empty string. + is_public (Optional[bool]): Whether the project should be public. Defaults to False. + + Returns: + Dict: A dictionary containing the details of the created project. + + Example: + client.create_project(name="my_project", is_public=False) + """ resp = self._request( "POST", "projects",