Esempio n. 1
0
    def getCommentByID(self, comment_id):
        """Get the :class:`rtcclient.models.Comment` object by its id

        Note: the comment id starts from 0

        :param comment_id: the comment id (integer or equivalent string)
        :return: the :class:`rtcclient.models.Comment` object
        :rtype: rtcclient.models.Comment
        """

        # check the validity of comment id
        try:
            if isinstance(comment_id, bool):
                raise ValueError()
            if isinstance(comment_id, six.string_types):
                comment_id = int(comment_id)
            if not isinstance(comment_id, int):
                raise ValueError()
        except (ValueError, TypeError):
            raise exception.BadValue("Please input valid comment id")

        comment_url = "/".join([self.url, "rtc_cm:comments/%s" % comment_id])
        try:
            return Comment(comment_url, self.rtc_obj)
        except HTTPError:
            self.log.error("Comment %s does not exist", comment_id)
            raise exception.BadValue("Comment %s does not exist" % comment_id)
Esempio n. 2
0
    def getTemplates(self, workitems, template_folder=None,
                     template_names=None, keep=False, encoding="UTF-8"):
        """Get templates from a group of to-be-copied :class:`Workitems` and
        write them to files named after the names in `template_names`
        respectively.

        :param workitems: a :class:`list`/:class:`tuple`/:class:`set`
            contains the ids (integer or equivalent string) of some
            to-be-copied :class:`Workitems`
        :param template_names: a :class:`list`/:class:`tuple`/:class:`set`
            contains the template file names for copied :class:`Workitems`.
            If `None`, the new template files will be named after the
            :class:`rtcclient.workitem.Workitem` id with "`.template`" as a
            postfix
        :param template_folder: refer to
            :class:`rtcclient.template.Templater.getTemplate`
        :param keep: (default is False) refer to
            :class:`rtcclient.template.Templater.getTemplate`
        :param encoding: (default is "UTF-8") refer to
            :class:`rtcclient.template.Templater.getTemplate`
        """

        if (not workitems or isinstance(workitems, six.string_types) or
                isinstance(workitems, int) or
                isinstance(workitems, float) or
                not hasattr(workitems, "__iter__")):
            error_msg = "Input parameter 'workitems' is not iterable"
            self.log.error(error_msg)
            raise exception.BadValue(error_msg)

        if template_names is not None:
            if not hasattr(template_names, "__iter__"):
                error_msg = "Input parameter 'template_names' is not iterable"
                self.log.error(error_msg)
                raise exception.BadValue(error_msg)

            if len(workitems) != len(template_names):
                error_msg = "".join(["Input parameters 'workitems' and ",
                                     "'template_names' have different length"])
                self.log.error(error_msg)
                raise exception.BadValue(error_msg)

        for index, wk_id in enumerate(workitems):
            try:
                if template_names is not None:
                    template_name = template_names[index]
                else:
                    template_name = ".".join([wk_id, "template"])
                self.getTemplate(wk_id,
                                 template_name=template_name,
                                 template_folder=template_folder,
                                 keep=keep,
                                 encoding=encoding)
            except Exception as excp:
                self.log.error("Exception occurred when fetching"
                               "template from <Workitem %s>: %s",
                               str(wk_id), excp)
                continue
        self.log.info("Successfully fetch all the templates from "
                      "workitems: %s", workitems)
Esempio n. 3
0
    def runSavedQueryByID(self, saved_query_id, returned_properties=None):
        """Query workitems using the saved query id

        This saved query id can be obtained by below two methods:

        1. :class:`rtcclient.models.SavedQuery` object (e.g.
        mysavedquery.id)

        2. your saved query url (e.g.
        https://myrtc:9443/jazz/web/xxx#action=xxxx%id=_mGYe0CWgEeGofp83pg),
        where the last "_mGYe0CWgEeGofp83pg" is the saved query id.

        :param saved_query_id: the saved query id
        :param returned_properties: the returned properties that you want.
            Refer to :class:`rtcclient.client.RTCClient` for more explanations
        :return: a :class:`list` that contains the queried
            :class:`rtcclient.workitem.Workitem` objects
        :rtype: list
        """

        if not isinstance(saved_query_id,
                          six.string_types) or not saved_query_id:
            excp_msg = "Please specify a valid saved query id"
            self.log.error(excp_msg)
            raise exception.BadValue(excp_msg)
        return self._runSavedQuery(saved_query_id,
                                   returned_properties=returned_properties)
Esempio n. 4
0
    def getItemType(self, title, returned_properties=None):
        """Get the :class:`rtcclient.models.ItemType` object by the title

        :param title: the title (e.g. Story/Epic/..)
        :param returned_properties: the returned properties that you want.
            Refer to :class:`rtcclient.client.RTCClient` for more explanations
        :return: the :class:`rtcclient.models.ItemType` object
        :rtype: rtcclient.models.ItemType
        """

        if not isinstance(title, six.string_types) or not title:
            excp_msg = "Please specify a valid email address name"
            self.log.error(excp_msg)
            raise exception.BadValue(excp_msg)

        self.log.debug("Try to get <ItemType %s>", title)
        itemtypes = self._getItemTypes(returned_properties=returned_properties,
                                       title=title)
        if itemtypes is not None:
            itemtype = itemtypes[0]
            self.log.info("Get <ItemType %s> in <ProjectArea %s>", itemtype,
                          self)
            return itemtype

        excp_msg = "No itemtype's name is %s in <ProjectArea %s>" % (title,
                                                                     self)
        self.log.error(excp_msg)
        raise exception.NotFound(excp_msg)
Esempio n. 5
0
    def getAdministrator(self, email, returned_properties=None):
        """Get the :class:`rtcclient.models.Administrator` object
        by the email address

        :param email: the email address (e.g. [email protected])
        :param returned_properties: the returned properties that you want.
            Refer to :class:`rtcclient.client.RTCClient` for more explanations
        :return: the :class:`rtcclient.models.Administrator` object
        :rtype: rtcclient.models.Administrator
        """

        if not isinstance(email, six.string_types) or "@" not in email:
            excp_msg = "Please specify a valid email address name"
            self.log.error(excp_msg)
            raise exception.BadValue(excp_msg)

        self.log.debug("Try to get Administrator whose email is %s", email)
        rp = returned_properties
        administrators = self._getAdministrators(returned_properties=rp,
                                                 email=email)
        if administrators is not None:
            administrator = administrators[0]
            self.log.info("Get <Administrator %s> in <ProjectArea %s>",
                          administrator, self)
            return administrator

        msg = "No administrator's email is %s in <ProjectArea %s>" % (email,
                                                                      self)
        self.log.error(msg)
        raise exception.NotFound(msg)
Esempio n. 6
0
    def removeSubscribers(self, emails_list):
        """Remove subscribers from this workitem

        If the subscribers have not been added, no more actions will be
        performed.

        :param emails_list: a :class:`list`/:class:`tuple`/:class:`set`
            contains the the subscribers' emails
        """

        if not hasattr(emails_list, "__iter__"):
            error_msg = "Input parameter 'emails_list' is not iterable"
            self.log.error(error_msg)
            raise exception.BadValue(error_msg)

        # overall flag
        missing_flags = True

        headers, raw_data = self._perform_subscribe()
        for email in emails_list:
            missing_flag, raw_data = self._remove_subscriber(email, raw_data)
            missing_flags = missing_flags and missing_flag

        if missing_flags:
            return

        self._update_subscribe(headers, raw_data)
        self.log.info("Successfully remove subscribers: %s for <Workitem %s>",
                      emails_list, self)
Esempio n. 7
0
    def addSubscribers(self, emails_list):
        """Add subscribers to this workitem

        If the subscribers have already been added, no more actions will be
        performed.

        :param emails_list: a :class:`list`/:class:`tuple`/:class:`set`
            contains the the subscribers' emails
        """

        if not hasattr(emails_list, "__iter__"):
            error_msg = "Input parameter 'emails_list' is not iterable"
            self.log.error(error_msg)
            raise exception.BadValue(error_msg)

        # overall flag
        existed_flags = False

        headers, raw_data = self._perform_subscribe()
        for email in emails_list:
            existed_flag, raw_data = self._add_subscriber(email, raw_data)
            existed_flags = existed_flags and existed_flag

        if existed_flags:
            return

        self._update_subscribe(headers, raw_data)
        self.log.info("Successfully add subscribers: %s for <Workitem %s>",
                      emails_list, self)
Esempio n. 8
0
    def addParent(self, parent_id):
        """Add a parent to current workitem

        Notice: for a certain workitem, no more than one parent workitem
        can be added and specified

        :param parent_id: the parent workitem id/number
            (integer or equivalent string)
        """

        if isinstance(parent_id, bool):
            raise exception.BadValue("Please input a valid workitem id")
        if isinstance(parent_id, six.string_types):
            parent_id = int(parent_id)
        if not isinstance(parent_id, int):
            raise exception.BadValue("Please input a valid workitem id")

        self.log.debug(
            "Try to add a parent <Workitem %s> to current "
            "<Workitem %s>", parent_id, self)

        headers = copy.deepcopy(self.rtc_obj.headers)
        headers["Content-Type"] = self.OSLC_CR_JSON
        req_url = "".join([
            self.url, "?oslc_cm.properties=com.ibm.team.workitem.",
            "linktype.parentworkitem.parent"
        ])

        parent_tag = ("rtc_cm:com.ibm.team.workitem.linktype."
                      "parentworkitem.parent")
        parent_url = ("{0}/resource/itemName/com.ibm.team."
                      "workitem.WorkItem/{1}".format(self.rtc_obj.url,
                                                     parent_id))
        parent_original = {parent_tag: [{"rdf:resource": parent_url}]}

        self.put(req_url,
                 verify=False,
                 proxies=self.rtc_obj.proxies,
                 headers=headers,
                 data=json.dumps(parent_original))
        self.log.info(
            "Successfully add a parent <Workitem %s> to current "
            "<Workitem %s>", parent_id, self)
Esempio n. 9
0
    def _addChild(self, child_id, children_original):
        child_tag = ("rtc_cm:com.ibm.team.workitem.linktype."
                     "parentworkitem.children")

        # check data type
        if isinstance(child_id, bool):
            raise exception.BadValue("Invalid workitem id: %s", child_id)
        if isinstance(child_id, six.string_types):
            child_id = int(child_id)
        if not isinstance(child_id, int):
            raise exception.BadValue("Invalid workitem id: %s", child_id)

        # add child url
        child_url = ("{0}/resource/itemName/com.ibm.team."
                     "workitem.WorkItem/{1}".format(self.rtc_obj.url,
                                                    child_id))
        new_child = {"rdf:resource": child_url}
        if new_child not in children_original[child_tag]:
            children_original[child_tag].append(new_child)
        else:
            self.log.debug(
                "Child <Workitem %s> has already been added to "
                "current <Workitem %s>. Ignore it.", child_id, self)
Esempio n. 10
0
    def runSavedQueryByUrl(self, saved_query_url, returned_properties=None):
        """Query workitems using the saved query url

        :param saved_query_url: the saved query url
        :param returned_properties: the returned properties that you want.
            Refer to :class:`rtcclient.client.RTCClient` for more explanations
        :return: a :class:`list` that contains the queried
            :class:`rtcclient.workitem.Workitem` objects
        :rtype: list
        """

        try:
            if "=" not in saved_query_url:
                raise exception.BadValue()
            saved_query_id = saved_query_url.split("=")[-1]
            if not saved_query_id:
                raise exception.BadValue()
        except:
            error_msg = "No saved query id is found in the url"
            self.log.error(error_msg)
            raise exception.BadValue(error_msg)
        return self._runSavedQuery(saved_query_id,
                                   returned_properties=returned_properties)
Esempio n. 11
0
    def render(self, template, **kwargs):
        """Renders the template

        :param template: The template to render.
            The template is actually a file, which is usually generated
            by :class:`rtcclient.template.Templater.getTemplate`
            and can also be modified by user accordingly.
        :param kwargs: The `kwargs` dict is used to fill the template.
            These two parameter are mandatory:

                * description
                * title

            Some of below parameters (which may not be included in some
            customized workitem type ) are mandatory if `keep` (parameter in
            :class:`rtcclient.template.Templater.getTemplate`) is set to
            `False`; Optional for otherwise.

                * teamArea (Team Area)
                * ownedBy (Owned By)
                * plannedFor(Planned For)
                * severity(Severity)
                * priority(Priority)
                * filedAgainst(Filed Against)

            Actually all these needed keywords/attributes/fields can be
            retrieved by :class:`rtcclient.template.Templater.listFields`

        :return: the :class:`string` object
        :rtype: string
        """

        if kwargs.get("title", None) is not None:
            kwargs["title"] = escape(kwargs["title"])

        if kwargs.get("description", None) is not None:
            kwargs["description"] = escape(kwargs["description"])

        try:
            temp = self.environment.get_template(template)
            return temp.render(**kwargs)
        except AttributeError:
            err_msg = "Invalid value for 'template'"
            self.log.error(err_msg)
            raise exception.BadValue(err_msg)
Esempio n. 12
0
    def _remove_subscriber(self, email, raw_data):
        if not isinstance(email, six.string_types) or "@" not in email:
            excp_msg = "Please specify a valid email address name: %s" % email
            self.log.error(excp_msg)
            raise exception.BadValue(excp_msg)

        missing_flag = True
        del_sub = self.rtc_obj.getOwnedBy(email)
        description = raw_data.get("rdf:RDF").get("rdf:Description")
        subs = description.get("rtc_cm:subscribers", None)
        if subs is None:
            # no subscribers
            self.log.error("No subscribers for <Workitem %s>", self)
        else:
            if isinstance(subs, OrderedDict):
                # only one subscriber exist
                missing_flag = self._check_missing_subscriber(del_sub, subs)
                if not missing_flag:
                    description.pop("rtc_cm:subscribers")
                else:
                    self.log.error(
                        "The subscriber %s has not been "
                        "added. No need to unsubscribe", del_sub.email)
            else:
                # a list: several subscribers
                # check existing
                for exist_sub in subs:
                    missing_flag = self._check_missing_subscriber(
                        del_sub, exist_sub)
                    if not missing_flag:
                        subs.remove(exist_sub)

                        if len(subs) == 1:
                            # only one existing
                            description["rtc_cm:subscribers"] = subs[0]

                        break
                else:
                    self.log.error(
                        "The subscriber %s has not been "
                        "added. No need to unsubscribe", del_sub.email)

        return missing_flag, raw_data
Esempio n. 13
0
    def listFields(self, template):
        """List all the attributes to be rendered from the template file

        :param template: The template to render.
            The template is actually a file, which is usually generated
            by :class:`rtcclient.template.Templater.getTemplate` and can also
            be modified by user accordingly.
        :return: a :class:`set` contains all the needed attributes
        :rtype: set
        """

        try:
            temp_source = self.environment.loader.get_source(
                self.environment, template)
            return self.listFieldsFromSource(temp_source)
        except AttributeError:
            err_msg = "Invalid value for 'template'"
            self.log.error(err_msg)
            raise exception.BadValue(err_msg)
Esempio n. 14
0
    def removeChildren(self, child_ids):
        """Remove children from current workitem

        :param child_ids: a :class:`list` contains the children
            workitem id/number (integer or equivalent string)
        """

        if not hasattr(child_ids, "__iter__"):
            error_msg = "Input parameter 'child_ids' is not iterable"
            self.log.error(error_msg)
            raise exception.BadValue(error_msg)

        self.log.debug(
            "Try to remove children <Workitem %s> from current "
            "<Workitem %s>", child_ids, self)
        self._removeChildren(child_ids)
        self.log.info(
            "Successfully remove children <Workitem %s> from "
            "current <Workitem %s>", child_ids, self)
Esempio n. 15
0
    def _add_subscriber(self, email, raw_data):
        if not isinstance(email, six.string_types) or "@" not in email:
            excp_msg = "Please specify a valid email address name: %s" % email
            self.log.error(excp_msg)
            raise exception.BadValue(excp_msg)

        existed_flag = False
        new_subscriber = self.rtc_obj.getOwnedBy(email)
        new_sub = OrderedDict()
        new_sub["@rdf:resource"] = new_subscriber.url
        description = raw_data.get("rdf:RDF").get("rdf:Description")
        subs = description.get("rtc_cm:subscribers", None)
        if subs is None:
            # no subscribers
            added_url = "http://jazz.net/xmlns/prod/jazz/rtc/cm/1.0/"
            raw_data["rdf:RDF"]["@xmlns:rtc_cm"] = added_url
            description["rtc_cm:subscribers"] = new_sub
        else:
            if isinstance(subs, OrderedDict):
                # only one subscriber exist
                existed_flag = self._check_exist_subscriber(
                    new_subscriber, subs)
                if not existed_flag:
                    subs = [subs]
                    subs.append(new_sub)
                    description["rtc_cm:subscribers"] = subs
            else:
                # a list: several subscribers
                # check existing
                for exist_sub in subs:
                    existed_flag = self._check_exist_subscriber(
                        new_subscriber, exist_sub)
                    if existed_flag:
                        break
                else:
                    subs.append(new_sub)

        return existed_flag, raw_data
Esempio n. 16
0
    def getAction(self, action_name):
        """Get the :class:`rtcclient.models.Action` object by its name

        :param action_name: the name/title of the action
        :return: the :class:`rtcclient.models.Action` object
        :rtype: rtcclient.models.Action
        """

        self.log.debug("Try to get <Action %s>", action_name)
        if not isinstance(action_name, six.string_types) or not action_name:
            excp_msg = "Please specify a valid action name"
            self.log.error(excp_msg)
            raise exception.BadValue(excp_msg)

        actions = self._getActions(action_name=action_name)

        if actions is not None:
            action = actions[0]
            self.log.info("Find <Action %s>", action)
            return action

        self.log.error("No Action named %s", action_name)
        raise exception.NotFound("No Action named %s" % action_name)
Esempio n. 17
0
    def getRole(self, label):
        """Get the :class:`rtcclient.models.Role` object by the label name

        :param label: the label name of the role
        :return: the :class:`rtcclient.models.Role` object
        :rtype: :class:`rtcclient.models.Role`
        """

        if not isinstance(label, six.string_types) or not label:
            excp_msg = "Please specify a valid role label"
            self.log.error(excp_msg)
            raise exception.BadValue(excp_msg)

        roles = self.getRoles()
        if roles is not None:
            for role in roles:
                if role.label == label:
                    self.log.info("Get <Role %s> in <ProjectArea %s>", role,
                                  self)
                    return role

        excp_msg = "No role's label is %s in <ProjectArea %s>" % (label, self)
        self.log.error(excp_msg)
        raise exception.NotFound(excp_msg)
Esempio n. 18
0
    def getTemplate(self,
                    copied_from,
                    template_name=None,
                    template_folder=None,
                    keep=False,
                    encoding="UTF-8"):
        """Get template from some to-be-copied
        :class:`rtcclient.workitem.Workitem`

        The resulting XML document is returned as a :class:`string`, but if
        `template_name` (a string value) is specified,
        it is written there instead.

        :param copied_from: the to-be-copied
            :class:`rtcclient.workitem.Workitem` id (integer or
            equivalent string)
        :param template_name: the template file name
        :param template_folder: the folder to store template file
        :param keep: (default is False) If `True`, some of below parameters
            (which may not be included in some customized
            :class:`rtcclient.workitem.Workitem` type ) will remain
            unchangeable with the to-be-copied
            :class:`rtcclient.workitem.Workitem`.
            Otherwise for `False`.

                * teamArea (Team Area)
                * ownedBy (Owned By)
                * plannedFor(Planned For)
                * severity(Severity)
                * priority(Priority)
                * filedAgainst(Filed Against)
        :param encoding: (default is "UTF-8") coding format
        :return:

            * a :class:`string` object: if `template_name` is not specified
            * write the template to file `template_name`: if `template_name` is
              specified
        """

        try:
            if isinstance(copied_from, bool) or isinstance(copied_from, float):
                raise ValueError()
            if isinstance(copied_from, six.string_types):
                copied_from = int(copied_from)
            if not isinstance(copied_from, int):
                raise ValueError()
        except ValueError:
            err_msg = "Please input a valid workitem id you want to copy from"
            self.log.error(err_msg)
            raise exception.BadValue(err_msg)

        self.log.info("Fetch the template from <Workitem %s> with [keep]=%s",
                      copied_from, keep)

        if template_folder is None:
            template_folder = self.searchpath

        # identify whether output to a file
        if template_name is not None:
            template_file_path = os.path.join(template_folder, template_name)
            output = open(template_file_path, "w")
        else:
            template_file_path = None
            output = None

        workitem_url = "/".join([self.url, "oslc/workitems/%s" % copied_from])
        resp = self.get(workitem_url,
                        verify=False,
                        proxies=self.rtc_obj.proxies,
                        headers=self.rtc_obj.headers,
                        cookies=self.rtc_obj.cookiejar)
        raw_data = xmltodict.parse(resp.content)

        # pre-adjust the template:
        # remove some attribute to avoid being overwritten, which will only be
        # generated when the workitem is created
        wk_raw_data = raw_data.get("oslc_cm:ChangeRequest")
        self._remove_long_fields(wk_raw_data)

        # Be cautious when you want to modify these fields
        # These fields have been tested as must-removed one
        remove_fields = [
            "@rdf:about", "dc:created", "dc:creator", "dc:identifier",
            "rtc_cm:contextId", "rtc_cm:comments", "rtc_cm:state", "dc:type",
            "rtc_cm:subscribers", "dc:modified", "rtc_cm:modifiedBy",
            "rtc_cm:resolved", "rtc_cm:resolvedBy", "rtc_cm:resolution",
            "rtc_cm:startDate", "rtc_cm:timeSpent", "rtc_cm:progressTracking",
            "rtc_cm:projectArea", "oslc_cm:relatedChangeManagement",
            "oslc_cm:trackedWorkItem", "oslc_cm:tracksWorkItem",
            "rtc_cm:timeSheet", "oslc_pl:schedule"
        ]

        for remove_field in remove_fields:
            try:
                wk_raw_data.pop(remove_field)
                self.log.debug(
                    "Successfully remove field [%s] from the "
                    "template originated from <Workitem %s>", remove_field,
                    copied_from)
            except:
                self.log.warning(
                    "No field named [%s] in this template "
                    "from <Workitem %s>", remove_field, copied_from)
                continue

        wk_raw_data["dc:description"] = "{{ description }}"
        wk_raw_data["dc:title"] = "{{ title }}"

        if keep:
            if template_file_path:
                self.log.info("Writing the template to file %s",
                              template_file_path)
            return xmltodict.unparse(raw_data,
                                     output=output,
                                     encoding=encoding,
                                     pretty=True)

        replace_fields = [("rtc_cm:teamArea", "{{ teamArea }}"),
                          ("rtc_cm:ownedBy", "{{ ownedBy }}"),
                          ("rtc_cm:plannedFor", "{{ plannedFor }}"),
                          ("rtc_cm:foundIn", "{{ foundIn }}"),
                          ("oslc_cm:severity", "{{ severity }}"),
                          ("oslc_cm:priority", "{{ priority }}"),
                          ("rtc_cm:filedAgainst", "{{ filedAgainst }}")]
        for field in replace_fields:
            try:
                wk_raw_data[field[0]]["@rdf:resource"] = field[1]
                self.log.debug("Successfully replace field [%s] with [%s]",
                               field[0], field[1])
            except:
                self.log.warning("Cannot replace field [%s]", field[0])
                continue

        if template_file_path:
            self.log.info("Writing the template to file %s",
                          template_file_path)

        return xmltodict.unparse(raw_data,
                                 output=output,
                                 encoding=encoding,
                                 pretty=True)