aboutsummaryrefslogtreecommitdiffstats
path: root/gsxws.py
diff options
context:
space:
mode:
Diffstat (limited to 'gsxws.py')
-rwxr-xr-xgsxws.py184
1 files changed, 100 insertions, 84 deletions
diff --git a/gsxws.py b/gsxws.py
index 8afd359..256d53b 100755
--- a/gsxws.py
+++ b/gsxws.py
@@ -5,24 +5,24 @@
"""
Copyright (c) 2013, Filipp Lepalaan All rights reserved.
-Redistribution and use in source and binary forms, with or without modification,
+Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
-- Redistributions of source code must retain the above copyright notice,
+- Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
-- Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation
+- Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
-AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
@@ -32,7 +32,6 @@ import json
import suds
import base64
import urllib
-import urlparse
import hashlib
import tempfile
@@ -84,6 +83,7 @@ ENVIRONMENTS = (
('it', "Testing"),
)
+
def validate(value, what=None):
"""
Tries to guess the meaning of value or validate that
@@ -95,29 +95,31 @@ def validate(value, what=None):
raise ValueError('%s is not valid input')
rex = {
- 'partNumber' : r'^([A-Z]{1,2})?\d{3}\-?(\d{4}|[A-Z]{2})(/[A-Z])?$',
- 'serialNumber' : r'^[A-Z0-9]{11,12}$',
- 'eeeCode' : r'^[A-Z0-9]{3,4}$',
- 'returnOrder' : r'^7\d{9}$',
- 'repairNumber' : r'^\d{12}$',
- 'dispatchId' : r'^G\d{9}$',
- 'alternateDeviceId' : r'^\d{15}$',
+ 'partNumber': r'^([A-Z]{1,2})?\d{3}\-?(\d{4}|[A-Z]{2})(/[A-Z])?$',
+ 'serialNumber': r'^[A-Z0-9]{11,12}$',
+ 'eeeCode': r'^[A-Z0-9]{3,4}$',
+ 'returnOrder': r'^7\d{9}$',
+ 'repairNumber': r'^\d{12}$',
+ 'dispatchId': r'^G\d{9}$',
+ 'alternateDeviceId': r'^\d{15}$',
'diagnosticEventNumber': r'^\d{23}$',
- 'productName' : r'^i?Mac',
+ 'productName': r'^i?Mac',
}
-
+
for k, v in rex.items():
if re.match(v, value):
result = k
return (result == what) if what else result
+
def get_format(locale=LOCALE):
df = open(os.path.join(os.path.dirname(__file__), 'langs.json'), 'r')
data = json.load(df)
return data[locale]
+
class GsxObject(object):
"""
The thing that gets sent to and from GSX
@@ -127,7 +129,7 @@ class GsxObject(object):
method = 'Authenticate' # The SOAP method to call on the GSX server
def __init__(self, *args, **kwargs):
-
+
formats = get_format()
# native types are not welcome here :)
@@ -138,7 +140,7 @@ class GsxObject(object):
kwargs[k] = v.strftime(formats['tf'])
if isinstance(v, bool):
kwargs[k] = 'Y' if v else 'N'
-
+
self.data = kwargs
if CLIENT is not None:
@@ -489,21 +491,22 @@ class Diagnostics(GsxObject):
result = CLIENT.service.FetchIOSDiagnostic(dt)
except suds.WebFault, e:
raise GsxError(fault=e)
-
+
root = ET.fromstring(result).findall('*//%s' % 'FetchIOSDiagnosticResponse')[0]
else:
dt = self._make_type('ns3:fetchRepairDiagnosticRequestType')
dt.lookupRequestData = self.data
-
+
try:
result = CLIENT.service.FetchRepairDiagnostic(dt)
except suds.WebFault, e:
raise GsxError(fault=e)
root = ET.fromstring(result).findall('*//%s' % 'FetchRepairDiagnosticResponse')[0]
-
+
return GsxResponse.Process(root)
+
class Order(GsxObject):
def __init__(self, type='stocking', *args, **kwargs):
super(Order, self).__init__(*args, **kwargs)
@@ -512,7 +515,7 @@ class Order(GsxObject):
def add_part(self, part_number, quantity):
self.data['orderLines'].append({
'partNumber': part_number, 'quantity': quantity
- })
+ })
def submit(self):
dt = CLIENT.factory.create('ns1:createStockingOrderRequestType')
@@ -525,27 +528,28 @@ class Order(GsxObject):
except suds.WebFault, e:
raise GsxError(fault=e)
+
class Returns(GsxObject):
def __init__(self, order_number=None, *args, **kwargs):
super(Returns, self).__init__(*args, **kwargs)
-
+
if order_number is not None:
self.data['returnOrderNumber'] = order_number
def get_pending(self):
"""
- The Parts Pending Return API returns a list of all parts that
- are pending for return, based on the search criteria.
+ The Parts Pending Return API returns a list of all parts that
+ are pending for return, based on the search criteria.
"""
dt = self._make_type('ns1:partsPendingReturnRequestType')
dt.repairData = self.data
-
+
return self.submit('PartsPendingReturn', dt, 'partsPendingResponse')
def get_report(self):
"""
- The Return Report API returns a list of all parts that are returned
- or pending for return, based on the search criteria.
+ The Return Report API returns a list of all parts that are returned
+ or pending for return, based on the search criteria.
"""
dt = self._make_type('ns1:returnReportRequestType')
dt.returnRequestData = self.data
@@ -581,9 +585,14 @@ class Returns(GsxObject):
el = ET.fromstring(result).findall('*//%s' % 'returnLabelData')[0]
for r in el.iter():
+
k, v = r.tag, r.text
+
if k in ['packingList', 'proformaFileData', 'returnLabelFileData']:
v = base64.b64decode(v)
+ of = tempfile.NamedTemporaryFile(suffix=".pdf", delete=False)
+ of.write(v)
+ v = of.name
setattr(rd, k, v)
@@ -591,32 +600,33 @@ class Returns(GsxObject):
def get_proforma(self):
"""
- The View Bulk Return Proforma API allows you to view
+ The View Bulk Return Proforma API allows you to view
the proforma label for a given Bulk Return Id.
- You can create a parts bulk return
- by using the Register Parts for Bulk Return API.
+ You can create a parts bulk return
+ by using the Register Parts for Bulk Return API.
"""
pass
def register_parts(self, parts):
"""
- The Register Parts for Bulk Return API creates a bulk return for
+ The Register Parts for Bulk Return API creates a bulk return for
the registered parts.
The API returns the Bulk Return Id with the packing list.
"""
- dt = self._make_type('ns1:registerPartsForBulkReturnRequestType')
+ dt = self._make_type("ns1:registerPartsForBulkReturnRequestType")
self.data['bulkReturnOrder'] = parts
dt.bulkPartsRegistrationRequest = self.data
- result = self.submit('RegisterPartsForBulkReturn', dt, 'bulkPartsRegistrationData')
+ result = self.submit("RegisterPartsForBulkReturn", dt, "bulkPartsRegistrationData")
pdf = base64.b64decode(result.packingList)
- of = tempfile.NamedTemporaryFile(suffix='.pdf', delete=False)
+ of = tempfile.NamedTemporaryFile(suffix=".pdf", delete=False)
of.write(pdf)
result.packingList = of.name
return result
+
class Part(GsxObject):
def lookup(self):
lookup = Lookup(**self.data)
@@ -638,6 +648,7 @@ class Part(GsxObject):
except Exception, e:
raise GsxError('Failed to fetch part image: %s' % e)
+
class Escalation(GsxObject):
def create(self):
"""
@@ -657,14 +668,15 @@ class Escalation(GsxObject):
dt.escalationRequest = self.data
return self.submit("UpdateGeneralEscalation", dt, "escalationConfirmation")
+
class Repair(GsxObject):
-
+
dt = 'ns6:repairLookupInfoType'
request_dt = 'ns1:repairLookupRequestType'
def __init__(self, number=None, *args, **kwargs):
super(Repair, self).__init__(*args, **kwargs)
-
+
if number is not None:
self.data['repairConfirmationNumber'] = number
@@ -677,11 +689,11 @@ class Repair(GsxObject):
dt.repairData = self.data
return self.submit('CreateCarryInRepair', dt, 'repairConfirmation')
-
+
def create_cnd(self):
"""
- The Create CND Repair API allows Service Providers to create a repair
- whenever the reported issue cannot be duplicated, and the repair
+ The Create CND Repair API allows Service Providers to create a repair
+ whenever the reported issue cannot be duplicated, and the repair
requires no parts replacement.
N01 Unable to Replicate
N02 Software Update/Issue
@@ -696,10 +708,10 @@ class Repair(GsxObject):
def update_carryin(self, newdata):
"""
Description
- The Update Carry-In Repair API allows the service providers
- to update the existing open carry-in repairs.
- This API assists in addition/deletion of parts and addition of notes
- to a repair. On successful update, the repair confirmation number and
+ The Update Carry-In Repair API allows the service providers
+ to update the existing open carry-in repairs.
+ This API assists in addition/deletion of parts and addition of notes
+ to a repair. On successful update, the repair confirmation number and
quote for any newly added parts would be returned.
In case of any validation error or unsuccessful update, a fault code is issued.
@@ -710,7 +722,7 @@ class Repair(GsxObject):
RFPU Ready for Pickup
"""
dt = self._make_type('ns1:updateCarryInRequestType')
-
+
# Merge old and new data (old data should have Dispatch ID)
dt.repairData = dict(self.data.items() + newdata.items())
@@ -719,31 +731,31 @@ class Repair(GsxObject):
def update_sn(self, parts):
"""
Description
- The Update Serial Number API allows the service providers to
- update the module serial numbers.
+ The Update Serial Number API allows the service providers to
+ update the module serial numbers.
Context:
- The API is not applicable for whole unit replacement
+ The API is not applicable for whole unit replacement
serial number entry (see KGB serial update).
"""
dt = self._make_type('ns1:updateSerialNumberRequestType')
repairData = {'repairConfirmationNumber': self.data.get('dispatchId')}
repairData['partInfo'] = parts
dt.repairData = repairData
-
+
return self.submit('UpdateSerialNumber', dt, 'repairConfirmation')
def update_kgb_sn(self, sn):
"""
Description:
- The KGB Serial Number Update API is always to be used on
- whole unit repairs that are in a released state.
- This API allows users to provide the KGB serial number for the
- whole unit exchange repairs. It also checks for the privilege
- to create/ update whole unit exchange repairs
+ The KGB Serial Number Update API is always to be used on
+ whole unit repairs that are in a released state.
+ This API allows users to provide the KGB serial number for the
+ whole unit exchange repairs. It also checks for the privilege
+ to create/ update whole unit exchange repairs
before updating the whole unit exchange repair.
Context:
- The API is to be used on whole unit repairs that are in a released state.
+ The API is to be used on whole unit repairs that are in a released state.
This API can be invoked only after carry-in repair creation API.
"""
@@ -758,7 +770,7 @@ class Repair(GsxObject):
result = CLIENT.service.KGBSerialNumberUpdate(dt)
except suds.WebFault, e:
raise GsxError(fault=e)
-
+
root = ET.fromstring(result).findall('*//%s' % 'UpdateKGBSerialNumberResponse')
return GsxResponse.Process(root[0])
@@ -767,14 +779,14 @@ class Repair(GsxObject):
Description:
The Repair Lookup API mimics the front-end repair search functionality.
It fetches up to 2500 repairs in a given criteria.
- Subsequently, the extended Repair Status API can be used
- to retrieve more details of the repair.
+ Subsequently, the extended Repair Status API can be used
+ to retrieve more details of the repair.
"""
return Lookup(**self.data).repairs()
def mark_complete(self, numbers=None):
"""
- The Mark Repair Complete API allows a single or an array of
+ The Mark Repair Complete API allows a single or an array of
repair confirmation numbers to be submitted to GSX to be marked as complete.
"""
dt = self._make_type('ns1:markRepairCompleteRequestType')
@@ -788,9 +800,9 @@ class Repair(GsxObject):
def delete(self):
"""
- The Delete Repair API allows the service providers to delete
- the existing GSX Initiated Carry-In, Return Before Replace & Onsite repairs
- which are in Declined-Rejected By TSPS Approver state,
+ The Delete Repair API allows the service providers to delete
+ the existing GSX Initiated Carry-In, Return Before Replace & Onsite repairs
+ which are in Declined-Rejected By TSPS Approver state,
that do not have an active repair id.
"""
pass
@@ -811,8 +823,8 @@ class Repair(GsxObject):
def get_details(self):
"""
- The Repair Details API includes the shipment information
- similar to the Repair Lookup API.
+ The Repair Details API includes the shipment information
+ similar to the Repair Lookup API.
"""
dt = self._make_type('ns0:repairDetailsRequestType')
dt.dispatchId = self.data['dispatchId']
@@ -824,32 +836,34 @@ class Repair(GsxObject):
try:
url = re.sub('<<TRKNO>>', p.deliveryTrackingNumber, p.carrierURL)
details.partsInfo[i].carrierURL = url
- except AttributeError, e:
+ except AttributeError:
pass
return details
+
class Communication(GsxObject):
def get_content():
"""
The Fetch Communication Content API allows the service providers/depot/carriers
- to fetch the communication content by article ID from the service news channel.
+ to fetch the communication content by article ID from the service news channel.
"""
def get_articles():
"""
The Fetch Communication Articles API allows the service partners
- to fetch all the active communication message IDs.
+ to fetch all the active communication message IDs.
"""
+
class Product(GsxObject):
-
+
dt = 'ns7:unitDetailType'
def __init__(self, serialNumber, *args, **kwargs):
super(Product, self).__init__(*args, **kwargs)
self.serialNumber = serialNumber
-
+
if SESSION:
self.dt.serialNumber = serialNumber
self.lookup = Lookup(serialNumber=self.serialNumber)
@@ -878,11 +892,10 @@ class Product(GsxObject):
result = self.submit("WarrantyStatus", dt, "warrantyDetailInfo")
return self._process(result)
-
def get_activation(self):
"""
- The Fetch iOS Activation Details API is used to
- fetch activation details of iOS Devices.
+ The Fetch iOS Activation Details API is used to
+ fetch activation details of iOS Devices.
"""
dt = self._make_type('ns3:fetchIOSActivationDetailsRequestType')
dt.serialNumber = self.serialNumber
@@ -908,6 +921,7 @@ class Product(GsxObject):
except Exception, e:
raise GsxError('Failed to fetch product image: %s' % e)
+
def init(env='ut', region='emea'):
"""
Initialize the SOAP client
@@ -931,12 +945,13 @@ def init(env='ut', region='emea'):
cache = CLIENT.options.cache
cache.setduration(weeks=1)
+
def connect(
user_id,
password,
- sold_to,
+ sold_to,
language='en',
- timezone='CEST',
+ timezone='CEST',
environment='ut',
region='emea',
locale=LOCALE):
@@ -944,7 +959,7 @@ def connect(
Establishes connection with GSX Web Services.
Returns the session ID of the new connection.
"""
-
+
global CACHE
global LOCALE
global SESSION
@@ -980,6 +995,7 @@ def connect(
except suds.WebFault, e:
raise GsxError(fault=e)
+
def logout():
CLIENT.service.Logout()
@@ -987,7 +1003,7 @@ if __name__ == '__main__':
import sys
import json
import argparse
-
+
parser = argparse.ArgumentParser(description='Communicate with GSX Web Services')
parser.add_argument('user_id')
@@ -997,5 +1013,5 @@ if __name__ == '__main__':
parser.add_argument('--timezone', default='CEST')
parser.add_argument('--environment', default='pr')
parser.add_argument('--region', default='emea')
-
+
args = parser.parse_args()