Skip to content

Commit 293edab

Browse files
committed
added working addCollectionItems to restapi.py, non-working dropCollectionItems method, small mods in prep for 1.2.1 release including functionality in build_dist.py to create a PKG-INFO file with appropriate content
1 parent 6ed5185 commit 293edab

5 files changed

Lines changed: 174 additions & 53 deletions

File tree

MANIFEST.in

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,6 @@
1-
LICENSE
2-
README.short
3-
README.rst
4-
setup.py
5-
template.cfg
6-
rallyfire.py
7-
pyral/__init__.py
8-
pyral/config.py
9-
pyral/context.py
10-
pyral/entity.py
11-
pyral/hydrate.py
12-
pyral/rallyresp.py
13-
pyral/restapi.py
14-
examples/getitem.py
15-
examples/periscope.py
16-
examples/showdefects.py
17-
examples/statecounts.py
18-
examples/crtask.py
19-
examples/uptask.py
20-
examples/typedef.py
21-
examples/repoitems.py
22-
examples/wkspcounts.py
23-
examples/builddefs.py
24-
examples/creattach.py
25-
examples/get_attachments.py
26-
examples/get_schedulable_artifacts.py
27-
examples/get_schema.py
28-
examples/defrevs.py
29-
examples/add_tcrs.py
30-
examples/updtag.py
1+
include LICENSE README.short README.rst setup.py
2+
include template.cfg rallyfire.py
3+
recursive-include pyral *.py
4+
recursive-include doc *.txt *.html *.js *.css *.png *.gif
5+
recursive-include examples *.py
6+

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,13 +274,13 @@ Prerequisites
274274
Versions
275275
--------
276276

277-
1.2.1
277+
**1.2.1**
278278
Added mention that the six package is required.
279279
Fixed context setup for proper handling when a user has no default workspace/project settings.
280280
Corrected handling of allowedValues for attributes when the single allowedValue is a boolean value.
281281
Added an allowedValues.py example script.
282282

283-
1.2.0
283+
**1.2.0**
284284
Support for Python 3.5.x
285285
Begin deprecation sequence for pinging the Rally server before the connection attempt,
286286
initially with this version, allow option on instantiation to bypass ping.

build_dist.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
VERSION = "1.2.1"
1515

1616
AUX_FILES = ['MANIFEST.in',
17+
'PKG-INFO',
1718
'LICENSE',
1819
'README.short',
1920
'README.rst',
@@ -76,6 +77,11 @@
7677
################################################################################
7778

7879
def main(args):
80+
pkgcfg = package_meta('setup.py')
81+
#peek_in_to_pkg(pkgcfg)
82+
pkg_info = pkg_info_content(pkgcfg)
83+
pifi = save_pkg_info(".", 'PKG-INFO', pkg_info)
84+
7985
tarball = make_tarball(PACKAGE_NAME, VERSION, AUX_FILES, EXAMPLES, DOC_FILES)
8086
print tarball
8187

@@ -94,6 +100,74 @@ def main(args):
94100

95101
################################################################################
96102

103+
def package_meta(filename):
104+
import imp
105+
106+
if not os.path.exists(filename):
107+
raise Exception('No such file: %s' % filename)
108+
with open(filename, 'r') as pcf:
109+
content = pcf.read()
110+
chunk, setup = re.split('setup\(', content, maxsplit=1, flags=re.M)
111+
consties = [line for line in chunk.split("\n")
112+
if (len(line) > 0 and line[0] == " ") or re.search(r'^[A-Z]', line)]
113+
assignments = "\n".join(consties)
114+
115+
#print(assignments)
116+
pkgcfg = imp.new_module('pkgcfg')
117+
exec(assignments, pkgcfg.__dict__)
118+
sys.modules['pkgcfg'] = pkgcfg
119+
return pkgcfg
120+
121+
################################################################################
122+
123+
def indentified_text(source_body):
124+
"""
125+
The source_body should be a single string with embedded newline chars.
126+
This function splits the string on the newline chars, yielding a list
127+
of strings. The indentation should only be performed lines after the first
128+
line. The first line shall have no indentation performed.
129+
Return the result as a single string.
130+
"""
131+
lines = source_body.split("\n")
132+
indented = [' %s' % line for ix, line in enumerate(lines) if ix > 0]
133+
indented.insert(0, lines[0])
134+
return "\n".join(indented)
135+
136+
137+
def pkg_info_content(pkgcfg):
138+
with open(pkgcfg.SHORT_DESCRIPTION, 'r') as sdf:
139+
short_desc = indentified_text(sdf.read())
140+
meta_ver = 'Metadata-Version: 1.1'
141+
name = 'Name: %s' % pkgcfg.PACKAGE
142+
version = 'Version: %s' % pkgcfg.VERSION
143+
summary = 'Summary: %s' % pkgcfg.OFFICIAL_NAME
144+
homepage = 'Home-page: %s' % pkgcfg.GITHUB_SITE
145+
author = 'Author: %s' % pkgcfg.AUTHOR
146+
license = 'License: %s' % pkgcfg.LICENSE
147+
download = 'Download-URL: %s' % pkgcfg.DOWNLOADABLE_ZIP
148+
desc = 'Description: %s' % short_desc
149+
keywords = 'Keywords: %s' % ",".join(pkgcfg.KEYWORDS)
150+
requires = ['Requires: %s' % reqmt for reqmt in pkgcfg.REQUIRES]
151+
platform = 'Platform: %s' % pkgcfg.PLATFORM
152+
classifiers = ['Classifier: %s' % item for item in pkgcfg.CLASSIFIERS]
153+
154+
pki_items = [meta_ver, name, version, summary, homepage, author, license,
155+
download, desc, keywords,
156+
"\n".join(requires), platform, "\n".join(classifiers)
157+
]
158+
pkg_info = "\n".join(pki_items)
159+
return pkg_info
160+
161+
162+
def save_pkg_info(directory, filename, pkg_info):
163+
full_path = os.path.join(directory, filename)
164+
with open(full_path, 'w') as pif:
165+
pif.write(pkg_info)
166+
pif.write("\n")
167+
return full_path
168+
169+
################################################################################
170+
97171
def make_tarball(pkg_name, pkg_version, base_files, example_files, doc_files):
98172

99173
base_dir = '%s-%s' % (pkg_name, pkg_version)

pyral/restapi.py

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -923,7 +923,7 @@ def getCollection(self, collection_url, **kwargs):
923923
in the collection.
924924
"""
925925
context = self.contextHelper.currentContext()
926-
# craven ugly hackiness...
926+
# craven ugly hackiness to support calls triggered from within ContextHelper.check ...
927927
if not '?fetch=' in collection_url:
928928
collection_url = "%s?pagesize=%d&start=1" % (collection_url, MAX_PAGESIZE)
929929
resource = collection_url
@@ -943,6 +943,70 @@ def getCollection(self, collection_url, **kwargs):
943943
return rally_rest_response
944944

945945

946+
def addCollectionItems(self, target, items):
947+
"""
948+
Given a target which is a hydrated RallyEntity instance having a valid _type
949+
and a items which is a list of hydrated Rally Entity instances
950+
all of the same _type, construct a valid AC WSAPI collection url and
951+
issue a POST request to that URL supplying the _greased items refs as the
952+
payload.
953+
"""
954+
if not items: return None
955+
auth_token = self.obtainSecurityToken()
956+
target_type = target._type
957+
item_type = items[0]._type
958+
resource = "%s/%s/%ss/add" % (target_type, target.oid, item_type)
959+
collection_url = '%s/%s?fetch=Name&key=%s' % (self.service_url, resource, auth_token)
960+
#print(collection_url)
961+
payload = {"CollectionItems":[{'_ref' : "%s/%s" % (str(item._type), str(item.oid))}
962+
for item in items]}
963+
#print(payload)
964+
#print("-------------------------------")
965+
response = self.session.post(collection_url, data=json.dumps(payload), headers=RALLY_REST_HEADERS)
966+
#print(response.text)
967+
#print(response.reason)
968+
#print(response.content)
969+
context = self.contextHelper.currentContext()
970+
response = RallyRESTResponse(self.session, context, resource, response, "shell", 0)
971+
added_items = [str(item[u'Name']) for item in response.data[u'Results']]
972+
return added_items
973+
974+
def dropCollectionItems(self, target, items):
975+
"""
976+
Given a target which is a hydrated RallyEntity instance having a valid _type
977+
and a items which is a list of hydrated Rally Entity instances
978+
all of the same _type, construct a valid AC WSAPI collection url and
979+
issue a POST request to that URL supplying the item refs in an appropriate
980+
JSON structure as the payload.
981+
"""
982+
if not items: return None
983+
auth_token = self.obtainSecurityToken()
984+
target_type = target._type
985+
item_type = items[0]._type
986+
resource = "%s/%s/%ss/delete" % (target_type, target.oid, item_type)
987+
collection_url = '%s/%s?key=%s' % (self.service_url, resource, auth_token)
988+
print(collection_url)
989+
payload = {"CollectionItems":[{'_ref' : "%s/%s" % (str(item._type), str(item.oid))}
990+
for item in items]}
991+
print(payload)
992+
print("-------------------------------")
993+
response = self.session.post(collection_url, data=json.dumps(payload), headers=RALLY_REST_HEADERS)
994+
print(response.status_code)
995+
print(response.reason)
996+
#print(response.text)
997+
#print(" - - - - - - - - - - - - - - - - - -")
998+
#print(response.content)
999+
print(" - - - - - - - - - - - - - - - - - -")
1000+
return response.reason
1001+
1002+
#errors = response.errors
1003+
#context = self.contextHelper.currentContext()
1004+
#response = RallyRESTResponse(self.session, context, resource, response, "shell", 0)
1005+
#errors = [item[u'Errors'] for item in response.data[u'OperationResult']]
1006+
#errors = [item[u'Errors'] for item in response.data[u'QueryResult']]
1007+
#return errors
1008+
1009+
9461010
def put(self, entityName, itemData, workspace='current', project='current', **kwargs):
9471011
"""
9481012
Given a Rally entityName, a dict with data that the newly created entity should contain,

setup.py

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,33 @@
1919
PKG_URL_NAME = 'python-toolkit-rally-rest-api'
2020
AUTHOR = 'Kip Lehman (Rally Software Development)'
2121
AUTHOR_EMAIL = 'klehman@rallydev.com'
22+
LICENSE = 'BSD'
2223
GITHUB_SITE = 'https://github.com/RallyTools/RallyRestToolkitForPython'
2324
GITHUB_DISTS = '%s/raw/master/dists' % GITHUB_SITE
2425
DOWNLOADABLE_ZIP = '%s/%s-%s.zip' % (GITHUB_DISTS, PACKAGE, VERSION)
26+
SHORT_DESCRIPTION = 'README.short'
27+
FULL_DESCRIPTION = 'README.rst'
28+
KEYWORDS = ['rally', 'agilecentral', 'api']
2529

2630
MINIMUM_REQUESTS_VERSION = '2.8.1'
31+
REQUIRES = ['six',
32+
'requests>=%s' % MINIMUM_REQUESTS_VERSION
33+
]
34+
PLATFORM = 'any'
35+
36+
CLASSIFIERS = [ 'Development Status :: 5 - Production/Stable',
37+
'Environment :: Web Environment',
38+
'Intended Audience :: Developers',
39+
'License :: OSI Approved :: BSD License',
40+
'Operating System :: OS Independent',
41+
'Programming Language :: Python',
42+
'Programming Language :: Python :: 2.6',
43+
'Programming Language :: Python :: 2.7',
44+
'Programming Language :: Python :: 3.5',
45+
'Topic :: Internet :: WWW/HTTP',
46+
'Topic :: Software Development :: Libraries',
47+
]
48+
DOCUMENTATION = 'http://pyral.readthedocs.io/en/latest/'
2749

2850
setup(name=PACKAGE,
2951
packages=[PACKAGE],
@@ -33,25 +55,10 @@
3355
author_email=AUTHOR_EMAIL,
3456
url=GITHUB_SITE,
3557
download_url=DOWNLOADABLE_ZIP,
36-
long_description=open('README.rst').read(),
37-
license='BSD',
38-
keywords=['rally', 'agilecentral', 'api'],
39-
install_requires=['requests>=%s' % MINIMUM_REQUESTS_VERSION,
40-
'six'
41-
],
42-
classifiers=[
43-
'Development Status :: 5 - Production/Stable',
44-
'Environment :: Web Environment',
45-
'Intended Audience :: Developers',
46-
'License :: OSI Approved :: BSD License',
47-
'Operating System :: OS Independent',
48-
'Programming Language :: Python',
49-
'Programming Language :: Python :: 2.6',
50-
'Programming Language :: Python :: 2.7',
51-
'Programming Language :: Python :: 3.5',
52-
'Topic :: Internet :: WWW/HTTP',
53-
'Topic :: Software Development :: Libraries',
54-
],
55-
#documentation='http://pyral.readthedocs.io/en/latest/'
58+
long_description=open(FULL_DESCRIPTION, 'r').read(),
59+
license=LICENSE,
60+
keywords=KEYWORDS,
61+
install_requires=REQUIRES,
62+
classifiers=CLASSIFIERS
5663
)
5764

0 commit comments

Comments
 (0)