Skip to content
This repository was archived by the owner on Aug 28, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ All rights reserved.


============================
Python bindings to Upwork API
Upwork API
============================
These are Python bindings for Upwork Public API https://developers.upwork.com/
You can use the API to build apps that will help you:
Expand Down
7 changes: 7 additions & 0 deletions changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
Changelog
***************

.. _1.1.0:

Version 1.1.0
-------------
* Get Categories (V1) is now fully depricated
* Added new Activities API - :py:meth:`Assign to specific engagement the list of activities <upwork.routers.task.Task_V2.assign_to_engagement>`.

.. _1.0.2:

Version 1.0.2
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
readme.close()


VERSION = (1, 0, 1, 0, 0)
VERSION = (1, 1, 0, 0, 0)


def get_version():
Expand Down
2 changes: 1 addition & 1 deletion upwork/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

"""

VERSION = '1.0.1'
VERSION = '1.1.0'


def get_version():
Expand Down
240 changes: 4 additions & 236 deletions upwork/routers/provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,238 +43,6 @@ def get_provider_brief(self, provider_ciphertext):
result = self.get(url)
return result.get('profile', result)

def search_providers(self, data=None, page_offset=0, page_size=20,
order_by=None):
"""
Search Upwork providers.
NOTE: This call will be deprecated in favor to API V2
:py:meth:`~upwork.routers.provider.Provider_V2.search_providers`

*Parameters:*
:data: A dict of the following parameters
(all parameters are optional):

:q: Search query, e.g. "python".
Any text that appears in a provider's profile

:c1: Category name.
Use
:py:meth:`~upwork.routers.provider.Provider.get_categories_metadata`
to get the list of currently valid categories

:c2: Subcategory, which is related to category (c1),
please use c2[] to specify a couple subcategories.
Use
:py:meth:`~upwork.routers.provider.Provider.get_categories_metadata`
to get available subcategories.

:fb: Feedback (adjusted score),
e.g. ``fb='2.0 - 2.9 Stars'``
This searches for providers who have an adjusted
feedback score equal or greater (up to 5) than the number
passed in this parameter (decimals are okay).

:hrs: (Total hours) This searches for providers who have
a total number of hours equal or greater to the number
passed in this parameter.

:ir: This boolean parameter is used in combination with
the total hours worked parameter, and searches providers
who have worked within the last six months.
"Yes" or "No" are the only valid searches.
Omitting this will default to 'No'.

:min: The provider's minimum rate they have charged
in the past. Excludes providers with a public rate
less than this amount.

:max: The provider's maximum rate they have charged
in the past. Excludes providers with a public rate
greater than this amount.

:loc: Country region. Limit your searches to a
specific country region. Possible values:
* 'Australasia'
* 'East Asia'
* 'Eastern Europe'
* 'North America'
* 'South Asia'
* 'Western Europe'
* 'Misc'

:pt: Provider type. Limit your search to independent
or affiliate providers. Possible values:
* 'Individual'
* 'Affiliated'
By default both types are returned.

:last: Limit your search to providers who were active
after the date passed in this parameter.
Dates should be formatted like: 07-13-2009

:test: Limit your search to providers who have passed
a specific test (based on the test id).
You can get available tests using
:py:meth:`~upwork.routers.provider.Provider.get_tests_metadata`
Only singe value is allowed.

:port: Limit your search to providers who have at least
this number of portfolio items.

:rdy: Only return Upwork ready providers.

:eng: Limit your results to providers who have
at least the rating passed in the parameter.
Only the following English levels are available
(no decimals): [1,2,3,4,5]

:ag: Agency reference. Limit your search to a specific agency.

:to: Search the provider profile title text only.
Possible values: 'yes'|'no', by default 'no'.

:g: Limit your search to a specific group.

:skills: Required skills. A name of the skill.
Multiple values are allowed as a comma-separated string

:page_offset: Start of page (number of results to skip) (optional)

:page_size: Page size (number of results) (optional: default 20)

:order_by: Sorting, in format
$field_name1;$field_name2;..$field_nameN;AD...A,
where 'A' means ascending, 'D' means descending,
the only available sort field as of now is "Date Created"

"""
url = 'search/providers'
if data is None:
data = {}

data['page'] = '{0};{1}'.format(page_offset, page_size)
if order_by is not None:
data['sort'] = order_by
result = self.get(url, data=data)
return result.get('providers', result)

def search_jobs(self, data=None,
page_offset=0, page_size=20, order_by=None):
"""
Search Upwork jobs.
NOTE: This call will be deprecated in favor to API V2
:py:meth:`~upwork.routers.provider.Provider_V2.search_jobs`

*Parameters:*
:data: A dict of the following parameters
(all parameters are optional):

:q: Query, e.g. "python",
search the text of the job's description.

:c1: Category name. Use Metadata API to get the list
of currently valid categories

:c2: Subcategory, which is related to category (c1),
please use c2[] to specify a couple subcategories

:qs: Skill required, single value or comma-separated list

:fb: Feedback (adjusted score). Limit your search to buyers
with at least a score of the number passed in this
parameter. Use the following values to filter by score:
* none = '0'
* 1 - 4 Scores = '10'
* 4 - 4.5 Scores = '40'
* 4.5 - 5 Scores = '45'
* 5.0 Scores = '50'

:min: Minimum budget

:max: Maximum budget

:t: Job type. Possible values are:
* 'Hourly'
* 'Fixed'

:wl: Hours per week. This parameter can only be used when
searching Hourly jobs. These numbers are
a little arbitrary, so follow the following parameters
in order to successfully use this parameter:
* As Needed < 10 Hours/Week = '0'
* Part Time: 10-30 hrs/week = '20'
* Full Time: 30+ hrs/week = '40'

:dur: Engagement duration. This parameter can only be used
when searching Hourly jobs. These numbers are
a little arbitrary, so follow the following parameters
in order to successfully use this parameter:
* Ongoing / More than 6 months = '26'
* 3 to 6 months = '13'
* 1 to 3 months = '4'
* Less than 1 month = '1'
* Less than 1 week = '0'

:dp: Date posted. Search jobs posted according to timeframe.
Use the following parameters to specify a timeframe:
* Any Timeframe = empty
* Last 24 hours = '0'
* Last 24 hours - 3 Days = '1'
* Last 3-7 Days = '3'
* Last 7-14 Days - '7'
* Last 14-30 Days - '14'
* > 30 Days - '30'

:st: Job status. Search for Canceled jobs, In Progress Jobs
and Completed Jobs. Defaults to Open Jobs.
Possible values:
* Open Jobs = 'Open'
* Jobs in Progress = 'In Progress'
* Completed Jobs = 'Completed'
* Canceled Jobs = 'Cancelled'

:tba: Total billed assignments.
Limit your search to buyers who completed at least
this number of paid assignments. Possible values:
* none = '0'
* has 1-5 billed assignments = '1'
* has 5-10 billed assignments = '5'
* has >10 billed assignments = '10'

:gr: Preferred group. Limits your search to buyers
in a particular group

:to: Search the provider profile title text only.
Possible values: 'yes'|'no', by default 'no'.

:page_offset: Start of page (number of results to skip) (optional)

:page_size: Page size (number of results) (optional: default 20)

:order_by: Sorting, in format
``$field_name1;$field_name2;..$field_nameN;AD...A``,
where A means 'Ascending', D means 'Descending',
e.g. ``date_posted;A``

"""
url = 'search/jobs'
if data is None:
data = {}
data['page'] = '{0};{1}'.format(page_offset, page_size)
if order_by is not None:
data['sort'] = order_by
result = self.get(url, data=data)
return result.get('jobs', result)

def get_categories_metadata(self):
"""
Returns list of all categories available for job/contractor profiles.

"""
url = 'metadata/categories'
result = self.get(url)
return result.get('categories', result)

def get_skills_metadata(self):
"""
Returns list of all skills available for job/contractor profiles.
Expand Down Expand Up @@ -371,12 +139,12 @@ def search_providers(self, data=None, page_offset=0, page_size=20):
:tests_top_30: Search for contractors that are
in top 30 for test

:category: Search for category of contractor profile.
:category2: Search for category of contractor profile.
Use
:py:meth:`~upwork.routers.provider.Provider.get_categories_metadata`
to get available categories

:subcategory: Search for subcategory of contractor profile.
:subcategory2: Search for subcategory of contractor profile.
Use
:py:meth:`~upwork.routers.provider.Provider.get_categories_metadata`
to get available categories
Expand Down Expand Up @@ -485,11 +253,11 @@ def search_jobs(self, data=None, page_offset=0, page_size=20):
:tests_top_30: Search for jobs that require provider to be
in top 30 for test

:category: Search for category of job profile
:category2: Search for category of job profile
See full list here:
https://developers.upwork.com/?lang=python#metadata_list-categories

:subcategory: Search for subcategory of job profile
:subcategory2: Search for subcategory of job profile
See full list here:
https://developers.upwork.com/?lang=python#metadata_list-categories
in the table "Changes"
Expand Down
31 changes: 31 additions & 0 deletions upwork/routers/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,12 @@ class Task_V2(Namespace):
api_url = 'tasks/'
version = 2

def _encode_task_codes(self, task_codes):
if isinstance(task_codes, (list, tuple)):
return ';'.join(str(c) for c in task_codes)
else:
return str(task_codes)

def list_engagement_activities(self, engagement_ref):
"""
Retrieve list of all activities assigned to the specific engagement.
Expand All @@ -440,3 +446,28 @@ def list_engagement_activities(self, engagement_ref):
url = 'tasks/contracts/{0}'.format(engagement_ref)
result = self.get(url)
return result

def assign_to_engagement(self, engagement_ref, task_codes=None):
"""Assign a list of activities to the existing engagement.

Note that activity will appear in contractor's team client
only if his engagement is assigned to the activity and
activities are activated for the ongoing contract.

This will override assigned engagements for the given activities.
For example, if you pass empty ``task_codes`` or just omit
this parameter, contractor engagement will be unassigned from
all Activities.

*Parameters:*
:engagement_ref: Engagement ID that will be assigned/unassigned
to the given list of Activities.

:task_codes: Task codes (must be a list, even of 1 item)

"""
task_codes = self._encode_task_codes(task_codes)
url = 'tasks/contracts/{0}'.format(engagement_ref)
data = {'tasks': task_codes}
result = self.put(url, data)
return result
Loading