Source code for pylero.document

# -*- coding: utf8 -*-
from __future__ import absolute_import, division, print_function
from __future__ import unicode_literals
from pylero._compatible import basestring

import suds
from pylero.exceptions import PyleroLibException
from pylero.base_polarion import BasePolarion
from pylero.enum_option_id import EnumOptionId
from pylero.enum_option_id import ArrayOfEnumOptionId
from pylero.user import User
from pylero.subterra_uri import SubterraURI
from pylero.text import Text
from pylero.module_comment import ModuleComment
from pylero.module_comment import ArrayOfModuleComment
from pylero.project import Project
from pylero.custom import Custom
from pylero.custom import ArrayOfCustom
from pylero.signature_context import SignatureContext
from pylero.signature_context import ArrayOfSignatureContext
from pylero.work_item import _WorkItem
from pylero.base_polarion import tx_wrapper


[docs]class Document(BasePolarion): """An object to manage the TestManagement WS tns4:Module Attributes: allowed_wi_types (ArrayOfEnumOptionId) are_links_from_parent_to_child (boolean) author (User) auto_suspect (boolean) branched_from (Module) branched_with_query (string) comments (ArrayOfModuleComment) created (dateTime) custom_fields (ArrayOfCustom) derived_fields (ArrayOfstring) derived_from_link_role (EnumOptionId) derived_from_uri (SubterraURI) home_page_content (Text) id (string) location (Location) module_absolute_location (Location) module_folder (string) module_location (Location) module_name (string) project (Project) signature_contexts (ArrayOfSignatureContext) status (EnumOptionId) structure_link_role (EnumOptionId) title (string) type (EnumOptionId) updated (dateTime) updated_by (User) uses_outline_numbering (boolean) """ _cls_suds_map = { "allowed_wi_types": {"field_name": "allowedWITypes", "is_array": True, "cls": EnumOptionId, "arr_cls": ArrayOfEnumOptionId, "inner_field_name": "EnumOptionId", "enum_id": "workitem-type"}, "are_links_from_parent_to_child": "areLinksFromParentToChild", "author": {"field_name": "author", "cls": User}, "auto_suspect": "autoSuspect", "branched_from": {"field_name": "branchedFrom"}, # populated in circ refs "branched_with_query": "branchedWithQuery", "comments": {"field_name": "comments", "is_array": True, "cls": ModuleComment, "arr_cls": ArrayOfModuleComment, "inner_field_name": "ModuleComment"}, "created": "created", "derived_fields": "derived_fields", # arrayOfstring? "derived_from_uri": {"field_name": "derivedFromURI", "cls": SubterraURI}, "derived_from_link_role": {"field_name": "derivedFromLinkRole", "cls": EnumOptionId}, "home_page_content": {"field_name": "homePageContent", "cls": Text}, "document_id": "id", "space": "moduleLocation", "document_absolute_location": "moduleAbsoluteLocation", "document_folder": "moduleFolder", "document_name": "moduleName", "project_id": {"field_name": "project", "cls": Project}, "signature_contexts": {"field_name": "signatureContexts", "is_array": True, "cls": SignatureContext, "arr_cls": ArrayOfSignatureContext, "inner_field_name": "SignatureContext"}, "status": {"field_name": "status", "cls": EnumOptionId, "enum_id": "documents/document-status"}, "structure_link_role": {"field_name": "structureLinkRole", "cls": EnumOptionId}, "title": "title", "type": {"field_name": "type", "cls": EnumOptionId, "enum_id": "documents/document-type"}, "updated": "updated", "updated_by": {"field_name": "updatedBy", "cls": User}, "uses_outline_numbering": "usesOutlineNumbering", "custom_fields": {"field_name": "customFields", "is_array": True, "cls": Custom, "arr_cls": ArrayOfCustom, "inner_field_name": "Custom"}, "uri": "_uri", "_unresolvable": "_unresolvable"} _obj_client = "test_management_client" _obj_struct = "tns4:Module" # The uri struct of a module is different then others because of extra # moduleFolder element. Also requires a substitution from # to / and back URI_STRUCT = "subterra:data-service:objects:/default/" \ "%(project)s${%(obj)s}{moduleFolder}%(id)s" # must wrap lambda with classmethod so it can be used as such URI_ID_GET_REPLACE = classmethod(lambda cls, x: x.replace("#", "/")) URI_ID_SET_REPLACE = classmethod(lambda cls, x: x.replace("/", "#"))
[docs] @classmethod @tx_wrapper def create(cls, project_id, space, document_name, document_title, allowed_wi_types, document_type, structure_link_role="parent", home_page_content=""): # There is no document object. # don't know what to do with the URI it returns. """class method create Creates a document or an old-style Module/Document in given location with given parameters. Args: project_id: project to create module in space: document space location with one component or None for default space document_name: Document name (required) document_title: Document title (required) allowed_wi_types: list of types, only one should be specified document_type: Type of document (required i.e testspecification). structure_link_role: required, role which defines the hierarchy of work items inside the Module, default: parent home_page_content: HTML markup for document home page, default "" Returns: None References: Tracker.createDocument """ if isinstance(allowed_wi_types, basestring): allowed_wi_types = [allowed_wi_types] awit = [EnumOptionId(item)._suds_object for item in allowed_wi_types] slr = EnumOptionId(structure_link_role)._suds_object try: uri = cls.session.tracker_client.service.createDocument( project_id, space, document_name, document_title, awit, slr, home_page_content) doc = Document(uri=uri) doc.type = document_type # for some reason, when in a tx (@tx_wrapper), the # returned doc does not include the home_page_content attribute # so it must be reset before the update. If it is not set, an # exception is raised: # "java.lang.IllegalArgumentException: Content can't be null" if not doc.home_page_content: doc.home_page_content = home_page_content doc.update() if not home_page_content: # create heading work item for each document wi_head = _WorkItem() wi_head.type = "heading" wi_head.title = document_title doc.create_work_item(None, wi_head) return doc except suds.WebFault as e: if "Invalid document on location Location" in e.fault.faultstring: raise PyleroLibException( "Document {0}/{1} already exists".format(space, document_name)) else: raise PyleroLibException(e.fault)
[docs] @classmethod def get_documents(cls, project_id, space, fields=[]): """returns a list of Document objects Args: project_id: the project where the modules are located space: specific location of the repository fields: optional list of fields that should be contained in the returned objects. Returns: list of Document Objects References: Tracker.getModules Tracker.getModulesWithFields """ # function names and parameter lists generated dynamically based on # parameters passed in. docs = [] function_name = "getModules" parms = [project_id, space] p_fields = cls._convert_obj_fields_to_polarion(fields) if p_fields: function_name += "WithFields" parms += [p_fields] for suds_module in getattr(cls.session.tracker_client.service, function_name)(*parms): docs.append(cls(suds_object=suds_module)) return docs
[docs] @classmethod def query(cls, query, is_sql=False, fields=["document_id"], sort="document_id", limit=-1, baseline_revision=None, query_uris=False): """Searches for Modules/Documents. Args: query: query, either Lucene or SQL is_sql (bool): determines if the query is SQL or Lucene fields: list of field names to fill in the returned Modules/Documents (can be null). For nested structures in the lists you can use following syntax to include only subset of fields: myList.LIST.key (e.g. linkedWorkItems.LIST.role). For custom fields you can specify which fields you want to be filled using following syntax: customFields.CUSTOM_FIELD_ID (e.g. customFields.risk). default: list containing "document_id" sort: Lucene sort string, default: document_id limit: how many results to return (-1 means everything (default)) baseline_revision (str): if populated, query done in specified rev default - None query_uris: returns a list of URI of the Modules found, instead of a list of Documents. default - False. Returns: list of modules References: queryModuleUris queryModuleUrisBySQL queryModuleUrisInBaseline queryModuleUrisInBaselineBySQL queryModules queryModulesBySQL queryModulesInBaseline queryModulesInBaselineBySQL """ parms = [query] # The parameters have to be listed in the specific order, based on the # specific function called. That's why there are 2 if not is_sql # conditions. if not is_sql: parms.append(sort) if baseline_revision: parms.append(baseline_revision) if not query_uris: p_fields = cls._convert_obj_fields_to_polarion(fields) parms.append(p_fields) if not is_sql: parms.append(limit) if not query_uris: base_name = "queryModules" else: base_name = "queryModuleUris" if baseline_revision: base_name += "InBaseline" if is_sql: base_name += "BySQL" docs = getattr(cls.session.tracker_client.service, base_name)(*parms) if query_uris: return docs else: lst_doc = [Document(suds_object=doc) for doc in docs] return lst_doc
[docs] def __init__(self, project_id=None, doc_with_space=None, fields=None, uri=None, suds_object=None): """constructor for the Module object. Gets the module object from the Polarion server based on parameters passed in. Args: project_id: the project where the module is located doc_with_space: specific space/doc_name of the repository, required if project_id is given (Testing, Development, ...) fields: optional list of fields that should be contained in the returned object. uri: The Polarion specific uri of the module object suds_object: the WSDL Module object Returns: None References: Tracker.getModuleByLocation Tracker.getModuleByLocationWithFields Tracker.getModuleByUri Tracker.getModuleByUriWithFields """ super(self.__class__, self).__init__(suds_object=suds_object) # function names and parameter lists generated dynamically based on # parameters passed in. if doc_with_space or uri: function_name = "getModuleBy" parms = [] if doc_with_space: function_name += "Location" parms += [project_id, doc_with_space] elif uri: function_name += "Uri" parms.append(uri) if fields: function_name = "WithFields" parms.append(self._convert_obj_fields_to_polarion(fields)) self._suds_object = getattr(self.session.tracker_client.service, function_name)(*parms) if getattr(self._suds_object, "_unresolvable", True): raise PyleroLibException( "The Document {0} was not found.".format(doc_with_space or uri))
def _fix_circular_refs(self): # a class can't reference itself as a class attribute. # defined after instatiation self._cls_suds_map["branched_from"]["cls"] = self.__class__
[docs] @tx_wrapper def create_work_item(self, parent_id, w_item): """create a work item in the current document Args: parent_id: The work_item_id of the parent _WorkItem wi: The Work Item object to create. Returns: The created _WorkItem References: Tracker.createWorkItemInModule """ self._verify_obj() if isinstance(w_item, _WorkItem): w_item.verify_required() suds_wi = w_item._suds_object else: raise PyleroLibException( "the w_item parameter must be a _WorkItem") if parent_id: parent_uri = _WorkItem(work_item_id=parent_id, project_id=self.project_id).uri else: doc_wis = self.get_work_items(None, False, None) if doc_wis: parent_uri = doc_wis[0].uri else: parent_uri = None wi_uri = self.session.tracker_client.service. \ createWorkItemInModule(self.uri, parent_uri, suds_wi) new_wi = w_item.__class__(uri=wi_uri) new_wi._changed_fields = w_item._changed_fields new_wi.update() new_wi = _WorkItem(uri=wi_uri) return new_wi
[docs] def delete(self): """delete the current document Args: None Returns: None """ self._verify_obj() self.session.tracker_client.service.deleteModule(self.uri)
[docs] def get_work_items(self, parent_work_item_id, deep, fields=["work_item_id", "type"]): """Returns work items (with given fields set) contained in given Module/Document under given parent (if specified). Args: parent_work_item_id (str): Id of parent work item or None deep: true to return work items from the whole subtree fields: fields to fill. For nested structures in the lists you can use following syntax to include only subset of fields: myList.LIST.key (e.g. linkedWorkItems.LIST.role). For custom fields you can specify which fields you want to be filled using following syntax: customFields.CUSTOM_FIELD_ID (e.g. customFields.risk). Returns: list of _WorkItem objects References: Tracker.getModuleWorkItems """ self._verify_obj() if parent_work_item_id: parent_uri = _WorkItem(work_item_id=parent_work_item_id, project_id=self.project_id).uri else: parent_uri = None p_fields = _WorkItem._convert_obj_fields_to_polarion(fields) suds_wi = self.session.tracker_client.service. \ getModuleWorkItems(self.uri, parent_uri, deep, p_fields) work_items = [] for w_item in suds_wi: work_items.append(_WorkItem(suds_object=w_item)) return work_items
[docs] def move_work_item_here(self, work_item_id, parent_id, position=-1, retain_flow=True): """Moves a work item to a specific position in a Document. If the work item is not yet inside the Document it will be moved into the Document. Args: work_item_id: WorkItem id to move parent_id: The parent WorkItem id, can be None position (int): desired position in the list of children or a value < 0 to insert at the end (if the old and new parent is the same then moved work item is not counted) retain_flow (bool): true to retain the position of moved work item in the document flow (even if it means to change the parent). false to keep desired parent (even if it means to move work item to different position) Returns: None References: Tracker.moveWorkItemToDocument """ self._verify_obj() wi = _WorkItem(self.project_id, work_item_id) if parent_id: parent_uri = _WorkItem(self.project_id, parent_id).uri else: parent_uri = None self.session.tracker_client.service.moveWorkItemToDocument( wi.uri, self.uri, parent_uri, position, retain_flow)
[docs] def add_referenced_work_item(self, work_item_id): """Adds a work item to the document as a referenced work_item to the end of the current document. Args: work_item_id (str): the id of a work item in the same project as the current document Returns: None """ self._verify_obj() wi = _WorkItem(project_id=self.project_id, work_item_id=work_item_id) ref_wi_template = """<div id="polarion_wiki macro name=""" \ """module-workitem;params=id=%s|external=true">""" self.home_page_content += ref_wi_template % work_item_id self.update()
[docs] def update(self): """updates the server with the current module data Args: None Returns: None References: Tracker.updateModule """ self.session.tracker_client.service.updateModule(self._suds_object)