diff --git a/examples/onedrive/files/get_by_abs_url.py b/examples/onedrive/files/get_by_abs_url.py index 7d8fa9d28..64a9fefbc 100644 --- a/examples/onedrive/files/get_by_abs_url.py +++ b/examples/onedrive/files/get_by_abs_url.py @@ -5,7 +5,7 @@ from tests import test_team_site_url from tests.graph_case import acquire_token_by_username_password -file_abs_url = "{0}/Shared Documents/big_buck_bunny.mp4".format(test_team_site_url) +file_abs_url = "{0}/Shared Documents/Financial Sample.xlsx".format(test_team_site_url) client = GraphClient(acquire_token_by_username_password) file_item = client.shares.by_url(file_abs_url).drive_item.get().execute_query() diff --git a/examples/onedrive/shares/read_workbook.py b/examples/onedrive/shares/read_workbook.py new file mode 100644 index 000000000..1f6b5e53e --- /dev/null +++ b/examples/onedrive/shares/read_workbook.py @@ -0,0 +1,14 @@ +""" +Get workbook +""" +from office365.graph_client import GraphClient +from tests import test_team_site_url +from tests.graph_case import acquire_token_by_username_password + +file_abs_url = "{0}/Shared Documents/Financial Sample.xlsx".format(test_team_site_url) + +client = GraphClient(acquire_token_by_username_password) +drive_item = client.shares.by_url(file_abs_url).drive_item.get().execute_query() +worksheets = drive_item.workbook.worksheets.get().execute_query() +for ws in worksheets: + print(ws) diff --git a/examples/onenote/__init__.py b/examples/onenote/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/examples/onenote/create_notebook.py b/examples/onenote/create_notebook.py index 638e59dcc..4d626e59d 100644 --- a/examples/onenote/create_notebook.py +++ b/examples/onenote/create_notebook.py @@ -1,7 +1,7 @@ """ Demonstrates how to create a new OneNote notebook -https://learn.microsoft.com/en-us/graph/api/onenote-post-notebooks?view=graph-rest-1.0&tabs=http +https://learn.microsoft.com/en-us/graph/api/onenote-post-notebooks?view=graph-rest-1.0 """ from office365.graph_client import GraphClient diff --git a/office365/directory/applications/template.py b/office365/directory/applications/template.py index e74bb28de..cb61f5e86 100644 --- a/office365/directory/applications/template.py +++ b/office365/directory/applications/template.py @@ -1,3 +1,5 @@ +from typing import Optional + from office365.entity import Entity from office365.runtime.client_result import ClientResult from office365.runtime.queries.service_operation import ServiceOperationQuery @@ -25,11 +27,8 @@ def instantiate(self, display_name): @property def display_name(self): - """ - The name of the application. - - :rtype: str or None - """ + # type: () -> Optional[str] + """The name of the application.""" return self.properties.get("displayName", None) @property @@ -44,9 +43,7 @@ def categories(self): @property def supported_provisioning_types(self): - """ - The list of provisioning modes supported by this application - """ + """The list of provisioning modes supported by this application""" return self.properties.get("supportedProvisioningTypes", StringCollection()) @property diff --git a/office365/directory/protection/riskyusers/history_item.py b/office365/directory/protection/riskyusers/history_item.py index 221ddbe63..2542825d0 100644 --- a/office365/directory/protection/riskyusers/history_item.py +++ b/office365/directory/protection/riskyusers/history_item.py @@ -1,3 +1,5 @@ +from typing import Optional + from office365.directory.protection.riskyusers.activity import RiskUserActivity from office365.directory.protection.riskyusers.risky_user import RiskyUser @@ -13,7 +15,6 @@ def activity(self): @property def initiated_by(self): - """The ID of actor that does the operation. - :rtype: str - """ + # type: () -> Optional[str] + """The ID of actor that does the operation.""" return self.properties.get("initiatedBy", None) diff --git a/office365/onedrive/driveitems/driveItem.py b/office365/onedrive/driveitems/driveItem.py index d697dc585..89d1b45eb 100644 --- a/office365/onedrive/driveitems/driveItem.py +++ b/office365/onedrive/driveitems/driveItem.py @@ -795,6 +795,12 @@ def subscriptions(self): ), ) + def set_property(self, name, value, persist_changes=True): + super(DriveItem, self).set_property(name, value, persist_changes) + if name == "parentReference": + self._resource_path.parent.patch(self.parent_reference.driveId) + return self + def get_property(self, name, default_value=None): # type: (str, P_T) -> P_T if default_value is None: diff --git a/office365/onedrive/internal/paths/shared.py b/office365/onedrive/internal/paths/shared.py index 143422495..75e74f5d7 100644 --- a/office365/onedrive/internal/paths/shared.py +++ b/office365/onedrive/internal/paths/shared.py @@ -1,22 +1,27 @@ import base64 from office365.runtime.paths.resource_path import ResourcePath +from office365.runtime.paths.v4.entity import EntityPath def _url_to_shared_token(url): - """ - Converts url into shared token - :param str url: - """ + # type: (str) -> str + """Converts url into shared token""" value = base64.b64encode(url.encode("ascii")).decode("ascii") if value.endswith("="): value = value[:-1] return "u!" + value.replace("/", "_").replace("+", "-") -class SharedPath(ResourcePath): +class SharedPath(EntityPath): """Shared token path""" + def patch(self, key): + self._key = "items" + self._parent = ResourcePath(key, ResourcePath("drives")) + self.__class__ = ResourcePath + return self + @property def segment(self): return _url_to_shared_token(self._key) diff --git a/office365/onedrive/shares/drive_item.py b/office365/onedrive/shares/drive_item.py index 2c321bd69..719c04f8e 100644 --- a/office365/onedrive/shares/drive_item.py +++ b/office365/onedrive/shares/drive_item.py @@ -8,6 +8,7 @@ from office365.onedrive.permissions.permission import Permission from office365.onedrive.sites.site import Site from office365.runtime.paths.resource_path import ResourcePath +from office365.runtime.paths.v4.entity import EntityPath class SharedDriveItem(BaseItem): @@ -47,7 +48,7 @@ def drive_item(self): """Used to access the underlying driveItem""" return self.properties.get( "driveItem", - DriveItem(self.context, ResourcePath("driveItem", self.resource_path)), + DriveItem(self.context, EntityPath("driveItem", self.resource_path)), ) @property diff --git a/office365/onedrive/workbooks/names/named_item.py b/office365/onedrive/workbooks/names/named_item.py index 70e84f6a7..0609b6044 100644 --- a/office365/onedrive/workbooks/names/named_item.py +++ b/office365/onedrive/workbooks/names/named_item.py @@ -1,5 +1,9 @@ +from typing import Optional + from office365.entity import Entity +from office365.onedrive.workbooks.ranges.range import WorkbookRange from office365.runtime.paths.resource_path import ResourcePath +from office365.runtime.queries.function import FunctionQuery class WorkbookNamedItem(Entity): @@ -7,18 +11,26 @@ class WorkbookNamedItem(Entity): (as seen in the type below), range object, reference to a range. This object can be used to obtain range object associated with names.""" + def range(self): + """Returns the range object that is associated with the name. Throws an exception if the named item's type + isn't a range.""" + return_type = WorkbookRange( + self.context, ResourcePath("range", self.resource_path) + ) + qry = FunctionQuery(self, "range", return_type=return_type) + self.context.add_query(qry) + return return_type + @property def name(self): - """The name of the object. Read-only. - :rtype str or None - """ + # type: () -> Optional[str] + """The name of the object.""" return self.properties.get("name", None) @property def comment(self): - """Represents the comment associated with this name. - :rtype str or None - """ + # type: () -> Optional[str] + """Represents the comment associated with this name.""" return self.properties.get("comment", None) @property diff --git a/office365/onedrive/workbooks/ranges/range.py b/office365/onedrive/workbooks/ranges/range.py index 2ca02950f..c355f963c 100644 --- a/office365/onedrive/workbooks/ranges/range.py +++ b/office365/onedrive/workbooks/ranges/range.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import List, Optional from office365.entity import Entity from office365.onedrive.workbooks.ranges.format import WorkbookRangeFormat @@ -6,13 +6,50 @@ from office365.onedrive.workbooks.ranges.view import WorkbookRangeView from office365.runtime.paths.resource_path import ResourcePath from office365.runtime.queries.function import FunctionQuery +from office365.runtime.queries.service_operation import ServiceOperationQuery class WorkbookRange(Entity): """Range represents a set of one or more contiguous cells such as a cell, a row, a column, block of cells, etc.""" + def cell(self, row, column): + """ + Gets the range object containing the single cell based on row and column numbers. The cell can be outside + the bounds of its parent range, so long as it's stays within the worksheet grid. + :param int row: Row number of the cell to be retrieved. Zero-indexed. + :param int column: Column number of the cell to be retrieved. Zero-indexed. + """ + return_type = WorkbookRange(self.context) + params = {"row": row, "column": column} + qry = FunctionQuery(self, "cell", params, return_type=return_type) + self.context.add_query(qry) + return return_type + + def clear(self, apply_to=None): + """Clear range values such as format, fill, and border. + :param str apply_to: + """ + payload = {"applyTo": apply_to} + qry = ServiceOperationQuery(self, "clear", parameters_type=payload) + self.context.add_query(qry) + return self + + def insert(self, shift): + """ + Inserts a cell or a range of cells into the worksheet in place of this range, and shifts the other cells to + make space. Returns a new Range object at the now blank space. + :param str shift: Specifies which way to shift the cells. The possible values are: Down, Right. + """ + return_type = WorkbookRange(self.context) + payload = {"shift": shift} + qry = ServiceOperationQuery( + self, "insert", parameters_type=payload, return_type=return_type + ) + self.context.add_query(qry) + return return_type + def visible_view(self): - """""" + """Get the range visible from a filtered range.""" return_type = WorkbookRangeView(self.context) qry = FunctionQuery(self, "visibleView", return_type=return_type) self.context.add_query(qry) @@ -63,6 +100,20 @@ def sort(self): WorkbookRangeSort(self.context, ResourcePath("sort", self.resource_path)), ) + @property + def values(self): + # type: () -> List + """Represents the raw values of the specified range. The data returned could be of type string, number, + or a boolean. Cell that contains an error returns the error string.""" + return self.properties.get("values", None) + + @property + def value_types(self): + # type: () -> List + """Represents the type of data of each cell. The possible values are: + Unknown, Empty, String, Integer, Double, Boolean, Error.""" + return self.properties.get("valueTypes", None) + @property def worksheet(self): """The worksheet containing the current range""" diff --git a/office365/onedrive/workbooks/tables/table.py b/office365/onedrive/workbooks/tables/table.py index 99f1a9c63..a9b0b66d0 100644 --- a/office365/onedrive/workbooks/tables/table.py +++ b/office365/onedrive/workbooks/tables/table.py @@ -18,14 +18,18 @@ class WorkbookTable(Entity): def data_body_range(self): """Gets the range object associated with the data body of the table.""" - return_type = WorkbookRange(self.context) + return_type = WorkbookRange( + self.context, ResourcePath("dataBodyRange", self.resource_path) + ) qry = FunctionQuery(self, "dataBodyRange", return_type=return_type) self.context.add_query(qry) return return_type def range(self): """Get the range object associated with the entire table.""" - return_type = WorkbookRange(self.context) + return_type = WorkbookRange( + self.context, ResourcePath("range", self.resource_path) + ) qry = FunctionQuery(self, "range", return_type=return_type) self.context.add_query(qry) return return_type diff --git a/office365/runtime/paths/v4/entity.py b/office365/runtime/paths/v4/entity.py index d28108cd7..e1fdfd812 100644 --- a/office365/runtime/paths/v4/entity.py +++ b/office365/runtime/paths/v4/entity.py @@ -1,12 +1,13 @@ +from typing import Optional + +from typing_extensions import Self + from office365.runtime.paths.resource_path import ResourcePath class EntityPath(ResourcePath): def __init__(self, key=None, parent=None, collection=None): - """ - :param str or None key: Entity key - :param ResourcePath or None collection: - """ + # type: (Optional[str], Optional[ResourcePath], Optional[ResourcePath]) -> None super(EntityPath, self).__init__(key, parent) self._collection = collection @@ -26,10 +27,8 @@ def segment(self): return str(self._key or "") def patch(self, key): - """ - Patches path - :type key: str or None - """ + # type: (str) -> Self + """Patches the path""" self._key = key self._parent = self.collection self.__class__ = EntityPath diff --git a/tests/onedrive/test_excel_ranges.py b/tests/onedrive/test_excel_ranges.py index f507a10e3..7966c42c6 100644 --- a/tests/onedrive/test_excel_ranges.py +++ b/tests/onedrive/test_excel_ranges.py @@ -1,5 +1,6 @@ from office365.onedrive.driveitems.driveItem import DriveItem from office365.onedrive.workbooks.names.named_item import WorkbookNamedItem +from office365.onedrive.workbooks.ranges.range import WorkbookRange from tests import create_unique_name from tests.graph_case import GraphTestCase from tests.onedrive.test_excel import upload_excel @@ -8,6 +9,7 @@ class TestExcelRanges(GraphTestCase): excel_file = None # type: DriveItem named_item = None # type: WorkbookNamedItem + range = None # type: WorkbookRange sheet_name = create_unique_name("Sheet") @classmethod @@ -30,3 +32,16 @@ def test1_name_create(self): def test2_names_get(self): result = self.__class__.named_item.get().execute_query() self.assertIsNotNone(result.resource_path) + + def test3_list_range(self): + result = self.__class__.named_item.range().execute_query() + self.assertIsNotNone(result.address) + self.__class__.range = result + + # def test4_insert_range(self): + # result = self.__class__.range.insert("Right").execute_query() + # self.assertIsNotNone(result.address) + + def test5_clear_range(self): + result = self.__class__.range.clear().execute_query() + self.assertIsNotNone(result.address) diff --git a/tests/onedrive/test_permissions.py b/tests/onedrive/test_permissions.py index bca642296..00bca1f1d 100644 --- a/tests/onedrive/test_permissions.py +++ b/tests/onedrive/test_permissions.py @@ -73,7 +73,7 @@ def test7_driveitem_delete_permission(self): perm_to_delete.delete_object().execute_query() def test8_driveitem_grant_access(self): - file_abs_url = "{0}/Shared Documents/big_buck_bunny.mp4".format( + file_abs_url = "{0}/Shared Documents/Financial Sample.xlsx".format( test_team_site_url ) permissions = (