[Python] Converting powershell script to python and facing error 'can only concatenate str...

Discussão em 'Python' iniciado por Stack, Maio 9, 2025.

  1. Stack

    Stack Membro Participativo

    "Below is the output getting post running the code where in archiving function getting 'can only concatenate str (not "NoneType") to str' error , been using 'office365-rest-python-client' module for executing the code ,in this code i am connecting with microsoft sharepoint in order to retrieve and push data, I will be pasting error as well as code below"

    'code using '


    from office365.sharepoint.client_context import ClientContext
    from office365.runtime.auth.client_credential import ClientCredential
    from office365.sharepoint.listitems.caml.query import CamlQuery
    from office365.sharepoint.listitems.creation_information import ListItemCreationInformation
    import csv

    # ---- Step 1: Authenticate to SharePoint ----
    def authenticate(site_url: str, client_id: str, client_secret: str) -> ClientContext:
    """
    Authenticates using client ID & secret and returns a ClientContext.
    """
    creds = ClientCredential(client_id, client_secret)
    ctx = ClientContext(site_url).with_credentials(creds)
    web = ctx.web
    ctx.load(web)
    ctx.execute_query()
    print("Step 1 completed: Authentication successful")
    return ctx

    # ---- Step 2: Retrieve all items from the target list ----
    def get_list_items(ctx: ClientContext, list_name: str):
    """
    Fetches all items from the given SharePoint list.
    """
    sp_list = ctx.web.lists.get_by_title(list_name)
    caml = CamlQuery.create_all_items_query()
    items = sp_list.get_items(caml)
    ctx.load(items)
    ctx.execute_query()
    print(f"Step 2 completed: Retrieved {len(items)} items from '{list_name}'")
    return items

    # ---- Step 3: Read and parse the CSV file ----
    def read_csv_rows(csv_path: str):
    """
    Reads the CSV and returns a list of row-dicts.
    """
    with open(csv_path, newline="", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    rows = list(reader)
    print(f"Step 3 completed: Loaded {len(rows)} rows from CSV")
    return rows

    # ---- Step 4: Compare CSV vs. SharePoint items ----
    def compare_csv_to_sp(sp_items, csv_rows):
    """
    Builds identifier lists and finds:
    • new_items: in CSV but not in SP
    • deleted_items: in SP but not in CSV
    • project_diff: projects present only in one source
    """
    sp_ids = [
    f"{item.properties['ProjectNumber'].replace('_',' ')}_{item.properties['RevisionNumber']}"
    for item in sp_items
    ]
    sp_projects = [item.properties['ProjectNumber'].replace('_',' ') for item in sp_items]

    csv_ids = [f"{row['Project Name']}_{row['Revision Number']}" for row in csv_rows]
    csv_projects = [row['Project Name'] for row in csv_rows]

    new_items = set(csv_ids) - set(sp_ids)
    deleted_items = set(sp_ids) - set(csv_ids)
    project_diff = set(sp_projects).symmetric_difference(set(csv_projects))

    print("Step 4 completed: Comparison done")
    print(f" • New to upload: {len(new_items)}")
    print(f" • To archive/delete: {len(deleted_items)}")
    print(f" • Projects with mismatched revisions: {len(project_diff)}")
    return {
    "new_items": new_items,
    "deleted_items": deleted_items,
    "project_diff": project_diff
    }


    # ---- Step 5: Archive outdated SharePoint items ----
    def archive_sp_items(ctx: ClientContext, list_name: str, archive_list_name: str, sp_items, comparison: dict):
    """
    Archives items removed or updated in CSV, then deletes them from the original list.
    Uses separate load+execute_query calls to populate root_folder.serverRelativeUrl.
    """
    # 5a) Load archive list
    archive_list = ctx.web.lists.get_by_title(archive_list_name)
    ctx.load(archive_list)
    ctx.execute_query()
    print(f" • Archive list '{archive_list_name}' loaded")

    # 5a-continued) Load its RootFolder separately
    root_folder = archive_list.root_folder
    ctx.load(root_folder)
    ctx.execute_query()
    print(f"Step 5a completed: root_folder.serverRelativeUrl = '{root_folder.serverRelativeUrl}'")

    # 5b) Identify items to archive/delete
    to_archive = []
    for item in sp_items:
    proj = item.properties['ProjectNumber'].replace('_',' ')
    rev = str(item.properties['RevisionNumber'])
    ident = f"{proj}_{rev}"
    if ident in comparison['deleted_items'] or proj in comparison['project_diff']:
    to_archive.append(item)
    print(f"Step 5b completed: Identified {len(to_archive)} items to archive/delete")

    # 5c) Archive + delete loop
    for item in to_archive:
    props = {
    "ProjectNumber": item.properties['ProjectNumber'],
    "RevisionNumber": str(item.properties['RevisionNumber']),
    "PBDSFlag": item.properties.get('PBDSFlag', ""),
    "ProjectDescription": item.properties.get('ProjectDescription', "")
    }
    # Create & populate archive item
    item_info = ListItemCreationInformation()
    arc_item = archive_list.add_item(item_info)
    for fld, val in props.items():
    arc_item.set_property(fld, val)
    arc_item.update()
    ctx.execute_query()
    print(f" • Archived: {props['ProjectNumber']}_{props['RevisionNumber']}")

    # Delete original
    orig_list = ctx.web.lists.get_by_title(list_name)
    ctx.load(orig_list)
    ctx.execute_query()
    orig_item = orig_list.get_item_by_id(item.properties['Id'])
    orig_item.delete_object()
    ctx.execute_query()
    print(f" • Deleted original item ID {item.properties['Id']}")

    print("Step 5 completed: Archive + delete operations done")


    if __name__ == "__main__":
    site_url = "cannot disclose"
    client_id = "cannot disclose"
    client_secret = "cannot disclose"
    list_name = "PBDS"
    archive_list_name = "PBDSArch"
    csv_path = r"E:\Anaplan\Speedit\PROD\CORDILLERA\Workflow\inbound\Export - Workflow - PB DS Flag.csv"

    ctx = authenticate(site_url, client_id, client_secret)
    sp_items = get_list_items(ctx, list_name)
    csv_rows = read_csv_rows(csv_path)
    comparison = compare_csv_to_sp(sp_items, csv_rows)
    archive_sp_items(ctx, list_name, archive_list_name, sp_items, comparison)





    ERROR Getting

    Step 1 completed: Authentication successful
    Step 2 completed: Retrieved 2 items from 'PBDS'
    Step 3 completed: Loaded 0 rows from CSV
    Step 4 completed: Comparison done
    • New to upload: 0
    • To archive/delete: 2
    • Projects with mismatched revisions: 2
    • Archive list 'PBDSArch' loaded
    Step 5a completed: root_folder.serverRelativeUrl = '/sites/SpeedITSite/Lists/PBDSArch'
    Step 5b completed: Identified 2 items to archive/delete
    Traceback (most recent call last): File "e:\\Anaplan\\Speedit\\PROD\\CORDILLERA\\Workflow\\Jobs\\Pnp PowerShell Scripts\\Test\\p33.py", line 145, in \<module\> archive_sp_items(ctx, list_name, archive_list_name, sp_items, comparison) File "e:\\Anaplan\\Speedit\\PROD\\CORDILLERA\\Workflow\\Jobs\\Pnp PowerShell Scripts\\Test\\p33.py", line 119, in archive_sp_items ctx.execute_query() File "C:\\Users\\UNIL-ADMN-PRANSHU.RA\\AppData\\Roaming\\Python\\Python311\\site-packages\\office365\\runtime\\client_runtime_context.py", line 173, in execute_query self.pending_request().execute_query(qry) File "C:\\Users\\UNIL-ADMN-PRANSHU.RA\\AppData\\Roaming\\Python\\Python311\\site-packages\\office365\\runtime\\client_request.py", line 39, in execute_query self.afterExecute.notify(response) File "C:\\Users\\UNIL-ADMN-PRANSHU.RA\\AppData\\Roaming\\Python\\Python311\\site-packages\\office365\\runtime\\types\\event_handler.py", line 41, in notify listener(\*args, \*\*kwargs) File "C:\\Users\\UNIL-ADMN-PRANSHU.RA\\AppData\\Roaming\\Python\\Python311\\site-packages\\office365\\runtime\\client_runtime_context.py", line 141, in \_process_response action(resp if include_response else query.return_type) File "C:\\Users\\UNIL-ADMN-PRANSHU.RA\\AppData\\Roaming\\Python\\Python311\\site-packages\\office365\\runtime\\client_object.py", line 201, in \_after_loaded action(\*args, \*\*kwargs) File "C:\\Users\\UNIL-ADMN-PRANSHU.RA\\AppData\\Roaming\\Python\\Python311\\site-packages\\office365\\sharepoint\\lists\\list.py", line 614, in \_add_item self.context.base_url + self.root_folder.serverRelativeUrl \~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~^\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~\~
    TypeError: can only concatenate str (not "NoneType") to str

    Continue reading...

Compartilhe esta Página