Replacing strings against template stories in Rally - python

I can successfully obtain user story information with my Python code; however, I want to replace a specific string located in the details and user story name. For instance, the string could be $projectName and I'd like to replace it with a user inputted value.
Is there something which helps to code for this or is there a working example? I am stuck because this isn't necessarily a file to edit and the Hierarchy output also creates a block for me to develop something that works.
Here is my code for pulling the data:
#!/usr/bin/env python
import sys
from pyral import Rally, rallyWorkset
options = [arg for arg in sys.argv[1:] if arg.startswith('--')]
args = [arg for arg in sys.argv[1:] if arg not in options]
server, username, password, apikey, workspace, project = rallyWorkset(options)
if apikey:
rally = Rally(server, apikey=apikey, workspace=workspace, project=project)
else:
rally = Rally(server, username=username, password=password, workspace=workspace, project=project)
response = rally.get ('UserStory', fetch=True, query='Name contains "$projecttest"')
for story1 in response:
print story1.details()
Here is the output I get, scrubbed, of course:
HierarchicalRequirement
oid : 81284473268
ref : hierarchicalrequirement/81284473268
ObjectID : 81284473268
ObjectUUID : 67c952b4-e414-4759-a8c5-d7d7543ba98d
_ref : https://rally1.rallydev.com/slm/webservice/v2.0/hierarchicalrequirement/81284473268
_CreatedAt : Dec 14, 2016
_hydrated : True
Name : Robert - test - pyRal - $projecttest
Subscription : <<SCRUBBED>>
Workspace : Workspace.ref (OID SCRUBBED)
FormattedID : US47008
AcceptedDate : None
Attachments : []
Blocked : False
BlockedReason : None
Blocker : None
Changesets : []
Children : []
CreationDate : 2016-12-14T18:21:03.663Z
DefectStatus : NONE
Defects : []
Description : <b>Description</b><div><br /><div>Some format here</div><div><br /></div><div><b>Outcome</b></div><div><b><br /></b></div><div>Some outcome here</div></div>
DirectChildrenCount : 0
Discussion : []
DisplayColor : #21a2e0
DragAndDropRank : O~zbf~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expedite : False
Feature : None
HasParent : False
InProgressDate : None
Iteration : None
LastBuild : None
LastRun : None
LastUpdateDate : 2016-12-19T22:42:48.173Z
LatestDiscussionAgeInMinutes : None
Milestones : []
Notes :
Owner : User.ref (OID SCRUBBED)
Package : None
Parent : None
PassingTestCaseCount : 0
PlanEstimate : None
PortfolioItem : None
Predecessors : []
Project : Project.ref (OID SCRUBBED)
Ready : False
Recycled : False
Release : None
ScheduleState : Ungroomed
ScheduleStatePrefix : U
Successors : []
Tags : []
TaskActualTotal : 0.0
TaskEstimateTotal : 0.0
TaskRemainingTotal : 0.0
TaskStatus : NONE
Tasks : []
TestCaseCount : 0
TestCaseStatus : NONE
TestCases : []
VersionId : 4
__collection_ref_for_RevisionHistory : SCRUBBED
_refObjectUUID : SCRUBBED
AcceptanceCriteria : None
IGNOREAcceptanceCriteria : None
IdeaURL : <pyral.entity.CustomField object at 0x7f1afb79a190>
IdeaVotes : None
JIRAPriority : None
JiraKey : None
JiraLink : None
KanbanState : None
Priority : None
ReleaseNote : None
RequestedDate : None
SNRequest : None
Submitter : None
TestRailPlanID : None
TrackingState : None
=====================================================
So, with PyRal, I would have to send an update to each user story this code pulls, only on the rows which contain the string; however, I would somehow have to store each FormattedID: and the associated Name: field to update, this is where I am stuck, on how to actually store this so it can be iterated over and updated.

You could use template strings which do exactly what you want, using an env-variable like to delimit template tokens:
from string import Template
s = Template('this is $projectName')
s.substitute(projectName='THE_PROJECT')
results in:
'this is THE_PROJECT'
https://docs.python.org/3/library/string.html
Alternate way, if you wrap projectName within curly braces, is using format:
s = 'this is {projectName}'
print(s.format(projectName='THE_PROJECT'))

Related

FastAPI - How to set correct response_model to return n documents from MongoDB?

Working on a FastAPI project using a MongoDB backend (using motor-asyncio). I will caveat all of this by saying that I'm very new to both FastAPI and motor (previously just worked with PyMongo).
I'm working on an endpoint that is supposed to return n documents from Mongo, contingent on the params set. I am able to get this to work if I don't set a response model in the function definition, and simply do the following:
#ScoresRouter.get("/getScores")
# omitted further query params for simplicity. there are a few of them which then alter the query
# but this is the simplest version
async def get_scores(members: list[str] | None = Query(default=None)):
c = coll.find({'membershipNumber' : {'$in' : members}}, {'_id' : 0, 'membershipNumber' : 1, 'score' : 1, 'workplace' : 1, 'created_at' : 1}
out = []
async for doc in c:
out.append(doc)
return out
But, I want to use the proper pydantic response_model syntax. So, I've defined the following:
class ScoreOut(BaseModel):
membershipNumber : str
score : float | None
historic_data : list | None
created_at : datetime | None
region : str | None
district : str | None
branch : str | None
workplace : str | None
class ScoresOut(BaseModel):
result : List[ScoreOut]
This is in accordance with what the data looks like in my target DB collection, which is this (this is copied from the mongo shell, not python):
mydb:PRIMARY> db.my_coll.findOne();
{
"_id" : ObjectId("1234"),
"membershipNumber" : "M123456"
"score" : 8.3,
"workplace" : "W0943294",
"created_at" : ISODate("2022-07-09T23:00:04.070Z"),
"end_date" : ISODate("2022-07-09T00:00:00Z"),
"historical_data" : [
{
"score" : 0,
"created_at" : ISODate("2022-05-10T16:50:19.136Z"),
"end_date" : ISODate("2020-01-08T00:00:00Z")
},
{
"score" : 0,
"end_date" : ISODate("2020-01-15T00:00:00Z"),
"created_at" : ISODate("2022-05-10T16:55:21.644Z")
}
]
}
Now, I change the route/function definition as follows:
async def get_scores(members: list[str] | None = Query(default=None),
response_model=ScoresOut,
response_model_exclude_unset=True):
c = coll.find({'membershipNumber' : {'$in' : members}}, {'_id' : 0, 'membershipNumber' : 1, 'score' : 1, 'workplace' : 1, 'created_at' : 1}
out = []
async for doc in c:
out.append(doc)
return out
And it no longer works. On the swagger-GUI I get a rather uninformative Internal Server Error, but in my terminal I'm getting this error:
pydantic.error_wrappers.ValidationError: 1 validation error for ScoresOut
response
value is not a valid dict (type=type_error.dict)
I imagine I somehow have to tell my function to wrap out in the ScoresOut response model, although a lot of tutorials I've seen do not do this step: They simple output an object which appears to match the response_model they've defined, and it somehow just works.
I wonder if this has something to do with Mongo's rather difficult bson datatypes, and converting them to something FastAPI/pydantic understand? I doubt it though, because if I don't define a response-model and simply return the out object, it works, and it looks as it would if I'd print the list of dicts in Python.
Any help with this would be hugely appreciated.
Since you're returning a list and not a single object, you'll have to tell FastAPI that it can expect a list of objects compatible with ScoresOut to be returned:
#ScoresRouter.get("/getScores", response_model=List[ScoresOut])
(side note: generally I recommend using just /scores as the path, as GET is implied by the HTTP method you're using)
If your mongodb library returns objects (instead of dictionaries), you'll need to configure your model to load its values from property lookups as well (.foo):
class ScoresOut(BaseModel):
result : List[ScoreOut]
class Config:
orm_mode = True
And as long as your mongodb library returns a iterable, you don't have to create the list and iterate it yourself:
async def get_scores(members: list[str] | None = Query(default=None),
response_model=List[ScoresOut],
response_model_exclude_unset=True):
return coll.find({'membershipNumber' : {'$in' : members}}, {'_id' : 0, 'membershipNumber' : 1, 'score' : 1, 'workplace' : 1, 'created_at' : 1}

TestCase defined under TestFolder on Rally not getting updated by pyral

I'm not able to update the testCase defined on Rally using pyral
Below is the code snippet I am using:
# Get the testcase that needs to be updated
query_criteria = 'FormattedID = "%s"' % tc
rally_response = rally_obj.get('TestCase', fetch=True, query=query_criteria)
target_project = rally.getProject()
testcase_fields = {
"Project" : target_project.ref,
"Workspace" : "workspace/59461540411",
"Name" : "test",
"Method" : "Automated",
"Type" : "Acceptance",
"Notes" : "netsim testing",
"Description" : "description changing",
#"TestCase" : "testcase/360978196712"
}
testcase_response = rally.put('TestCase', testcase_fields)
The status code of the testcase_response is "200" , but the Test Case is not updated.
What is wrong?
You are mixing 2 functions:
rally.put: to create a new WorkItem
rally.update: to modify an existing WorkItem
When you modify an item, you need the reference, FormattedID, or Object ID of the Workitem.
Your code should look like something like that:
# Get the testcase that needs to be updated
import logging
logging.basicConfig(format="%(levelname)s:%(module)s:%(lineno)d:%(msg)s")
def get_test_case(tcid):
"""Retrieve Test Case from its ID, or None if no such TestCase id"""
query_criteria = "FormattedID = %s" % tcid
try:
response = rally_obj.get('TestCase', fetch=True, query=query_criteria)
return response.next()
except StopIteration:
logging.error("Test Case '%s' not found" % tcid)
return None
target_project = rally.getProject()
test_case = get_test_case("TC1234")
if test_case:
testcase_fields = {
"FormattedID" : test_case.FormattedID
"Project" : target_project.ref,
"Workspace" : "workspace/59461540411", # might not be necessary if you don't change it
"Name" : "test",
"Method" : "Automated",
"Type" : "Acceptance",
"Notes" : "netsim testing",
"Description" : "description changed",
}
testcase_response = rally.update('TestCase', testcase_fields)
print(testcase_response)
NOTE: you don't have to have double quotes " in the query.

How to Create a PUT request in Python for the following JSON

I have a JSON body like this:
'\n{\n"users" : \n[\n\n{\n"dn" : null,\n"dns_domain" : null,\n"domain" : "UNIX_USERS",\n"email" : null,\n"enabled" : true,\n"expired" : false,\n"expiry" : null,\n"gecos" : "InsightIQ User",\n"generated_gid" : false,\n"generated_uid" : false,\n"generated_upn" : true,\n"gid" : \n{\n"id" : "GID:15",\n"name" : "insightiq",\n"type" : "group"\n},\n"home_directory" : "/ifs/home/insightiq",\n"id" : "insightiq",\n"locked" : false,\n"max_password_age" : null,\n"member_of" : null,\n"name" : "insightiq",\n"object_history" : [],\n"on_disk_group_identity" : \n{\n"id" : "GID:15",\n"name" : "insightiq",\n"type" : "group"\n},\n"on_disk_user_identity" : \n{\n"id" : "UID:15",\n"name" : "insightiq",\n"type" : "user"\n},\n"password_expired" : false,\n"password_expires" : true,\n"password_expiry" : null,\n"password_last_set" : null,\n"primary_group_sid" : \n{\n"id" : "SID:S-1-22-2-15",\n"name" : "insightiq",\n"type" : "group"\n},\n"prompt_password_change" : false,\n"provider" : "lsa-file-provider:System",\n"sam_account_name" : "insightiq",\n"shell" : "/sbin/nologin",\n"sid" : \n{\n"id" : "SID:S-1-22-1-15",\n"name" : "insightiq",\n"type" : "user"\n},\n"type" : "user",\n"uid" : \n{\n"id" : "UID:15",\n"name" : "insightiq",\n"type" : "user"\n},\n"upn" : "insightiq#UNIX_USERS",\n"user_can_change_password" : false\n}\n]\n}\n')
Now I want to create a PUT request in python so as to change the value of "enabled" to false.
This is what I have, but I keep getting a 400 bad request.
def revert_insightIQ_user_account(self):
''' Revert insightIQ_user_account'''
print "Reverting insightIQ user to default... "
req_url = "/platform/1/auth/users/insightiq"
default = {}
default['users'] = []
default['users'].append({})
default["users"][0]["enabled"] = False
self.http_papi.httpPut(self.base_url, self.port, self.username, self.password, req_url, req_body=default)
print "Settings reverted successfully.."
I think I am creating the JSON body format wrongly, because the URL and all is correct (i have double checked that) but I can't figure out what would be the correct one and have reached a dead end. Can someone please help?
edit:
http_papi is a library created which has all the API transactions like GET PUT defined, so whenever I use an API request I call it from that library (it has been imported)
The JSON string source is the URI mentioned : /platform/1/auth/users/insightiq (this along with the cluster IP I am using for my testing)
The JSON string posted is what I get back when I do a raw CURL put request to enable the user. I have to create a same JSON format in my python code and pass enabled = false there.
this worked by just changing the list object "enabled" didn't have to create the DS from scratch.
def revert_insightIQ_user_account(self):
''' Revert insightIQ_user_account'''
print
print "Reverting insightIQ user to default... "
default = {}
req_url = "/platform/1/auth/users/insightiq"
default['enabled'] = False
self.http_papi.httpPut(self.base_url, self.port, self.username, self.password, req_url, req_body=default)
print "Settings reverted successfully..

Python Type Dispatching with variables, is it possible?

I am developing a script that parses through a large number of IDs and want to build an abstraction that can hold all of these IDs. I also want this object to be able to be easily modified to add new IDs.
This is what I have so far:
class action: # abstraction for all IDs in an action
def __init__(self, dID=".", cID='.', dTp='.', dVs='.', mcID='.', aID='.', lID='.', pID='.', uID='.', uSe='.', udTp='.', componentID='.', eCx='.', eUr='.', eTp='.', rUrl='.', sec='.', oID='.', oVa='.', oCu='.', sID='.', saID='.', socNetworkUserID='.'):
self._dID = dID
self._cID = cID
self._dTp = dTp
self._dVs = dVs
self._mcID = mcID
self._aID = aID
self._lID = lID
self._pID = pID
self._uID = uID #Display and Insights
self._uSe = uSe
self._udTp = udTp
self._componentID = componentID
self._eCx = eCx #Display and Insights
self._eUr = eUr
self._eTp = eTp #Display and Insights
self._rUrl = rUrl
self._sec = sec
self._oID = oID #Display and Insights
self._oVa = oVa
self._oCu = oCu
self._sID = sID
self._saID = saID
self._socNetworkUserID = socNetworkUserID
self._empty_params = []
def insert_id_val(self, name, value): #Utility method
return
def insert_id_val_display(self, name, value):
item_map = {"dID" : _dID, "cID" : _cID, "dTp" : _dTp, "dVs" : _dVs,
"mcID" : _mcID, "aID" : _aID, "lID" : _lID, "pID" : _pID,
"uID" : _uID, "uSe" : _uSe, "udTp" : _udTp, "componentID" : _componentID,
"eCx" : _eCx,"eUr" : _eUr, "eTp" : _eTp, "rUrl" : _rUrl,
"sec" : _sec, "oID" : _oID, "oVa" : _oVa, "oCu" : _oCu,
"socNetworkUserID" : _socNetworkUserID, "_" : _empty_params}
self.item_map[name] = value
def insert_id_val_insights(self, name, value):
item_map = {"eTp" : self._eTp, "eCx" : self._eCx, "uID" : self._uID, "oID" : self._oID,
"sID" : self._sID, "saID" : self._saID}
item_map[name] = value
I tried two ways of doing type dispatching with two different ways, in the two different insert functions, but they both error. I have seen examples where people do things like Type-Dispatching Example
but they don't show how to use type dispatching with setting, only getting.
My implementation gives:
global name '_dID' is not defined at line 193 of program.py
How does one use type dispatching with setting variables? Is it even possible? If not, how else can I quickly make an abstraction like the one above without having to type a bunch of if statements?
EDIT: To add, I cannot instantiate the object with all of the IDs as their values are not known at instantiation.
I'm really not sure what you're asking here, or what exactly you mean by type dispatching. If you're asking about how to dynamically set attributes in a class, you can easily do that with **kwargs:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
and now you can instantiate it with action(dID='foo', oID='bar') and refer internally to self.dID and self.oID.

suds SOAP call parameters

I am new to python and using suds to consume data from a SOAP service. I face this error while making a call to the service. Other calls which do not require me to send some parameters are working fine. However this service is throwing me the following error.
Can somebody please help me in understanding what this error is?
primary is just a property of class devline and its a boolean.
criteria = connection.factory.create('criteria')
devline = connection.factory.create('devline')
devline.primary = True
devline.source ="abc"
devline.name = "xyz"
devline.hybridType = "xyz"
criteria.primaryDevline = devline
criteria.pairedDevlines = []
criteria.criteriaNumber = None
Criteria looks like this:
(criteriaKey){
primaryDevline =
(devline){
primary = None
sourceBase = None
devlineName = None
hybridType =
(hybridType){
value = None
}
}
pairedDevlines[] = <empty>
criteriaNumber = None
}
When I try to call the service using response = connection.service.somemethod(criteria,criteriaState.PUBLISHED)
I get the following error:
self = <suds.mx.literal.Literal instance at 0x103fd8998>
content = (Content){
tag = "primary"
value = True
type = None
}
> ???
E TypeNotFound: Type not found: 'primary'
File "/Users/nkimidi/projects/firstws/cca/ezcommit-client/test/unit/service/build/bdist.macosx-10.8-intel/egg/suds/mx/literal.py", line 87
TypeNotFound
=========================== 1 failed in 0.27 seconds ===========================
this started working when i used devline as a dictionary instead of the method used above. 1 days was wasted in this.
Instead of this:
devline.primary = True
devline.source ="abc"
devline.name = "xyz"
devline.hybridType = "xyz"
cKey.primaryDevline = devline
cKey.pairedDevlines = []
cKey.criteriaNumber = None
This snippet has worked:
devline = {
'primary':1,
'sourceBase':sourceBase,
'devlineName' : devlineName,
'hybridType' : hybridType
}
cKey['primaryDevline'] = devline
cKey['pairedDevlines'] = []
cKey['criteriaNumber'] = None
Documentation for suds says any of the methods would work, but the previous did not work for me.

Categories