# Copyright (c) 2025 Patrick Motsch # All rights reserved. """ SharePoint operations method module. Handles SharePoint document operations using the SharePoint service. """ import logging from modules.workflows.methods.methodBase import MethodBase from modules.datamodels.datamodelWorkflowActions import WorkflowActionDefinition, WorkflowActionParameter from modules.shared.frontendTypes import FrontendType # Import helpers from .helpers.connection import ConnectionHelper from .helpers.siteDiscovery import SiteDiscoveryHelper from .helpers.documentParsing import DocumentParsingHelper from .helpers.pathProcessing import PathProcessingHelper from .helpers.apiClient import ApiClientHelper # Import actions from .actions.findDocumentPath import findDocumentPath from .actions.readDocuments import readDocuments from .actions.uploadDocument import uploadDocument from .actions.listDocuments import listDocuments from .actions.analyzeFolderUsage import analyzeFolderUsage from .actions.findSiteByUrl import findSiteByUrl from .actions.downloadFileByPath import downloadFileByPath from .actions.copyFile import copyFile from .actions.uploadFile import uploadFile from .actions.getExpensesFromPdf import getExpensesFromPdf logger = logging.getLogger(__name__) class MethodSharepoint(MethodBase): """SharePoint operations methods.""" def __init__(self, services): super().__init__(services) self.name = "sharepoint" self.description = "SharePoint operations methods" # Initialize helper modules self.connection = ConnectionHelper(self) self.siteDiscovery = SiteDiscoveryHelper(self) self.documentParsing = DocumentParsingHelper(self) self.pathProcessing = PathProcessingHelper(self) self.apiClient = ApiClientHelper(self) # RBAC-Integration: Action-Definitionen mit actionId self._actions = { "findDocumentPath": WorkflowActionDefinition( actionId="sharepoint.findDocumentPath", description="Find documents and folders by name/path across sites", dynamicMode=True, parameters={ "connectionReference": WorkflowActionParameter( name="connectionReference", type="str", frontendType=FrontendType.USER_CONNECTION, required=True, description="Microsoft connection label" ), "site": WorkflowActionParameter( name="site", type="str", frontendType=FrontendType.TEXT, required=False, description="Site hint" ), "searchQuery": WorkflowActionParameter( name="searchQuery", type="str", frontendType=FrontendType.TEXT, required=True, description="Search terms or path" ), "maxResults": WorkflowActionParameter( name="maxResults", type="int", frontendType=FrontendType.NUMBER, required=False, default=1000, description="Maximum items to return", validation={"min": 1, "max": 10000} ) }, execute=findDocumentPath.__get__(self, self.__class__) ), "readDocuments": WorkflowActionDefinition( actionId="sharepoint.readDocuments", description="Read documents from SharePoint and extract content/metadata", dynamicMode=True, parameters={ "connectionReference": WorkflowActionParameter( name="connectionReference", type="str", frontendType=FrontendType.USER_CONNECTION, required=True, description="Microsoft connection label" ), "documentList": WorkflowActionParameter( name="documentList", type="List[str]", frontendType=FrontendType.DOCUMENT_REFERENCE, required=False, description="Document list reference(s) containing findDocumentPath result" ), "pathQuery": WorkflowActionParameter( name="pathQuery", type="str", frontendType=FrontendType.TEXT, required=False, description="Direct path query if no documentList (e.g., /sites/SiteName/FolderPath)" ), "includeMetadata": WorkflowActionParameter( name="includeMetadata", type="bool", frontendType=FrontendType.CHECKBOX, required=False, default=True, description="Include metadata" ) }, execute=readDocuments.__get__(self, self.__class__) ), "uploadDocument": WorkflowActionDefinition( actionId="sharepoint.uploadDocument", description="Upload documents to SharePoint", dynamicMode=True, parameters={ "connectionReference": WorkflowActionParameter( name="connectionReference", type="str", frontendType=FrontendType.USER_CONNECTION, required=True, description="Microsoft connection label" ), "documentList": WorkflowActionParameter( name="documentList", type="List[str]", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference(s) to upload. File names are taken from the documents" ), "pathQuery": WorkflowActionParameter( name="pathQuery", type="str", frontendType=FrontendType.TEXT, required=False, description="Direct upload target path if documentList doesn't contain findDocumentPath result (e.g., /sites/SiteName/FolderPath)" ) }, execute=uploadDocument.__get__(self, self.__class__) ), "listDocuments": WorkflowActionDefinition( actionId="sharepoint.listDocuments", description="List documents and folders in SharePoint paths across sites", dynamicMode=True, parameters={ "connectionReference": WorkflowActionParameter( name="connectionReference", type="str", frontendType=FrontendType.USER_CONNECTION, required=True, description="Microsoft connection label" ), "documentList": WorkflowActionParameter( name="documentList", type="List[str]", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document list reference(s) containing findDocumentPath result" ), "includeSubfolders": WorkflowActionParameter( name="includeSubfolders", type="bool", frontendType=FrontendType.CHECKBOX, required=False, default=False, description="Include one level of subfolders" ) }, execute=listDocuments.__get__(self, self.__class__) ), "analyzeFolderUsage": WorkflowActionDefinition( actionId="sharepoint.analyzeFolderUsage", description="Analyze usage intensity of folders and files in SharePoint", dynamicMode=True, parameters={ "connectionReference": WorkflowActionParameter( name="connectionReference", type="str", frontendType=FrontendType.USER_CONNECTION, required=True, description="Microsoft connection label" ), "documentList": WorkflowActionParameter( name="documentList", type="List[str]", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document list reference(s) containing findDocumentPath result" ), "startDateTime": WorkflowActionParameter( name="startDateTime", type="str", frontendType=FrontendType.DATETIME, required=False, description="Start date/time in ISO format (e.g., 2025-11-01T00:00:00Z). Default: 30 days ago" ), "endDateTime": WorkflowActionParameter( name="endDateTime", type="str", frontendType=FrontendType.DATETIME, required=False, description="End date/time in ISO format (e.g., 2025-11-30T23:59:59Z). Default: current time" ), "interval": WorkflowActionParameter( name="interval", type="str", frontendType=FrontendType.SELECT, frontendOptions=["day", "week", "month"], required=False, default="day", description="Time interval for grouping activities" ) }, execute=analyzeFolderUsage.__get__(self, self.__class__) ), "findSiteByUrl": WorkflowActionDefinition( actionId="sharepoint.findSiteByUrl", description="Find SharePoint site by hostname and site path", dynamicMode=True, parameters={ "connectionReference": WorkflowActionParameter( name="connectionReference", type="str", frontendType=FrontendType.USER_CONNECTION, required=True, description="Microsoft connection label" ), "hostname": WorkflowActionParameter( name="hostname", type="str", frontendType=FrontendType.TEXT, required=True, description="SharePoint hostname (e.g., example.sharepoint.com)" ), "sitePath": WorkflowActionParameter( name="sitePath", type="str", frontendType=FrontendType.TEXT, required=True, description="Site path (e.g., SteeringBPM or /sites/SteeringBPM)" ) }, execute=findSiteByUrl.__get__(self, self.__class__) ), "downloadFileByPath": WorkflowActionDefinition( actionId="sharepoint.downloadFileByPath", description="Download file from SharePoint by exact file path", dynamicMode=True, parameters={ "connectionReference": WorkflowActionParameter( name="connectionReference", type="str", frontendType=FrontendType.USER_CONNECTION, required=True, description="Microsoft connection label" ), "siteId": WorkflowActionParameter( name="siteId", type="str", frontendType=FrontendType.TEXT, required=True, description="SharePoint site ID (from findSiteByUrl result) or document reference containing site info" ), "filePath": WorkflowActionParameter( name="filePath", type="str", frontendType=FrontendType.TEXT, required=True, description="Full file path relative to site root (e.g., /General/50 Docs hosted by SELISE/file.xlsx)" ) }, execute=downloadFileByPath.__get__(self, self.__class__) ), "copyFile": WorkflowActionDefinition( actionId="sharepoint.copyFile", description="Copy file within SharePoint", dynamicMode=True, parameters={ "connectionReference": WorkflowActionParameter( name="connectionReference", type="str", frontendType=FrontendType.USER_CONNECTION, required=True, description="Microsoft connection label" ), "siteId": WorkflowActionParameter( name="siteId", type="str", frontendType=FrontendType.TEXT, required=True, description="SharePoint site ID (from findSiteByUrl result) or document reference containing site info" ), "sourceFolder": WorkflowActionParameter( name="sourceFolder", type="str", frontendType=FrontendType.TEXT, required=True, description="Source folder path relative to site root" ), "sourceFile": WorkflowActionParameter( name="sourceFile", type="str", frontendType=FrontendType.TEXT, required=True, description="Source file name" ), "destFolder": WorkflowActionParameter( name="destFolder", type="str", frontendType=FrontendType.TEXT, required=True, description="Destination folder path relative to site root" ), "destFile": WorkflowActionParameter( name="destFile", type="str", frontendType=FrontendType.TEXT, required=True, description="Destination file name" ) }, execute=copyFile.__get__(self, self.__class__) ), "uploadFile": WorkflowActionDefinition( actionId="sharepoint.uploadFile", description="Upload raw file content (bytes) to SharePoint", dynamicMode=True, parameters={ "connectionReference": WorkflowActionParameter( name="connectionReference", type="str", frontendType=FrontendType.USER_CONNECTION, required=True, description="Microsoft connection label" ), "siteId": WorkflowActionParameter( name="siteId", type="str", frontendType=FrontendType.TEXT, required=True, description="SharePoint site ID (from findSiteByUrl result) or document reference containing site info" ), "folderPath": WorkflowActionParameter( name="folderPath", type="str", frontendType=FrontendType.TEXT, required=True, description="Folder path relative to site root" ), "fileName": WorkflowActionParameter( name="fileName", type="str", frontendType=FrontendType.TEXT, required=True, description="File name" ), "content": WorkflowActionParameter( name="content", type="str", frontendType=FrontendType.DOCUMENT_REFERENCE, required=True, description="Document reference containing file content as base64-encoded bytes" ) }, execute=uploadFile.__get__(self, self.__class__) ), "getExpensesFromPdf": WorkflowActionDefinition( actionId="sharepoint.getExpensesFromPdf", description="Extract expenses from PDF documents in SharePoint folder and save to TrusteePosition", dynamicMode=False, # Not for dynamic workflow parameters={ "connectionReference": WorkflowActionParameter( name="connectionReference", type="str", frontendType=FrontendType.USER_CONNECTION, required=True, description="Microsoft connection label for SharePoint access" ), "sharepointFolder": WorkflowActionParameter( name="sharepointFolder", type="str", frontendType=FrontendType.TEXT, required=True, description="SharePoint folder path containing PDF expense documents (e.g., /sites/MySite/Documents/Expenses)" ), "featureInstanceId": WorkflowActionParameter( name="featureInstanceId", type="str", frontendType=FrontendType.TEXT, required=True, description="Feature Instance ID for the Trustee feature where positions will be stored" ), "prompt": WorkflowActionParameter( name="prompt", type="str", frontendType=FrontendType.TEXTAREA, required=True, description="AI prompt for extracting expense data from PDF content" ) }, execute=getExpensesFromPdf.__get__(self, self.__class__) ) } # Validate actions after definition self._validateActions() # Register actions as methods (optional, für direkten Zugriff) self.findDocumentPath = findDocumentPath.__get__(self, self.__class__) self.readDocuments = readDocuments.__get__(self, self.__class__) self.uploadDocument = uploadDocument.__get__(self, self.__class__) self.listDocuments = listDocuments.__get__(self, self.__class__) self.analyzeFolderUsage = analyzeFolderUsage.__get__(self, self.__class__) self.findSiteByUrl = findSiteByUrl.__get__(self, self.__class__) self.downloadFileByPath = downloadFileByPath.__get__(self, self.__class__) self.copyFile = copyFile.__get__(self, self.__class__) self.uploadFile = uploadFile.__get__(self, self.__class__) self.getExpensesFromPdf = getExpensesFromPdf.__get__(self, self.__class__)