surge-api 1.5.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Surge
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,172 @@
1
+ Metadata-Version: 2.1
2
+ Name: surge-api
3
+ Version: 1.5.7
4
+ Summary: Surge Python SDK
5
+ Home-page: https://github.com/surge-ai/surge-python
6
+ Author: Surge
7
+ Author-email: team@surgehq.ai
8
+ License: MIT
9
+ Description: # Surge Python SDK
10
+
11
+ The Surge Python SDK provides convenient access to the Surge API from applications written in the Python language.
12
+
13
+ ## Installation
14
+
15
+ Install this package by using pip:
16
+
17
+ ```bash
18
+ pip install --upgrade surge-api
19
+ ```
20
+
21
+ ### Requirements
22
+
23
+ * Python 3.6+
24
+
25
+ ## Usage
26
+
27
+ Documentation and and examples are available [here](https://app.surgehq.ai/docs/api#).
28
+
29
+ ### Authentication
30
+
31
+ The library needs to be configured with your account's API key which is available in your Surge Profile. Set `surge.api_key` to its value:
32
+
33
+ ```python
34
+ import surge
35
+ surge.api_key = "YOUR API KEY"
36
+ ```
37
+ Or set the API key as an environment variable:
38
+
39
+ ```bash
40
+ export SURGE_API_KEY=<YOUR API KEY>
41
+ ```
42
+
43
+ ### Projects
44
+
45
+ Once the API key has been set, you can list all of the Projects under your Surge account or retrieve a specific Project by its ID.
46
+
47
+ ```python
48
+ # List your Projects
49
+ projects = surge.Project.list()
50
+
51
+ # Print the name of the first Project
52
+ print(projects[0].name)
53
+
54
+ # Retrieve a specific Project
55
+ project = surge.Project.retrieve("076d207b-c207-41ca-b73a-5822fe2248ab")
56
+
57
+ # print the number of tasks in that Project
58
+ print(project.num_tasks)
59
+ ```
60
+ If you have an existing project, you can use it as a template to get a new batch of data annotated.
61
+ You can add new labeling tasks from a CSV or with a list of dictionaries.
62
+
63
+ ```python
64
+ # Create a project from a template
65
+ template_project_id = "076d207b-c207-41ca-b73a-5822fe2248ab"
66
+ project = surge.Project.create("My Labeling Project (July 2023 Batch)", template_id=template_project_id)
67
+
68
+ # Add data from a CSV file
69
+ project.create_tasks_from_csv('my_data.csv')
70
+
71
+ # Or add data directly
72
+ tasks = project.create_tasks([{
73
+ "company": "Surge",
74
+ "city": "San Francisco",
75
+ "state": "CA"
76
+ }])
77
+
78
+ # Launch the project to send it to the Surge workforce
79
+ project.launch()
80
+ ```
81
+
82
+
83
+ Or you can create a new project from scratch by creating your own template and list of Question:
84
+
85
+ ```python
86
+ from surge.questions import FreeResponseQuestion, MultipleChoiceQuestion, CheckboxQuestion
87
+
88
+ # Create a new Project
89
+ free_response_q = FreeResponseQuestion(
90
+ text="What is this company's website?",
91
+ label="")
92
+
93
+ multiple_choice_q = MultipleChoiceQuestion(
94
+ text="What category does this company belong to?",
95
+ label="Category",
96
+ options=["Tech", "Sports", "Gaming"])
97
+
98
+ checkbox_q = CheckboxQuestion(
99
+ text="Check all the social media accounts this company has",
100
+ label="",
101
+ options=["Facebook", "Twitter", "Pinterest", "Google+"])
102
+
103
+ fields_template_text = '''
104
+ <p>Company: {{company}}</p>
105
+ '''
106
+
107
+ project = surge.Project.create(
108
+ name="Categorize this company",
109
+ instructions="You will be asked to categorize a company.",
110
+ questions=[free_response_q, multiple_choice_q, checkbox_q],
111
+ callback_url="https://customer-callback-url/",
112
+ fields_template=fields_template_text,
113
+ num_workers_per_task=3)
114
+ ```
115
+
116
+ ### Tasks
117
+
118
+ You can create new Tasks for a project, list all of the Tasks in a given project, or retrieve a specific Task given its ID.
119
+
120
+ ```python
121
+ # Create Tasks for the new Project
122
+ tasks_data = [{"id": 1, "company": "Surge AI"}, {"id": 2, "company":"Twitch TV"}]
123
+ tasks = project.create_tasks(tasks_data)
124
+
125
+ # List all Tasks in the Project
126
+ all_tasks = project.list_tasks()
127
+
128
+ # Retrieve a specific Task
129
+ task = surge.Task.retrieve(task_id = "eaa44610-c8f6-4480-b746-28b6c8defd4d")
130
+
131
+ # Print the fields of that Task
132
+ print(task.fields)
133
+ ```
134
+
135
+ You can also create Tasks in bulk by uploading a local CSV file. The header of the CSV file must specify the fields that are used in your Tasks.
136
+
137
+ | id | company |
138
+ | :--- | :----: |
139
+ | 1 | Surge AI |
140
+ | 2 | Twitch TV |
141
+
142
+ ```python
143
+ # Create Tasks in bulk via CSV file
144
+ file_path = "./companies_to_classify.csv"
145
+ tasks = project.create_tasks_from_csv(file_path)
146
+ ```
147
+
148
+
149
+ ## Development
150
+
151
+ The test suite depends on `pytest`, which you can install using pip:
152
+
153
+ ```bash
154
+ pip install pytest
155
+ ```
156
+
157
+ To run tests from the command line:
158
+
159
+ ```bash
160
+ # Run all tests
161
+ pytest
162
+
163
+ # Run tests in a specific file
164
+ pytest tests/test_projects.py
165
+
166
+ # Run a specific test
167
+ pytest tests/test_projects.py::test_init_complete
168
+ ```
169
+
170
+ Platform: UNKNOWN
171
+ Requires-Python: >=3.6
172
+ Description-Content-Type: text/markdown
@@ -0,0 +1,160 @@
1
+ # Surge Python SDK
2
+
3
+ The Surge Python SDK provides convenient access to the Surge API from applications written in the Python language.
4
+
5
+ ## Installation
6
+
7
+ Install this package by using pip:
8
+
9
+ ```bash
10
+ pip install --upgrade surge-api
11
+ ```
12
+
13
+ ### Requirements
14
+
15
+ * Python 3.6+
16
+
17
+ ## Usage
18
+
19
+ Documentation and and examples are available [here](https://app.surgehq.ai/docs/api#).
20
+
21
+ ### Authentication
22
+
23
+ The library needs to be configured with your account's API key which is available in your Surge Profile. Set `surge.api_key` to its value:
24
+
25
+ ```python
26
+ import surge
27
+ surge.api_key = "YOUR API KEY"
28
+ ```
29
+ Or set the API key as an environment variable:
30
+
31
+ ```bash
32
+ export SURGE_API_KEY=<YOUR API KEY>
33
+ ```
34
+
35
+ ### Projects
36
+
37
+ Once the API key has been set, you can list all of the Projects under your Surge account or retrieve a specific Project by its ID.
38
+
39
+ ```python
40
+ # List your Projects
41
+ projects = surge.Project.list()
42
+
43
+ # Print the name of the first Project
44
+ print(projects[0].name)
45
+
46
+ # Retrieve a specific Project
47
+ project = surge.Project.retrieve("076d207b-c207-41ca-b73a-5822fe2248ab")
48
+
49
+ # print the number of tasks in that Project
50
+ print(project.num_tasks)
51
+ ```
52
+ If you have an existing project, you can use it as a template to get a new batch of data annotated.
53
+ You can add new labeling tasks from a CSV or with a list of dictionaries.
54
+
55
+ ```python
56
+ # Create a project from a template
57
+ template_project_id = "076d207b-c207-41ca-b73a-5822fe2248ab"
58
+ project = surge.Project.create("My Labeling Project (July 2023 Batch)", template_id=template_project_id)
59
+
60
+ # Add data from a CSV file
61
+ project.create_tasks_from_csv('my_data.csv')
62
+
63
+ # Or add data directly
64
+ tasks = project.create_tasks([{
65
+ "company": "Surge",
66
+ "city": "San Francisco",
67
+ "state": "CA"
68
+ }])
69
+
70
+ # Launch the project to send it to the Surge workforce
71
+ project.launch()
72
+ ```
73
+
74
+
75
+ Or you can create a new project from scratch by creating your own template and list of Question:
76
+
77
+ ```python
78
+ from surge.questions import FreeResponseQuestion, MultipleChoiceQuestion, CheckboxQuestion
79
+
80
+ # Create a new Project
81
+ free_response_q = FreeResponseQuestion(
82
+ text="What is this company's website?",
83
+ label="")
84
+
85
+ multiple_choice_q = MultipleChoiceQuestion(
86
+ text="What category does this company belong to?",
87
+ label="Category",
88
+ options=["Tech", "Sports", "Gaming"])
89
+
90
+ checkbox_q = CheckboxQuestion(
91
+ text="Check all the social media accounts this company has",
92
+ label="",
93
+ options=["Facebook", "Twitter", "Pinterest", "Google+"])
94
+
95
+ fields_template_text = '''
96
+ <p>Company: {{company}}</p>
97
+ '''
98
+
99
+ project = surge.Project.create(
100
+ name="Categorize this company",
101
+ instructions="You will be asked to categorize a company.",
102
+ questions=[free_response_q, multiple_choice_q, checkbox_q],
103
+ callback_url="https://customer-callback-url/",
104
+ fields_template=fields_template_text,
105
+ num_workers_per_task=3)
106
+ ```
107
+
108
+ ### Tasks
109
+
110
+ You can create new Tasks for a project, list all of the Tasks in a given project, or retrieve a specific Task given its ID.
111
+
112
+ ```python
113
+ # Create Tasks for the new Project
114
+ tasks_data = [{"id": 1, "company": "Surge AI"}, {"id": 2, "company":"Twitch TV"}]
115
+ tasks = project.create_tasks(tasks_data)
116
+
117
+ # List all Tasks in the Project
118
+ all_tasks = project.list_tasks()
119
+
120
+ # Retrieve a specific Task
121
+ task = surge.Task.retrieve(task_id = "eaa44610-c8f6-4480-b746-28b6c8defd4d")
122
+
123
+ # Print the fields of that Task
124
+ print(task.fields)
125
+ ```
126
+
127
+ You can also create Tasks in bulk by uploading a local CSV file. The header of the CSV file must specify the fields that are used in your Tasks.
128
+
129
+ | id | company |
130
+ | :--- | :----: |
131
+ | 1 | Surge AI |
132
+ | 2 | Twitch TV |
133
+
134
+ ```python
135
+ # Create Tasks in bulk via CSV file
136
+ file_path = "./companies_to_classify.csv"
137
+ tasks = project.create_tasks_from_csv(file_path)
138
+ ```
139
+
140
+
141
+ ## Development
142
+
143
+ The test suite depends on `pytest`, which you can install using pip:
144
+
145
+ ```bash
146
+ pip install pytest
147
+ ```
148
+
149
+ To run tests from the command line:
150
+
151
+ ```bash
152
+ # Run all tests
153
+ pytest
154
+
155
+ # Run tests in a specific file
156
+ pytest tests/test_projects.py
157
+
158
+ # Run a specific test
159
+ pytest tests/test_projects.py::test_init_complete
160
+ ```
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,23 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ VERSION = '1.5.7'
4
+
5
+ with open('requirements.txt') as f:
6
+ requirements = f.read().splitlines()
7
+
8
+ with open('README.md', 'r') as fh:
9
+ long_description = fh.read()
10
+
11
+ setup(name='surge-api',
12
+ version=VERSION,
13
+ description='Surge Python SDK',
14
+ author='Surge',
15
+ author_email='team@surgehq.ai',
16
+ url='https://github.com/surge-ai/surge-python',
17
+ license='MIT',
18
+ long_description=long_description,
19
+ long_description_content_type='text/markdown',
20
+ python_requires='>=3.6',
21
+ packages=find_packages(exclude=['tests', 'tests.*']),
22
+ install_requires=requirements,
23
+ tests_require=['pytest >= 6.0.0'])
@@ -0,0 +1,9 @@
1
+ import os
2
+
3
+ from surge.projects import Project
4
+ from surge.tasks import Task
5
+ from surge.teams import Team
6
+ from surge.reports import Report
7
+
8
+ api_key = os.environ.get("SURGE_API_KEY", None)
9
+ base_url = os.environ.get("SURGE_BASE_URL", "https://app.surgehq.ai/api")
@@ -0,0 +1,99 @@
1
+ import requests
2
+
3
+ import surge
4
+ from surge.errors import SurgeRequestError, SurgeMissingAPIKeyError
5
+
6
+ PROJECTS_ENDPOINT = "projects"
7
+ TASKS_ENDPOINT = "tasks"
8
+ REPORTS_ENDPOINT = "projects"
9
+ QUESTIONS_ENDPOINT = "items"
10
+ TEAMS_ENDPOINT = "teams"
11
+
12
+
13
+ class APIResource(object):
14
+
15
+ def __init__(self, id=None):
16
+ self.id = id
17
+
18
+ def print_attrs(self, forbid_list: list = []):
19
+ return " ".join(
20
+ [f'{k}="{v}"' for k, v in self.__dict__.items() if not k in forbid_list]
21
+ )
22
+
23
+ @classmethod
24
+ def _base_request(cls, method, api_endpoint, params=None, api_key=None):
25
+ api_key_to_use = api_key or surge.api_key
26
+ if api_key_to_use is None:
27
+ raise SurgeMissingAPIKeyError
28
+
29
+ try:
30
+ url = f"{surge.base_url}/{api_endpoint}"
31
+
32
+ # GET request
33
+ if method == "get":
34
+ response = requests.get(url, auth=(api_key_to_use, ""), params=params)
35
+
36
+ # POST request
37
+ elif method == "post":
38
+ response = requests.post(url, auth=(api_key_to_use, ""), json=params)
39
+
40
+ # PUT request
41
+ elif method == "put":
42
+ if params is not None and len(params):
43
+ response = requests.put(url, auth=(api_key_to_use, ""), data=params)
44
+ else:
45
+ response = requests.put(url, auth=(api_key_to_use, ""))
46
+
47
+ elif method == "delete":
48
+ response = requests.delete(url, auth=(api_key_to_use, ""))
49
+
50
+ else:
51
+ raise SurgeRequestError("Invalid HTTP method.")
52
+
53
+ # Raise exception if there is an http error
54
+ response.raise_for_status()
55
+
56
+ # If no errors, return response as json
57
+ return response.json()
58
+
59
+ except requests.exceptions.HTTPError as err:
60
+ message = err.args[0]
61
+ message = f"{message}. {err.response.text}"
62
+ raise SurgeRequestError(message) from None
63
+
64
+ except requests.exceptions.JSONDecodeError as err:
65
+ message = err.args[0]
66
+ raise SurgeRequestError(message) from None
67
+
68
+ except Exception:
69
+ # Generic exception handling
70
+ raise SurgeRequestError
71
+
72
+ @classmethod
73
+ def get(cls, api_endpoint, params=None, api_key=None):
74
+ method = "get"
75
+ return cls._base_request(method,
76
+ api_endpoint,
77
+ params=params,
78
+ api_key=api_key)
79
+
80
+ @classmethod
81
+ def post(cls, api_endpoint, params=None, api_key=None):
82
+ method = "post"
83
+ return cls._base_request(method,
84
+ api_endpoint,
85
+ params=params,
86
+ api_key=api_key)
87
+
88
+ @classmethod
89
+ def put(cls, api_endpoint, params=None, api_key=None):
90
+ method = "put"
91
+ return cls._base_request(method,
92
+ api_endpoint,
93
+ params=params,
94
+ api_key=api_key)
95
+
96
+ @classmethod
97
+ def delete_request(cls, api_endpoint, api_key=None):
98
+ method = "delete"
99
+ return cls._base_request(method, api_endpoint, api_key=api_key)
@@ -0,0 +1,35 @@
1
+ import json
2
+
3
+
4
+ class Carousel(object):
5
+
6
+ def __init__(self, carousel_type=None):
7
+ self.carousel_type = carousel_type
8
+
9
+ def to_dict(self):
10
+ return self.__dict__
11
+
12
+ def to_json(self):
13
+ return json.dumps(self.to_dict())
14
+
15
+
16
+ class BoundedRoundsCarousel(Carousel):
17
+
18
+ def __init__(self, min_rounds_for_carousel=1, max_rounds_for_carousel=1):
19
+ super().__init__(carousel_type="bounded_rounds")
20
+ self.min_rounds_for_carousel = min_rounds_for_carousel
21
+ self.max_rounds_for_carousel = max_rounds_for_carousel
22
+
23
+
24
+ class DataKeyCarousel(Carousel):
25
+
26
+ def __init__(self, carousel_data_key=None):
27
+ super().__init__(carousel_type="data_key")
28
+ self.carousel_data_key = carousel_data_key
29
+
30
+
31
+ class OrdinalColumnsCarousel(Carousel):
32
+
33
+ def __init__(self, max_rounds_for_carousel=1):
34
+ super().__init__(carousel_type="ordinal_columns")
35
+ self.max_rounds_for_carousel = max_rounds_for_carousel
@@ -0,0 +1,52 @@
1
+ class SurgeRequestError(Exception):
2
+ """Catch-all exception for errors that occur when making a request"""
3
+
4
+ def __init__(self, message="Something went wrong with the API request."):
5
+ self.message = message
6
+ super().__init__(self.message)
7
+
8
+
9
+ class SurgeMissingAPIKeyError(Exception):
10
+ """Raise when user has not set their API key"""
11
+
12
+ def __init__(self, message="Surge API key has not been set."):
13
+ self.message = message
14
+ super().__init__(self.message)
15
+
16
+
17
+ class SurgeMissingIDError(Exception):
18
+ """Raise when a Surge object is missing an ID"""
19
+
20
+ def __init__(self, message="Must be initialized with an id."):
21
+ self.message = message
22
+ super().__init__(self.message)
23
+
24
+
25
+ class SurgeMissingAttributeError(Exception):
26
+ """Raise when a Surge object is missing a required attribute"""
27
+
28
+ def __init__(self, message="Object is missing a required attribute."):
29
+ self.message = message
30
+ super().__init__(self.message)
31
+
32
+
33
+ class SurgeProjectQuestionError(Exception):
34
+ """Raise for exceptions that occur when adding Questions to a new Project"""
35
+
36
+ def __init__(
37
+ self,
38
+ message="All questions added to a Project must be of type Question."
39
+ ):
40
+ self.message = message
41
+ super().__init__(self.message)
42
+
43
+
44
+ class SurgeTaskDataError(Exception):
45
+ """Raise for exceptions that occur when creating Task objects"""
46
+
47
+ def __init__(
48
+ self,
49
+ message="Invalid argument: task_data must be a non-empty list of dicts."
50
+ ):
51
+ self.message = message
52
+ super().__init__(self.message)