Package: prospyr
A Python client library for ProsperWorks.Prospyr runs on Python 2.7 or Python 3.4+.InstallationpipinstallprospyrQuickstartIf you’ve used Django, Prospyr might feel strangely familiar.fromprospyrimportconnect,Person,Company# see https://www.prosperworks.com/developer_api/token_generation to obtain# a token.cn=connect(email='user@domain.tld',token='1aefcc3...')# collections can be ordered and sliced.newest_person=Person.objects.order_by('-date_modified')[0]# new records can be created.art=Person(name='Art Vandelay',emails=[{'email':'art@vandelayindustries.net','category':'work'}])art.create()# Art is local-only until .create() is called# related objects can be read and assignedart.company=Company.objects.all()[0]art.update()# and deleting works too.art.delete()ResourcesThe following ProsperWorks resources are supported by Prospyr:Account (read-only)Activity (read–only)ActivityTypeCompanyCustomerSource (read–only)IdentifierLeadLossReason (read–only)OpportunityPersonPipeline (read–only)PipelineStage (read–only)Task (read–only)User (read–only)Webhook (read-only)The following resources are not supported, but will still appear when
referenced by the supported resources above. In this case, they come only with
anidattribute.ProjectNote you will receive errors trying to deal with the Lead resource if the Leads
feature is not enabled in your ProsperWorks account. You can change this at
Settings / Customize ProsperWorks / Lead Management.UsageConnectingTo connect, you’ll need an email and token pertoken generation.fromprospyrimportconnectcn=connect(email='...',token='...')All reads are cached per–connection for five minutes. You can pass a custom
cache instance when connecting to ProsperWorks to change this behaviour.fromprospyrimportconnectfromprospyr.cacheimportNoOpCache,InMemoryCache# only cache the last requestcn=connect(email='...',token='...',cache=InMemoryCache(size=1))# no cachingcn=connect(email='...',token='...',cache=NoOpCache())You can also substitute your own custom cache here to use e.g. Redis or
memcached.Prospyr also supports multiple named connections. Provide aname='...'argument when callingconnect()and refer to the connection when
interacting with the API later, e.g.Person.objects.get(id=1,using='...').CreateYou can create new records in ProsperWorks.fromprospyrimportPersonsteve=Person(name='Steve Cognito',emails=[{'category':'work','email':'steve@example.org'}])# steve only exists locally at this stagesteve.id>>>None# now he exists remotely toosteve.create()>>>Truesteve.id>>>1ReadThere are two ways to read a single record from ProsperWorks. A new instance
can be fetched using the resource’sobjects.get()method, or you can callread()on an existing instance to have its attributes refreshed.fromprospyrimportPerson# a new instancesteve=Person.objects.get(id=1)steve.name>>>'Steve Cognito'# update an existing instancesteve=Person(id=1)steve.read()>>>Truesteve.name>>>'Steve Cognito'# as a special case, People can be read by email as well as ID:steve=Person.objects.get(email='steve@example.org')UpdateNote that “update” means to push an update to ProsperWorks using your local
data, rather than to refresh local data using ProsperWorks. In this example,
Steve is fetched from ProsperWorks and given a new title. Hey, congrats on the
promotion Steve.fromprospyrimportPersonsteve=Person.objects.get(id=1)steve.title='Chairman'steve.update()>>>TrueDeleteWhen Steve has reached the end of his useful lifespan, he can be deleted too.fromprospyrimportPersonsteve=Person.objects.get(id=1)steve.delete()>>>TrueOrderingResource collections can be ordered. Check theProsperWorks API documentationto learn which fields can be
ordered. However, Prospyr does check that the fields you argue are correct.fromprospyrimportPerson# oldest firstrs=Person.objects.order_by('date_modified')# newest first (note the hyphen)rs=Person.objects.order_by('-date_modified')# At this stage, no requests have been made. Results are lazily evaluated# and paging is handled transparently.# The results can be indexed and sliced like a Python list. Doing so forces# evaluation. The below causes the first page of results to be fetched.rs[0]>>><Person:SteveCognito># No request is required here, as the Bones was on the first page requested# above. The default page size is 200.rs[1]>>><Person:BonesJohannson># This result is on the second page, so another request is fired.rs[200]>>><Person:AlfonsTundra>OnceResultSetinstances have been evaluated they are cached for their
lifetime. However, thefilter()andorder_by()methods return newResultSetinstances which require fresh evaluation. While you are dealing
with a singleResultSet, it is safe to iterate and slice it as many times
as necessary.FilteringResource collections can be filtered. Check theProsperWorks API documentationto learn which filters can be
used. Prospyr doesnotcurrently validate your filter arguments, and note
that ProsperWorks does not either; if you make an invalid filter argument,
results will be returned as though you had not filtered at all.Multiple filters are logically ANDed together. A single call tofilter()with many parameters is equivalent to many calls with single parameters.fromprospyrimportCompanyactive=Company.objects.filter(minimum_interaction_count=10)active_in_china=active.filter(country='CN')# this is equivalentactive_in_china=Company.objects.filter(minimum_interaction_count=10,country='CN')As with ordering, filtered results are evaluated lazily and then cached
indefinitely. Re-ordering or re-filtering results in a newResultSetwhich
requires fresh evaluation.ProsperWorks’ “Secondary Resources”, such as Pipeline Stages, cannot be
filtered or ordered. These resources useListSetrather thanResultSetinstances; these only support theall()method:fromprospyrimportPipelineStagePipelineStage.objects.all()>>><ListSet:Qualifying,Quoted,...>AccountTheAccountresource represents the ProsperWorks account which you are
currently working with. The name of the account can be read like so:fromprospyrimportAccountaccount=Account.objects.get()account.name>>>'So-and-so Company'Collection Error HandlingProspyr validates data delivered from ProsperWorks when building representative
Python objects for local use. Because there are no documented details on the
validation that ProsperWorks itself uses, Prospyr’s validation rules are
sometimes incorrect or more strict than necessary. The author suspects that
sometimes ProsperWorks also delivers data that is simply invalid.This can cause exceptions to be raised when iterating over result sets (e.g.for person inPerson.objects.all()...) which prevent the remainder of the
collection from being accessed.To make your life easier while such a mismatch is corrected in Prospyr, you can
choose to have these validation errors collected instead of being raised:fromprospyrimportPersonerrs=[]forpersoninPerson.objects.store_invalid(errs).all():# ...iferrs:# handle errorsThe argument tostore_invalidmust, like a list, have a workingappendmethod. It will be filled withValidationErrorinstances which each haveerrors,raw_dataandresource_clsattributes.If your use–case allows you to correct the problem inraw_data, you can
recover like so:forerrinerrs:good_data=make_corrections(err.raw_data)instance=err.resource_cls.from_api_data(good_data)Testspipinstall-rdev-requirements# test using the current python interpretermaketest# test with all supported interpreterstox