from typing import List, TYPE_CHECKING, Union, Dict
from urllib.parse import urlencode, urljoin
from .base import Base
from .utils import clean_data, xor_encode
from .enums import UserRole, UserStatus
from .plan import Plan
if TYPE_CHECKING:
from . import GeoboxClient
[docs]
class User(Base):
BASE_ENDPOINT: str = 'users/'
[docs]
def __init__(self, api: 'GeoboxClient', user_id: int, data: dict = {}) -> None:
"""
Initialize a User instance.
Args:
api (GeoboxClient): The GeoboxClient instance for making requests.
user_id (int): the id of the user
data (Dict): The data of the user.
"""
super().__init__(api, data=data)
self.user_id = user_id
self.endpoint = urljoin(self.BASE_ENDPOINT, f'{self.user_id}/') if self.user_id else 'me'
[docs]
def __repr__(self) -> str:
"""
Return a string representation of the User instance.
Returns:
str: A string representation of the User instance.
"""
return f'User(id={self.id}, first_name={self.first_name}, last_name={self.last_name})'
@property
def role(self) -> 'UserRole':
"""
User role property
Returns:
UserRole: the user role
"""
return UserRole(self.data.get('role')) if self.data.get('role') else None
@property
def status(self) -> 'UserStatus':
"""
User status Property
Returns:
UserStatus: the user status
"""
return UserStatus(self.data.get('status')) if self.data.get('status') else None
@property
def plan(self) -> 'Plan':
"""
User plan Property
Returns:
Plan: the plan object
"""
plan = self.data.get('plan', {})
return Plan(self.api, plan.get('id'), plan) if plan else None
[docs]
@classmethod
def get_users(cls, api: 'GeoboxClient', **kwargs) -> Union[List['User'], int]:
"""
Retrieves a list of users (Permission Required)
Args:
api (GeoboxClient): The API instance.
Keyword Args:
status (UserStatus): the status of the users filter.
q (str): query filter based on OGC CQL standard. e.g. "field1 LIKE '%GIS%' AND created_at > '2021-01-01'"
search (str): search term for keyword-based searching among search_fields or all textual fields if search_fields does not have value. NOTE: if q param is defined this param will be ignored.
search_fields (str): comma separated list of fields for searching.
order_by (str): comma separated list of fields for sorting results [field1 A|D, field2 A|D, …]. e.g. name A, type D. NOTE: "A" denotes ascending order and "D" denotes descending order.
return_count (bool): Whether to return total count. default is False.
skip (int): Number of items to skip. default is 0.
limit (int): Number of items to return. default is 10.
user_id (int): Specific user. privileges required.
shared (bool): Whether to return shared maps. default is False.
Returns:
List[User] | int: list of users or the count number.
Example:
>>> from geobox import Geoboxclient
>>> from geobox.user import User
>>> client = GeoboxClient()
>>> users = User.get_users(client)
or
>>> users = client.get_users()
"""
params = {
'f': 'json',
'status': kwargs.get('status').value if kwargs.get('status') else None,
'q': kwargs.get('q'),
'search': kwargs.get('search'),
'search_fields': kwargs.get('search_fields'),
'order_by': kwargs.get('order_by'),
'return_count': kwargs.get('return_count', False),
'skip': kwargs.get('skip', 0),
'limit': kwargs.get('limit', 10),
'user_id': kwargs.get('user_id')
}
return super()._get_list(api, cls.BASE_ENDPOINT, params, factory_func=lambda api, item: User(api, item['id'], item))
[docs]
@classmethod
def create_user(cls,
api: 'GeoboxClient',
username: str,
email: str,
password: str,
role: 'UserRole',
first_name: str,
last_name: str,
mobile: str,
status: 'UserStatus') -> 'User':
"""
Create a User (Permission Required)
Args:
api (GeoboxClient): The GeoboxClient instance for making requests.
username (str): the username of the user.
email (str): the email of the user.
password (str): the password of the user.
role (UserRole): the role of the user.
first_name (str): the firstname of the user.
last_name (str): the lastname of the user.
mobile (str): the mobile number of the user. e.g. "+98 9120123456".
status (UserStatus): the status of the user.
Returns:
User: the user object.
Example:
>>> from geobox import GeoboxClient
>>> from geobox.user import User
>>> client = GeoboxClient()
>>> user = User.create_user(client,
... username="user1",
... email="user1@example.com",
... password="P@ssw0rd",
... role=UserRole.ACCOUNT_ADMIN,
... first_name="user 1",
... last_name="user 1",
... mobile="+98 9120123456",
... status=UserStatus.ACTIVE)
or
>>> user = client.create_user(username="user1",
... email="user1@example.com",
... password="P@ssw0rd",
... role=UserRole.ACCOUNT_ADMIN,
... first_name="user 1",
... last_name="user 1",
... mobile="+98 9120123456",
... status=UserStatus.ACTIVE)
"""
data = {
"username": username,
"email": email,
"password": xor_encode(password),
"role": role.value,
"first_name": first_name,
"last_name": last_name,
"mobile": mobile,
"status": status.value
}
return super()._create(api, cls.BASE_ENDPOINT, data, factory_func=lambda api, item: User(api, item['id'], item))
[docs]
@classmethod
def search_users(cls, api: 'GeoboxClient', search: str = None, skip: int = 0, limit: int = 10) -> List['User']:
"""
Get list of users based on the search term.
Args:
api (GeoboxClient): The GeoboxClient instance for making requests.
search (str, optional): The Search Term.
skip (int, optional): Number of items to skip. default is 0.
limit (int, optional): Number of items to return. default is 10.
Returns:
List[User]: A list of User instances.
Example:
>>> from geobox import GeoboxClient
>>> from geobox.user import User
>>> client = GeoboxClient()
>>> users = User.get_users(client, search="John")
or
>>> users = client.get_users(search="John")
"""
params = {
'search': search,
'skip': skip,
'limit': limit
}
endpoint = urljoin(cls.BASE_ENDPOINT, 'search/')
return super()._get_list(api, endpoint, params, factory_func=lambda api, item: User(api, item['id'], item))
[docs]
@classmethod
def get_user(cls, api: 'GeoboxClient', user_id: int = 'me') -> 'User':
"""
Get a user by its id (Permission Required)
Args:
api (GeoboxClient): The GeoboxClient instance for making requests.
user_id (int, optional): Specific user. don't specify a user_id to get the current user.
Returns:
User: the user object.
Raises:
NotFoundError: If the user with the specified id is not found.
Example:
>>> from geobox import GeoboxClient
>>> from geobox.user import User
>>> client = GeoboxClient()
>>> user = User.get_user(client, user_id=1)
or
>>> user = client.get_user(user_id=1)
get the current user
>>> user = User.get_user(client)
or
>>> user = client.get_user()
"""
params = {
'f': 'json'
}
return super()._get_detail(api, cls.BASE_ENDPOINT, user_id, params, factory_func=lambda api, item: User(api, item['id'], item))
[docs]
def update(self, **kwargs) -> Dict:
"""
Update the user (Permission Required)
Keyword Args:
username (str)
email (str)
first_name (str)
last_name (str)
mobile (str): e.g. "+98 9120123456"
status (UserStatus)
role (UserRole)
plan (Plan)
expiration_date (str)
Returns:
Dict: updated data
Example:
>>> from geobox imoprt GeoboxClient
>>> from geobox.user import User
>>> client = GeoboxClient()
>>> user = User.get_user(client, user_id=1)
>>> user.update(status=UserStatus.PENDING)
"""
data = {
"username": kwargs.get('username'),
"email": kwargs.get('email'),
"first_name": kwargs.get('first_name'),
"last_name": kwargs.get('last_name'),
"status": kwargs.get('status').value if kwargs.get('status') else None,
"role": kwargs.get('role').value if kwargs.get('role') else None,
}
data = clean_data(data)
try:
data['mobile'] = None if kwargs['mobile'] == '' else kwargs['mobile']
except:
pass
try:
data['plan_id'] = None if kwargs['plan'] == '' else kwargs['plan'].id
except:
pass
try:
data['expiration_date'] = None if kwargs['expiration_date'] == '' else kwargs['expiration_date']
except:
pass
response = self.api.put(self.endpoint, data)
self._update_properties(response)
return response
[docs]
def delete(self) -> None:
"""
Delete the user (Permission Required)
Returns:
None
Example:
>>> from geobox import GeoboxClient
>>> from geobox.user import User
>>> client = GeoboxClient()
>>> user = User.get_user(client, user_id=1)
>>> user.delete()
"""
super()._delete(self.endpoint)
[docs]
def get_sessions(self, user_id: int = 'me') -> List['Session']:
"""
Get a list of user available sessions (Permission Required)
Args:
user_id (int, optional): Specific user. don't specify user_id to get the current user.
Returns:
List[Session]: list of user sessions.
Example:
>>> from geobox import GeoboxClient
>>> from geobox.user import User
>>> client = GeoboxClient()
>>> user = User.get_user(client, user_id=1)
or
>>> user = client.get_user(user_id=1)
>>> user.get_sessions()
or
>>> client.get_sessions()
"""
params = clean_data({
'f': 'json'
})
query_string = urlencode(params)
if user_id != 'me':
user = self.get_user(self.api, user_id=user_id)
endpoint = f"{self.BASE_ENDPOINT}{user_id}/sessions/?{query_string}"
else:
user = self
endpoint = urljoin(self.endpoint, f'sessions/?{query_string}')
response = self.api.get(endpoint)
return [Session(item['uuid'], item, user) for item in response]
[docs]
def change_password(self, new_password: str) -> None:
"""
Change the user password (privileges required)
Args:
new_password (str): new password for the user.
Returns:
None
Example:
>>> from geobox import GeoboxClient
>>> from geobox.user import User
>>> client = GeoboxClient()
>>> user = client.get_user(user_id=1)
>>> user.change_password(new_password='user_new_password')
"""
data = clean_data({
"new_password": xor_encode(new_password)
})
endpoint = urljoin(self.endpoint, 'change-password')
self.api.post(endpoint, data, is_json=False)
[docs]
def renew_plan(self) -> None:
"""
Renew the user plan (privileges required)
Returns:
None
Example:
>>> from geobox import GeoboxClient
>>> user = client.get_user(user_id=1)
>>> user.renew_plan()
"""
endpoint = urljoin(self.endpoint, 'renewPlan')
self.api.post(endpoint)
[docs]
class Session(Base):
[docs]
def __init__(self, uuid: str, data: Dict, user: 'User'):
"""
Initialize a user session instance.
Args:
uuid (str): The unique identifier for the user session.
data (Dict): The data of the session.
user (User): the user instance.
"""
self.uuid = uuid
self.data = data
self.user = user
self.endpoint = urljoin(self.user.endpoint, f'sessions/{self.uuid}')
[docs]
def __repr__(self) -> str:
"""
Return a string representation of the resource.
Returns:
str: A string representation of the Session object.
"""
return f"Session(user={self.user}, agent='{self.agent}')"
[docs]
def close(self) -> None:
"""
Close the user session
Returns:
None
Example:
>>> from geobox import geoboxClient
>>> from geobox.user import User
>>> client = GeoboxClient()
>>> user = User.get_user(client) # without user_id parameter, it gets the current user
or
>>> user = client.get_user() # without user_id parameter, it gets the current user
>>> session = user.get_sessions()[0]
>>> session.close()
"""
data = clean_data({
'user_id': self.user.user_id,
'session_uuid': self.uuid
})
self.user.api.post(self.endpoint, data)