class Model(object):
class Collaborator(Model):
class CollaboratorState(Model):
class Filter(Model):
class Item(Model):
class Label(Model):
class LiveNotification(Model):
class GenericNote(Model):
class Note(GenericNote):
class ProjectNote(GenericNote):
class Project(Model):
class Reminder(Model):
class Section(Model):
    def __init__(self, data, api):
    def __setitem__(self, key, value):
    def __getitem__(self, key):
    def __repr__(self):
    def __contains__(self, value):
    def delete(self, project_id):
    def update(self, **kwargs):
    def delete(self):
    def update(self, **kwargs):
    def delete(self):
    def move(self, **kwargs):
    def reorder(self, child_order):
    def close(self):
    def complete(self, date_completed=None):
    def uncomplete(self):
    def archive(self):
    def unarchive(self):
    def update_date_complete(self, due=None):
    def update(self, **kwargs):
    def delete(self):
    def update(self, **kwargs):
    def delete(self):
    def __init__(self, data, api):
    def __init__(self, data, api):
    def update(self, **kwargs):
    def delete(self):
    def archive(self):
    def unarchive(self):
    def move(self, parent_id):
    def reorder(self, child_order):
    def share(self, email):
    def take_ownership(self):
    def update(self, **kwargs):
    def delete(self):
    def update(self, **kwargs):
    def delete(self):
    def move(self, project_id):
    def reorder(self, section_order):
    def archive(self, date_archived=None):
    def unarchive(self): class ActivityManager(Manager):
    def get(self, **kwargs):
        """
        Get events from the activity log.
        """
        params = {"token": self.token}
        params.update(kwargs)
        return self.api._get("activity/get", params=params) class ItemsArchiveManagerMaker(object):
    def __init__(self, api):
        self.api = api

    def __repr__(self):
        return "{}()".format(self.__class__.__name__)

    def for_project(self, project_id):
        """Get manager to iterate over all top-level archived items for project."""
        return ItemsArchiveManager(api=self.api, project_id=project_id)

    def for_section(self, section_id):
        """Get manager to iterate over all top-level archived items for section."""
        return ItemsArchiveManager(api=self.api, section_id=section_id)

    def for_parent(self, parent_id):
        """Get manager to iterate over all archived sub-tasks for an item."""
        return ItemsArchiveManager(api=self.api, parent_id=parent_id) class SectionsArchiveManagerMaker(object):
    def __init__(self, api):
        self.api = api

    def __repr__(self):
        return "{}()".format(self.__class__.__name__)

    def for_project(self, project_id):
        """Get manager to iterate over all archived sections for project."""
        return SectionsArchiveManager(api=self.api, project_id=project_id) class BackupsManager(Manager):
    def get(self):
        """
        Get backups.
        """
        params = {"token": self.token}
        return self.api._get("backups/get", params=params) class BizInvitationsManager(Manager):

    state_name = None  # there is no local state associated
    object_type = None  # there is no object type associated

    def accept(self, invitation_id, invitation_secret):
        """
        Accepts a business invitation to share a project.
        """
        cmd = {
            "type": "biz_accept_invitation",
            "uuid": self.api.generate_uuid(),
            "args": {
                "invitation_id": invitation_id,
                "invitation_secret": invitation_secret,
            },
        }
        self.queue.append(cmd)

    def reject(self, invitation_id, invitation_secret):
        """
        Rejects a business invitation to share a project.
        """
        cmd = {
            "type": "biz_reject_invitation",
            "uuid": self.api.generate_uuid(),
            "args": {
                "invitation_id": invitation_id,
                "invitation_secret": invitation_secret,
            },
        }
        self.queue.append(cmd) class BusinessUsersManager(Manager):
    def invite(self, email_list):
        """
        Send a business user invitation.
        """
        params = {"token": self.token, "email_list": json.dumps(email_list)}
        return self.api._get("business/users/invite", params=params)

    def accept_invitation(self, id, secret):
        """
        Accept a business user invitation.
        """
        params = {"token": self.token, "id": id, "secret": secret}
        return self.api._get("business/users/accept_invitation", params=params)

    def reject_invitation(self, id, secret):
        """
        Reject a business user invitation.
        """
        params = {"token": self.token, "id": id, "secret": secret}
        return self.api._get("business/users/reject_invitation", params=params) class CollaboratorStatesManager(Manager, SyncMixin):

    state_name = "collaborator_states"
    object_type = None  # there is no object type associated

    def get_by_ids(self, project_id, user_id):
        """
        Finds and returns the collaborator state based on the project and user
        ids.
        """
        for obj in self.state[self.state_name]:
            if obj["project_id"] == project_id and obj["user_id"] == user_id:
                return obj
        return None class CollaboratorsManager(Manager, GetByIdMixin, SyncMixin):

    state_name = "collaborators"
    object_type = None  # there is no object type associated

    def delete(self, project_id, email):
        """
        Deletes a collaborator from a shared project.
        """
        cmd = {
            "type": "delete_collaborator",
            "uuid": self.api.generate_uuid(),
            "args": {"project_id": project_id, "email": email},
        }
        self.queue.append(cmd) class CompletedManager(Manager):
    def get_stats(self):
        """
        Returns the user's recent productivity stats.
        """
        return self.api._get("completed/get_stats", params={"token": self.token})

    def get_all(self, **kwargs):
        """
        Returns all user's completed items.
        """
        params = {"token": self.token}
        params.update(kwargs)
        return self.api._get("completed/get_all", params=params) class EmailsManager(Manager):
    def get_or_create(self, obj_type, obj_id, **kwargs):
        """
        Get or create email to an object.
        """
        params = {"token": self.token, "obj_type": obj_type, "obj_id": obj_id}
        params.update(kwargs)
        return self.api._get("emails/get_or_create", params=params)

    def disable(self, obj_type, obj_id, **kwargs):
        """
        Disable email to an object.
        """
        params = {"token": self.token, "obj_type": obj_type, "obj_id": obj_id}
        params.update(kwargs)
        return self.api._get("emails/disable", params=params) class FiltersManager(Manager, AllMixin, GetByIdMixin, SyncMixin):

    state_name = "filters"
    object_type = "filter"

    def add(self, name, query, **kwargs):
        """
        Creates a local filter object.
        """
        obj = models.Filter({"name": name, "query": query}, self.api)
        obj.temp_id = obj["id"] = self.api.generate_uuid()
        obj.data.update(kwargs)
        self.state[self.state_name].append(obj)
        cmd = {
            "type": "filter_add",
            "temp_id": obj.temp_id,
            "uuid": self.api.generate_uuid(),
            "args": {key: obj.data[key] for key in obj.data if key != "id"},
        }
        self.queue.append(cmd)
        return obj

    def update(self, filter_id, **kwargs):
        """
        Updates a filter remotely.
        """
        args = {"id": filter_id}
        args.update(kwargs)
        cmd = {
            "type": "filter_update",
            "uuid": self.api.generate_uuid(),
            "args": args,
        }
        self.queue.append(cmd)

    def delete(self, filter_id):
        """
        Deletes a filter remotely.
        """
        cmd = {
            "type": "filter_delete",
            "uuid": self.api.generate_uuid(),
            "args": {"id": filter_id},
        }
        self.queue.append(cmd)

    def update_orders(self, id_order_mapping):
        """
        Updates the orders of multiple filters remotely.
        """
        cmd = {
            "type": "filter_update_orders",
            "uuid": self.api.generate_uuid(),
            "args": {"id_order_mapping": id_order_mapping},
        }
        self.queue.append(cmd)

    def get(self, filter_id):
        """
        Gets an existing filter.
        """
        params = {"token": self.token, "filter_id": filter_id}
        obj = self.api._get("filters/get", params=params)
        if obj and "error" in obj:
            return None
        data = {"filters": []}
        if obj.get("filter"):
            data["filters"].append(obj.get("filter"))
        self.api._update_state(data)
        return obj class InvitationsManager(Manager, SyncMixin):

    state_name = None  # there is no local state associated
    object_type = "share_invitation"

    def accept(self, invitation_id, invitation_secret):
        """
        Accepts an invitation to share a project.
        """
        cmd = {
            "type": "accept_invitation",
            "uuid": self.api.generate_uuid(),
            "args": {
                "invitation_id": invitation_id,
                "invitation_secret": invitation_secret,
            },
        }
        self.queue.append(cmd)

    def reject(self, invitation_id, invitation_secret):
        """
        Rejets an invitation to share a project.
        """
        cmd = {
            "type": "reject_invitation",
            "uuid": self.api.generate_uuid(),
            "args": {
                "invitation_id": invitation_id,
                "invitation_secret": invitation_secret,
            },
        }
        self.queue.append(cmd)

    def delete(self, invitation_id):
        """
        Delete an invitation to share a project.
        """
        cmd = {
            "type": "delete_invitation",
            "uuid": self.api.generate_uuid(),
            "args": {"invitation_id": invitation_id},
        }
        self.queue.append(cmd) class ItemsManager(Manager, AllMixin, GetByIdMixin, SyncMixin):

    state_name = "items"
    object_type = "item"

    def add(self, content, **kwargs):
        """
        Creates a local item object.
        """
        project_id = kwargs.get("project_id")
        if not project_id:
            project_id = self.state["user"]["inbox_project"]
        obj = models.Item({"content": content, "project_id": project_id}, self.api)
        obj.temp_id = obj["id"] = self.api.generate_uuid()
        obj.data.update(kwargs)
        self.state[self.state_name].append(obj)
        cmd = {
            "type": "item_add",
            "temp_id": obj.temp_id,
            "uuid": self.api.generate_uuid(),
            "args": {key: obj.data[key] for key in obj.data if key != "id"},
        }
        self.queue.append(cmd)
        return obj

    def update(self, item_id, **kwargs):
        """
        Updates an item remotely.
        """
        args = {"id": item_id}
        args.update(kwargs)
        cmd = {
            "type": "item_update",
            "uuid": self.api.generate_uuid(),
            "args": args,
        }
        self.queue.append(cmd)

    def delete(self, item_id):
        """
        Deletes an item remotely.
        """
        cmd = {
            "type": "item_delete",
            "uuid": self.api.generate_uuid(),
            "args": {"id": item_id},
        }
        self.queue.append(cmd)

    def move(self, item_id, **kwargs):
        """
        Moves item to another parent, project, or section remotely.
        """
        args = {
            "id": item_id,
        }
        if "parent_id" in kwargs:
            args["parent_id"] = kwargs.get("parent_id")
        elif "project_id" in kwargs:
            args["project_id"] = kwargs.get("project_id")
        elif "section_id" in kwargs:
            args["section_id"] = kwargs.get("section_id")
        else:
            raise TypeError("move() takes one of parent_id, project_id, or section_id arguments")
        cmd = {"type": "item_move", "uuid": self.api.generate_uuid(), "args": args}
        self.queue.append(cmd)

    def close(self, item_id):
        """
        Marks item as done
        """
        cmd = {
            "type": "item_close",
            "uuid": self.api.generate_uuid(),
            "args": {"id": item_id},
        }
        self.queue.append(cmd)

    def complete(self, item_id, date_completed=None, force_history=None):
        """
        Marks item as completed remotely.
        """
        args = {
            "id": item_id,
        }
        if date_completed is not None:
            args["date_completed"] = date_completed
        if force_history is not None:
            args["force_history"] = force_history
        cmd = {
            "type": "item_complete",
            "uuid": self.api.generate_uuid(),
            "args": args,
        }
        self.queue.append(cmd)

    def uncomplete(self, item_id):
        """
        Marks item as uncompleted remotely.
        """
        cmd = {
            "type": "item_uncomplete",
            "uuid": self.api.generate_uuid(),
            "args": {"id": item_id},
        }
        self.queue.append(cmd)

    def archive(self, item_id):
        """
        Marks item as archived remotely.
        """
        cmd = {
            "type": "item_archive",
            "uuid": self.api.generate_uuid(),
            "args": {"id": item_id},
        }
        self.queue.append(cmd)

    def unarchive(self, item_id):
        """
        Marks item as unarchived remotely.
        """
        cmd = {
            "type": "item_unarchive",
            "uuid": self.api.generate_uuid(),
            "args": {"id": item_id},
        }
        self.queue.append(cmd)

    def update_date_complete(self, item_id, due=None):
        """
        Completes a recurring task remotely.
        """
        args = {
            "id": item_id,
        }
        if due:
            args["due"] = due
        cmd = {
            "type": "item_update_date_complete",
            "uuid": self.api.generate_uuid(),
            "args": args,
        }
        self.queue.append(cmd)

    def reorder(self, items):
        """
        Updates the child_order of the specified items.
        """
        cmd = {
            "type": "item_reorder",
            "uuid": self.api.generate_uuid(),
            "args": {"items": items},
        }
        self.queue.append(cmd)

    def update_day_orders(self, ids_to_orders):
        """
        Updates in the local state the day orders of multiple items remotely.
        """
        cmd = {
            "type": "item_update_day_orders",
            "uuid": self.api.generate_uuid(),
            "args": {"ids_to_orders": ids_to_orders},
        }
        self.queue.append(cmd)

    def get_completed(self, project_id, **kwargs):
        """
        Returns a project's completed items.
        """
        params = {"token": self.token, "project_id": project_id}
        params.update(kwargs)
        return self.api._get("items/get_completed", params=params)

    def get(self, item_id):
        """
        Gets an existing item.
        """
        params = {"token": self.token, "item_id": item_id}
        obj = self.api._get("items/get", params=params)
        if obj and "error" in obj:
            return None

        data = {"projects": [], "items": [], "notes": []}
        if obj.get("project"):
            data["projects"].append(obj.get("project"))
        if obj.get("item"):
            data["items"].append(obj.get("item"))
        if obj.get("notes"):
            data["notes"] += obj.get("notes")
        self.api._update_state(data)
        return obj class LabelsManager(Manager, AllMixin, GetByIdMixin, SyncMixin):

    state_name = "labels"
    object_type = "label"

    def add(self, name, **kwargs):
        """
        Creates a local label object.
        """
        obj = models.Label({"name": name}, self.api)
        obj.temp_id = obj["id"] = self.api.generate_uuid()
        obj.data.update(kwargs)
        self.state[self.state_name].append(obj)
        cmd = {
            "type": "label_add",
            "temp_id": obj.temp_id,
            "uuid": self.api.generate_uuid(),
            "args": {key: obj.data[key] for key in obj.data if key != "id"},
        }
        self.queue.append(cmd)
        return obj

    def update(self, label_id, **kwargs):
        """
        Updates a label remotely.
        """
        args = {"id": label_id}
        args.update(kwargs)
        cmd = {
            "type": "label_update",
            "uuid": self.api.generate_uuid(),
            "args": args,
        }
        self.queue.append(cmd)

    def delete(self, label_id):
        """
        Deletes a label remotely.
        """
        cmd = {
            "type": "label_delete",
            "uuid": self.api.generate_uuid(),
            "args": {"id": label_id},
        }
        self.queue.append(cmd)

    def update_orders(self, id_order_mapping):
        """
        Updates the orders of multiple labels remotely.
        """
        cmd = {
            "type": "label_update_orders",
            "uuid": self.api.generate_uuid(),
            "args": {"id_order_mapping": id_order_mapping},
        }
        self.queue.append(cmd)

    def get(self, label_id):
        """
        Gets an existing label.
        """
        params = {"token": self.token, "label_id": label_id}
        obj = self.api._get("labels/get", params=params)
        if obj and "error" in obj:
            return None
        data = {"labels": []}
        if obj.get("label"):
            data["labels"].append(obj.get("label"))
        self.api._update_state(data)
        return obj class LiveNotificationsManager(Manager, GetByIdMixin, AllMixin, SyncMixin):

    state_name = "live_notifications"
    object_type = None  # there is no object type associated

    def set_last_read(self, id):
        """
        Sets the last known notification.
        """
        cmd = {
            "type": "live_notifications_set_last_read",
            "uuid": self.api.generate_uuid(),
            "args": {"id": id},
        }
        self.queue.append(cmd)

    def mark_read(self, id):
        """
        Marks notification as read.
        """
        cmd = {
            "type": "live_notifications_mark_read",
            "uuid": self.api.generate_uuid(),
            "args": {"id": id},
        }
        self.queue.append(cmd)

    def mark_read_all(self):
        """
        Marks all notifications as read.
        """
        cmd = {
            "type": "live_notifications_mark_read_all",
            "uuid": self.api.generate_uuid(),
        }
        self.queue.append(cmd)

    def mark_unread(self, id):
        """
        Marks notification as unread.
        """
        cmd = {
            "type": "live_notifications_mark_unread",
            "uuid": self.api.generate_uuid(),
            "args": {"id": id},
        }
        self.queue.append(cmd) class LocationsManager(Manager, AllMixin, SyncMixin):

    state_name = "locations"
    object_type = None  # there is no local state associated

    def clear(self):
        """
        Clears the locations.
        """
        cmd = {
            "type": "clear_locations",
            "uuid": self.api.generate_uuid(),
            "args": {},
        }
        self.queue.append(cmd) class NotesManager(GenericNotesManager):

    state_name = "notes"

    def add(self, item_id, content, **kwargs):
        """
        Creates a local item note object.
        """
        obj = models.Note({"item_id": item_id, "content": content}, self.api)
        obj.temp_id = obj["id"] = self.api.generate_uuid()
        obj.data.update(kwargs)
        self.state[self.state_name].append(obj)
        cmd = {
            "type": "note_add",
            "temp_id": obj.temp_id,
            "uuid": self.api.generate_uuid(),
            "args": {key: obj.data[key] for key in obj.data if key != "id"},
        }
        self.queue.append(cmd)
        return obj

    def get(self, note_id):
        """
        Gets an existing note.
        """
        params = {"token": self.token, "note_id": note_id}
        obj = self.api._get("notes/get", params=params)
        if obj and "error" in obj:
            return None
        data = {"notes": []}
        if obj.get("note"):
            data["notes"].append(obj.get("note"))
        self.api._update_state(data)
        return obj class ProjectNotesManager(GenericNotesManager):

    state_name = "project_notes"

    def add(self, project_id, content, **kwargs):
        """
        Creates a local project note object.
        """
        obj = models.ProjectNote(
            {"project_id": project_id, "content": content}, self.api
        )
        obj.temp_id = obj["id"] = self.api.generate_uuid()
        obj.data.update(kwargs)
        self.state[self.state_name].append(obj)
        cmd = {
            "type": "note_add",
            "temp_id": obj.temp_id,
            "uuid": self.api.generate_uuid(),
            "args": {key: obj.data[key] for key in obj.data if key != "id"},
        }
        self.queue.append(cmd)
        return obj

    def get(self, note_id):
        """
        Gets an existing project note.
        """
        params = {"token": self.token, "note_id": note_id}
        obj = self.api._get("notes/get", params=params)
        if obj and "error" in obj:
            return None
        data = {"project_notes": []}
        if obj.get("note"):
            data["project_notes"].append(obj.get("note"))
        self.api._update_state(data)
        return obj class ProjectsManager(Manager, AllMixin, GetByIdMixin, SyncMixin):

    state_name = "projects"
    object_type = "project"

    def add(self, name, **kwargs):
        """
        Creates a local project object.
        """
        obj = models.Project({"name": name}, self.api)
        obj.temp_id = obj["id"] = "$" + self.api.generate_uuid()
        obj.data.update(kwargs)
        self.state[self.state_name].append(obj)
        cmd = {
            "type": "project_add",
            "temp_id": obj.temp_id,
            "uuid": self.api.generate_uuid(),
            "args": {key: obj.data[key] for key in obj.data if key != "id"},
        }
        self.queue.append(cmd)
        return obj

    def update(self, project_id, **kwargs):
        """
        Updates a project remotely.
        """
        obj = self.get_by_id(project_id)
        if obj:
            obj.data.update(kwargs)

        args = {"id": project_id}
        args.update(kwargs)
        cmd = {
            "type": "project_update",
            "uuid": self.api.generate_uuid(),
            "args": args,
        }
        self.queue.append(cmd)

    def delete(self, project_id):
        """
        Deletes a project remotely.
        """
        cmd = {
            "type": "project_delete",
            "uuid": self.api.generate_uuid(),
            "args": {"id": project_id},
        }
        self.queue.append(cmd)

    def archive(self, project_id):
        """
        Marks project as archived remotely.
        """
        cmd = {
            "type": "project_archive",
            "uuid": self.api.generate_uuid(),
            "args": {"id": project_id},
        }
        self.queue.append(cmd)

    def unarchive(self, project_id):
        """
        Marks project as unarchived remotely.
        """
        cmd = {
            "type": "project_unarchive",
            "uuid": self.api.generate_uuid(),
            "args": {"id": project_id},
        }
        self.queue.append(cmd)

    def move(self, project_id, parent_id):
        """
        Moves project to another parent.
        """
        args = {
            "id": project_id,
            "parent_id": parent_id,
        }
        cmd = {"type": "project_move", "uuid": self.api.generate_uuid(), "args": args}
        self.queue.append(cmd)

    def reorder(self, projects):
        """
        Updates the child_order of the specified projects.
        """
        cmd = {
            "type": "project_reorder",
            "uuid": self.api.generate_uuid(),
            "args": {"projects": projects},
        }
        self.queue.append(cmd)

    def share(self, project_id, email):
        """
        Shares a project with a user.
        """
        cmd = {
            "type": "share_project",
            "temp_id": self.api.generate_uuid(),
            "uuid": self.api.generate_uuid(),
            "args": {"project_id": project_id, "email": email},
        }
        self.queue.append(cmd)

    def get_archived(self):
        """
        Returns archived projects.
        """
        params = {"token": self.token}
        return self.api._get("projects/get_archived", params=params)

    def get_data(self, project_id):
        """
        Returns a project's uncompleted items.
        """
        params = {"token": self.token, "project_id": project_id}
        return self.api._get("projects/get_data", params=params)

    def get(self, project_id):
        """
        Gets an existing project.
        """
        params = {"token": self.token, "project_id": project_id}
        obj = self.api._get("projects/get", params=params)
        if obj and "error" in obj:
            return None
        data = {"projects": [], "project_notes": []}
        if obj.get("project"):
            data["projects"].append(obj.get("project"))
        if obj.get("notes"):
            data["project_notes"] += obj.get("notes")
        self.api._update_state(data)
        return obj class QuickManager(Manager):
    def add(self, text, **kwargs):
        """
        Quick add task implementation.
        """
        params = {"token": self.token, "text": text}
        params.update(kwargs)
        return self.api._get("quick/add", params=params) class RemindersManager(Manager, AllMixin, GetByIdMixin, SyncMixin):

    state_name = "reminders"
    object_type = "reminder"

    def add(self, item_id, **kwargs):
        """
        Creates a local reminder object.
        """
        obj = models.Reminder({"item_id": item_id}, self.api)
        obj.temp_id = obj["id"] = self.api.generate_uuid()
        obj.data.update(kwargs)
        self.state[self.state_name].append(obj)
        cmd = {
            "type": "reminder_add",
            "temp_id": obj.temp_id,
            "uuid": self.api.generate_uuid(),
            "args": {key: obj.data[key] for key in obj.data if key != "id"},
        }
        self.queue.append(cmd)
        return obj

    def update(self, reminder_id, **kwargs):
        """
        Updates a reminder remotely.
        """
        args = {"id": reminder_id}
        args.update(kwargs)
        cmd = {
            "type": "reminder_update",
            "uuid": self.api.generate_uuid(),
            "args": args,
        }
        self.queue.append(cmd)

    def delete(self, reminder_id):
        """
        Deletes a reminder remotely.
        """
        cmd = {
            "type": "reminder_delete",
            "uuid": self.api.generate_uuid(),
            "args": {"id": reminder_id},
        }
        self.queue.append(cmd)

    def get(self, reminder_id):
        """
        Gets an existing reminder.
        """
        params = {"token": self.token, "reminder_id": reminder_id}
        obj = self.api._get("reminders/get", params=params)
        if obj and "error" in obj:
            return None
        data = {"reminders": []}
        if obj.get("reminder"):
            data["reminders"].append(obj.get("reminder"))
        self.api._update_state(data)
        return obj class SectionsManager(Manager, AllMixin, GetByIdMixin, SyncMixin):

    state_name = "sections"
    object_type = "section"

    def add(self, name, project_id, **kwargs):
        """
        Creates a local section object.
        """
        obj = models.Section({"name": name, "project_id": project_id}, self.api)
        obj.temp_id = obj["id"] = self.api.generate_uuid()
        obj.data.update(kwargs)
        self.state[self.state_name].append(obj)
        cmd = {
            "type": "section_add",
            "temp_id": obj.temp_id,
            "uuid": self.api.generate_uuid(),
            "args": {key: obj.data[key] for key in obj.data if key != "id"},
        }
        self.queue.append(cmd)
        return obj

    def update(self, section_id, **kwargs):
        """
        Updates a section remotely.
        """
        args = {"id": section_id}
        args.update(kwargs)
        cmd = {
            "type": "section_update",
            "uuid": self.api.generate_uuid(),
            "args": args,
        }
        self.queue.append(cmd)

    def delete(self, section_id):
        """
        Delete a section remotely.
        """
        cmd = {
            "type": "section_delete",
            "uuid": self.api.generate_uuid(),
            "args": {"id": section_id},
        }
        self.queue.append(cmd)

    def move(self, section_id, project_id):
        """
        Moves section to another project remotely.
        """
        cmd = {
            "type": "section_move",
            "uuid": self.api.generate_uuid(),
            "args": {"id": section_id, "project_id": project_id},
        }
        self.queue.append(cmd)

    def archive(self, section_id, **kwargs):
        """
        Marks section as archived remotely.
        """
        args = {
            "id": section_id,
        }
        if kwargs.get("date_archived"):
            args["date_archived"] = kwargs.get("date_archived")
        cmd = {
            "type": "section_archive",
            "uuid": self.api.generate_uuid(),
            "args": args,
        }
        self.queue.append(cmd)

    def unarchive(self, section_id):
        """
        Marks section as unarchived remotely.
        """
        cmd = {
            "type": "section_unarchive",
            "uuid": self.api.generate_uuid(),
            "args": {"id": section_id},
        }
        self.queue.append(cmd)

    def reorder(self, sections):
        """
        Updates the section_order of the specified sections.
        """
        cmd = {
            "type": "section_reorder",
            "uuid": self.api.generate_uuid(),
            "args": {"sections": sections},
        }
        self.queue.append(cmd)

    def get(self, section_id):
        """
        Gets an existing section.
        """
        params = {"token": self.token, "section_id": section_id}
        obj = self.api._get("sections/get", params=params)
        if obj and "error" in obj:
            return None
        data = {"sections": []}
        if obj.get("section"):
            data["sections"].append(obj.get("section"))
        self.api._update_state(data)
        return obj class TemplatesManager(Manager):
    def import_into_project(self, project_id, filename, **kwargs):
        """
        Imports a template into a project.
        """
        data = {"token": self.token, "project_id": project_id}
        data.update(kwargs)
        files = {"file": open(filename, "r")}
        return self.api._post("templates/import_into_project", data=data, files=files)

    def export_as_file(self, project_id, **kwargs):
        """
        Exports a template as a file.
        """
        data = {"token": self.token, "project_id": project_id}
        data.update(kwargs)
        return self.api._post("templates/export_as_file", data=data)

    def export_as_url(self, project_id, **kwargs):
        """
        Exports a template as a URL.
        """
        data = {"token": self.token, "project_id": project_id}
        data.update(kwargs)
        return self.api._post("templates/export_as_url", data=data) class UploadsManager(Manager):
    def add(self, filename, **kwargs):
        """
        Uploads a file.

        param filename: (str) name of file to upload
        """
        data = {"token": self.token}
        data.update(kwargs)
        files = {"file": open(filename, "rb")}
        return self.api._post("uploads/add", data=data, files=files)

    def get(self, **kwargs):
        """
        Returns all user's uploads.

        kwargs:
            limit: (int, optional) number of results (1-50)
            last_id: (int, optional) return results with id<last_id
        """
        params = {"token": self.token}
        params.update(kwargs)
        return self.api._get("uploads/get", params=params)

    def delete(self, file_url):
        """
        Deletes upload.

        param file_url: (str) uploaded file URL
        """
        params = {"token": self.token, "file_url": file_url}
        return self.api._get("uploads/delete", params=params) class UserManager(Manager):
    def update(self, **kwargs):
        """
        Updates the user data.
        """
        cmd = {
            "type": "user_update",
            "uuid": self.api.generate_uuid(),
            "args": kwargs,
        }
        self.queue.append(cmd)

    def update_goals(self, **kwargs):
        """
        Updates the user's karma goals.
        """
        cmd = {
            "type": "update_goals",
            "uuid": self.api.generate_uuid(),
            "args": kwargs,
        }
        self.queue.append(cmd)

    def sync(self):
        return self.api.sync()

    def get(self, key=None, default=None):
        ret = self.state["user"]
        if key is not None:
            ret = ret.get(key, default)
        return ret

    def get_id(self):
        return self.state["user"]["id"]

    def login(self, email, password):
        """
        Logins user, and returns the response received by the server.
        """
        data = self.api._post("user/login", data={"email": email, "password": password})
        if "token" in data:
            self.api.token = data["token"]
        return data

    def login_with_google(self, email, oauth2_token, **kwargs):
        """
        Logins user with Google account, and returns the response received by
        the server.

        """
        data = {"email": email, "oauth2_token": oauth2_token}
        data.update(kwargs)
        data = self.api._post("user/login_with_google", data=data)
        if "token" in data:
            self.api.token = data["token"]
        return data

    def register(self, email, full_name, password, **kwargs):
        """
        Registers a new user.
        """
        data = {"email": email, "full_name": full_name, "password": password}
        data.update(kwargs)
        data = self.api._post("user/register", data=data)
        if "token" in data:
            self.api.token = data["token"]
        return data

    def delete(self, current_password, **kwargs):
        """
        Deletes an existing user.
        """
        params = {"token": self.token, "current_password": current_password}
        params.update(kwargs)
        return self.api._get("user/delete", params=params)

    def update_notification_setting(self, notification_type, service, dont_notify):
        """
        Updates the user's notification settings.
        """
        return self.api._post(
            "user/update_notification_setting",
            data={
                "token": self.token,
                "notification_type": notification_type,
                "service": service,
                "dont_notify": dont_notify,
            },
        ) class UserSettingsManager(Manager):
    def update(self, **kwargs):
        """
        Updates the user's settings.
        """
        cmd = {
            "type": "user_settings_update",
            "uuid": self.api.generate_uuid(),
            "args": kwargs,
        }
        self.queue.append(cmd)
import datetime
import functools
import json
import os
import uuid
import requests
from todoist import models
from todoist.managers.activity import ActivityManager
from todoist.managers.archive import (
    ItemsArchiveManagerMaker,
    SectionsArchiveManagerMaker,
)
from todoist.managers.backups import BackupsManager
from todoist.managers.biz_invitations import BizInvitationsManager
from todoist.managers.business_users import BusinessUsersManager
from todoist.managers.collaborator_states import CollaboratorStatesManager
from todoist.managers.collaborators import CollaboratorsManager
from todoist.managers.completed import CompletedManager
from todoist.managers.emails import EmailsManager
from todoist.managers.filters import FiltersManager
from todoist.managers.invitations import InvitationsManager
from todoist.managers.items import ItemsManager
from todoist.managers.labels import LabelsManager
from todoist.managers.live_notifications import LiveNotificationsManager
from todoist.managers.locations import LocationsManager
from todoist.managers.notes import NotesManager, ProjectNotesManager
from todoist.managers.projects import ProjectsManager
from todoist.managers.quick import QuickManager
from todoist.managers.reminders import RemindersManager
from todoist.managers.sections import SectionsManager
from todoist.managers.templates import TemplatesManager
from todoist.managers.uploads import UploadsManager
from todoist.managers.user import UserManager
from todoist.managers.user_settings import UserSettingsManager



DEFAULT_API_VERSION = "v8"


class SyncError(Exception):
    pass


class TodoistAPI(object):
    """
    Implements the API that makes it possible to interact with a Todoist user
    account and its data.
    """

    _serialize_fields = ("token", "api_endpoint", "sync_token", "state", "temp_ids")

    @classmethod
    def deserialize(cls, data):
        obj = cls()
        for key in cls._serialize_fields:
            if key in data:
                setattr(obj, key, data[key])
        return obj

    def __init__(
        self,
        token="",
        api_endpoint="https://api.todoist.com",
        api_version=DEFAULT_API_VERSION,
        session=None,
        cache="~/.todoist-sync/",
    ):
        self.api_endpoint = api_endpoint
        self.api_version = api_version
        self.reset_state()
        self.token = token  # User's API token
        self.temp_ids = {}  # Mapping of temporary ids to real ids
        self.queue = []  # Requests to be sent are appended here
        self.session = session or requests.Session()  # Session instance for requests

        # managers
        self.biz_invitations = BizInvitationsManager(self)
        self.collaborators = CollaboratorsManager(self)
        self.collaborator_states = CollaboratorStatesManager(self)
        self.filters = FiltersManager(self)
        self.invitations = InvitationsManager(self)
        self.items = ItemsManager(self)
        self.labels = LabelsManager(self)
        self.live_notifications = LiveNotificationsManager(self)
        self.locations = LocationsManager(self)
        self.notes = NotesManager(self)
        self.projects = ProjectsManager(self)
        self.project_notes = ProjectNotesManager(self)
        self.reminders = RemindersManager(self)
        self.sections = SectionsManager(self)
        self.user = UserManager(self)
        self.user_settings = UserSettingsManager(self)

        self.activity = ActivityManager(self)
        self.backups = BackupsManager(self)
        self.business_users = BusinessUsersManager(self)
        self.completed = CompletedManager(self)
        self.emails = EmailsManager(self)
        self.quick = QuickManager(self)
        self.templates = TemplatesManager(self)
        self.uploads = UploadsManager(self)

        self.items_archive = ItemsArchiveManagerMaker(self)

        self.sections_archive = SectionsArchiveManagerMaker(self)