def upload_file_to_row_property(client, row, path, property_name): path = str(path) mimetype = mimetypes.guess_type(path)[0] or "text/plain" filename = os.path.split(path)[-1] data = client.post( "getUploadFileUrl", { "bucket": "secure", "name": filename, "contentType": mimetype }, ).json() # Return url, signedGetUrl, signedPutUrl mangled_property_name = [ e["id"] for e in row.schema if e["name"] == property_name ][0] with open(path, "rb") as f: response = requests.put(data["signedPutUrl"], data=f, headers={"Content-type": mimetype}) response.raise_for_status() simpleurl = data['signedGetUrl'].split('?')[0] op1 = build_operation(id=row.id, path=["properties", mangled_property_name], args=[[filename, [["a", simpleurl]]]], table="block", command="set") file_id = simpleurl.split("/")[-2] op2 = build_operation(id=row.id, path=["file_ids"], args={"id": file_id}, table="block", command="listAfter") client.submit_transaction([op1, op2])
def move_to(self, target_block: "Block", position="last-child"): assert position in ["first-child", "last-child", "before", "after"] if "child" in position: new_parent_id = target_block.id new_parent_table = "block" else: new_parent_id = target_block.get("parent_id") new_parent_table = target_block.get("parent_table") if position in ["first-child", "before"]: list_command = "listBefore" else: list_command = "listAfter" list_args = {"id": self.id} if position in ["before", "after"]: list_args[position] = target_block.id with self._client.as_atomic_transaction(): # First, remove the node, before we re-insert and re-activate it at the target location self.remove() if not self.is_alias: # Set the parent_id of the moving block to the new parent, and mark it as active again self._client.submit_transaction( build_operation( id=self.id, path=[], args={ "alive": True, "parent_id": new_parent_id, "parent_table": new_parent_table, }, command="update", )) else: self._alias_parent = new_parent_id # Add the moving block's ID to the "content" list of the new parent self._client.submit_transaction( build_operation( id=new_parent_id, path=["content"], args=list_args, command=list_command, )) # update the local block cache to reflect the updates self._client.refresh_records(block=[ self.id, self.get("parent_id"), target_block.id, target_block.get("parent_id"), ])
def create_record(self, table: str, parent: Record, **kwargs): """ Create new record. Arguments --------- table : str Table value. parent : Record Parent for the newly created record. Returns ------- str ID of newly created record. """ # make up a new UUID; apparently we get to choose our own! record_id = str(uuid.uuid4()) child_list_key = kwargs.get("child_list_key") or parent.child_list_key args = { "id": record_id, "version": 1, "alive": True, "created_by": self.current_user.id, "created_time": now(), "parent_id": parent.id, "parent_table": parent._table, **kwargs, } with self.as_atomic_transaction(): # create the new record self.submit_transaction( build_operation( args=args, command="set", id=record_id, path=[], table=table ) ) # add the record to the content list of the parent, if needed if child_list_key: self.submit_transaction( build_operation( id=parent.id, path=[child_list_key], args={"id": record_id}, command="listAfter", table=parent._table, ) ) return record_id
def remove(self, permanently: bool = False): """ Remove the node from its parent, and mark it as inactive. This corresponds to what happens in the Notion UI when you delete a block. Note that it doesn't *actually* delete it, just orphan it, unless `permanently` is set to True, in which case we make an extra call to hard-delete. Arguments --------- permanently : bool, optional Whether or not to hard-delete the block. Defaults to False. """ if self.is_alias: # only remove it from the alias parent's content list return self._client.submit_transaction( build_operation( id=self._alias_parent, path="content", args={"id": self.id}, command="listRemove", )) with self._client.as_atomic_transaction(): # Mark the block as inactive self._client.submit_transaction( build_operation(id=self.id, path=[], args={"alive": False}, command="update")) # Remove the block's ID from a list on its parent, if needed if self.parent.child_list_key: self._client.submit_transaction( build_operation( id=self.parent.id, path=[self.parent.child_list_key], args={"id": self.id}, command="listRemove", table=self.parent._table, )) if permanently: self._client.post("deleteBlocks", { "blockIds": [self.id], "permanentlyDelete": True }) del self._client._store._values["block"][self.id]
def add_alias(self, block: Block) -> Block: """ Adds an alias to the provided `block`, i.e. adds the block's ID to the parent's content list, but doesn't change the block's parent_id. Arguments --------- block : Block Instance of block to alias. Returns ------- Block Aliased block. """ # add the block to the content list of the parent self._client.submit_transaction( build_operation( id=self._parent.id, path=[self.child_list_key], args={"id": block.id}, command="listAfter", )) return self._get_block(block.id)
def remove(self): # Mark the block as inactive self._client.submit_transaction( build_operation(id=self.id, path=[], args={"alive": False}, command="update"))
def set(self, path, value): """ Set a specific `value` under the specific `path` on the record's data structure on the server. Arguments --------- path : list of str or str Specifies the field to which set the value. value Value to set under provided path. """ self._client.submit_transaction( build_operation(id=self.id, path=path, args=value, table=self._table))
def change_lock(self, locked: bool): """ Set or free the lock according to the value passed in `locked`. Arguments --------- locked : bool Whether or not to lock the block. """ args = dict(block_locked=locked, block_locked_by=self._client.current_user.id) with self._client.as_atomic_transaction(): self._client.submit_transaction( build_operation( id=self.id, path=["format"], args=args, command="update", )) # update the local block cache to reflect the updates self._client.refresh_records(block=[self.id])