brynq-sdk-vplan 2.0.1__py3-none-any.whl

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,46 @@
1
+ from brynq_sdk_brynq import BrynQ
2
+ from typing import List, Union, Literal, Optional
3
+ from .get_data import GetData
4
+ from .activity import Activity
5
+ from .item import Item
6
+ from .order import Order
7
+ from .project import Project
8
+ from .resource import Resource
9
+ from .time_tracking import TimeTracking
10
+ from .user import User
11
+ from .leave import Leave
12
+
13
+
14
+ class VPlan(BrynQ):
15
+ def __init__(self, system_type: Optional[Literal['source', 'target']] = None, debug: bool = False):
16
+ """
17
+ A class to fetch data from the vPlan API. See https://developer.vplan.com/documentation/#tag/General/ for more information
18
+ """
19
+ super().__init__()
20
+ self.timeout = 3600
21
+ self.headers = self._get_credentials(system_type)
22
+ self.post_headers = {**self.headers, 'Content-Type': 'application/json'}
23
+ self.base_url = 'https://api.vplan.com/v1/'
24
+ self.get = GetData(self)
25
+ self.activity = Activity(self)
26
+ self.item = Item(self)
27
+ self.order = Order(self)
28
+ self.project = Project(self)
29
+ self.resource = Resource(self)
30
+ self.time_tracking = TimeTracking(self)
31
+ self.user = User(self)
32
+ self.leave = Leave(self)
33
+
34
+ def _get_credentials(self, system_type) -> dict:
35
+ """
36
+ Retrieve API key and env from the system credentials.
37
+ Args: label (Union[str, List]): The label or list of labels to get the credentials.
38
+ Returns: str: The authorization headers
39
+ """
40
+ credentials = self.interfaces.credentials.get(system="vplan", system_type=system_type)
41
+ credentials = credentials.get('data')
42
+ headers = {
43
+ 'X-Api-Key': credentials['X-Api-Key'],
44
+ 'X-Api-Env': credentials['X-Api-Env']
45
+ }
46
+ return headers
@@ -0,0 +1,132 @@
1
+ import requests
2
+ from typing import Union, List
3
+ import warnings
4
+ import json
5
+ from .get_data import GetData
6
+
7
+
8
+ class Activity:
9
+ def __init__(self, vplan):
10
+ """
11
+ Initialize the GetData class.
12
+ Args: vplan: contains the vplan object with the headers and base_url
13
+ """
14
+ self.vplan = vplan
15
+ self.get_data = GetData(vplan)
16
+
17
+ def get_activity_list(self, filter: str = None, show: str = None, hide: str = None):
18
+ """
19
+ Get all the activitys from vPlan: https://developer.vplan.com/documentation/#tag/activity/paths/~1activity/get
20
+ Args: filter (str, optional): On the list endpoints it is possible to filter the result set given. To apply a filter use the filter query parameter with the format: field:operator:value
21
+ Example: ?filter=created_at:gt:2020-09-26,and,(id:not:starts_with:0000,or,id:contains:FFFF)
22
+
23
+ Colon : is the separator between the field, operator(s) and value.
24
+ Comma , can be used to combine filter statements by using ,and, or ,or,.
25
+ Braces (...) can be used to group filter statements.
26
+
27
+ The following operators are supported
28
+
29
+ operator description
30
+ eq equal to the given value
31
+ gt greater than the given value
32
+ gte greater than or equal to the given value
33
+ lt lesser than the given value
34
+ lte lesser than or equal to the given value
35
+ not negation of the operator that follows it
36
+ contains has occurrence of the given value
37
+ starts_with starts with the given value
38
+ end_with ends with the given value
39
+
40
+ warning: Currently the comma , and colon : are not supported within the filter value
41
+
42
+ show (str, optional): On the list endpoints it is possible to select the fields that should be returned. To apply a show use the show query parameter with the format: field1,field2,field3
43
+ hide (str, optional): On the list endpoints it is possible to hide the fields that should not be returned. To apply a hide use the hide query parameter with the format: field1,field2,field3
44
+
45
+ Returns: pd.DataFrame: The fetched data as a pandas DataFrame.
46
+ """
47
+ df = self.get_data.get_data(endpoint='activity', filter=filter, show=show, hide=hide)
48
+ return df
49
+
50
+ def post_activity(self, data: dict) -> requests.Response:
51
+ """
52
+ Create a new activity in vPlan: https://developer.vplan.com/documentation/#tag/activity/paths/~1activity/post
53
+
54
+ This method constructs a request URL based on the endpoint and sends a POST request
55
+ to the vPlan API with the provided data.
56
+
57
+ Args: endpoint (str): The name of the endpoint to create a new activity in.
58
+ data (dict): The data to create the new activity with.
59
+
60
+ Returns: requests.Response: The response from the vPlan API.
61
+ """
62
+ required_fields = ['code', 'description']
63
+ allowed_fields = ['resource_type', 'default_duration', 'external_ref', 'archive']
64
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
65
+
66
+ url = f"{self.vplan.base_url}activity"
67
+
68
+ base_body = {
69
+ "code": data['code'],
70
+ "description": data['description']
71
+ }
72
+ # Add fields that you want to update a dict (adding to body itself is too much text)
73
+ base_body.update({"resource_type": data['resource_type']}) if 'resource_type' in data else base_body
74
+ base_body.update({"default_duration": data['default_duration']}) if 'default_duration' in data else base_body
75
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
76
+ base_body.update({"archive": data['archive']}) if 'archive' in data else base_body
77
+
78
+ response = requests.request('POST', url, headers=self.vplan.post_headers, data=base_body, timeout=self.vplan.timeout)
79
+ return response
80
+
81
+ def update_activity(self, activity_id: str, data: dict) -> requests.Response:
82
+ """
83
+ Update an existing activity in vPlan: https://developer.vplan.com/documentation/#tag/activity/paths/~1activity~1%7Bactivity_id%7D/put
84
+
85
+ This method constructs a request URL based on the endpoint and sends a POST request
86
+ to the vPlan API with the provided data.
87
+
88
+ Args: endpoint (str): The name of the endpoint to create a new activity in.
89
+ data (dict): The data to create the new activity with.
90
+
91
+ Returns: requests.Response: The response from the vPlan API.
92
+ """
93
+ required_fields = ['code', 'description']
94
+ allowed_fields = ['resource_type', 'default_duration', 'external_ref', 'archive']
95
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
96
+
97
+ url = f"{self.vplan.base_url}activity/{activity_id}"
98
+
99
+ base_body = {
100
+ "code": data['code'],
101
+ "description": data['description']
102
+ }
103
+ # Add fields that you want to update a dict (adding to body itself is too much text)
104
+ base_body.update({"resource_type": data['resource_type']}) if 'resource_type' in data else base_body
105
+ base_body.update({"default_duration": data['default_duration']}) if 'default_duration' in data else base_body
106
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
107
+ base_body.update({"archive": data['archive']}) if 'archive' in data else base_body
108
+
109
+ response = requests.request('PUT', url, headers=self.vplan.post_headers, json=base_body, timeout=self.vplan.timeout)
110
+ return response
111
+
112
+ def delete_activity(self, activity_id):
113
+ """
114
+ Delete an existing activity in vPlan: https://developer.vplan.com/documentation/#tag/activity/paths/~1activity~1%7Bactivity_id%7D/delete
115
+ This method constructs a request URL based on the endpoint and sends a DELETE request to the vPlan API.
116
+ """
117
+ url = f"{self.vplan.base_url}activity/{activity_id}"
118
+ response = requests.request('DELETE', url, headers=self.vplan.headers, timeout=self.vplan.timeout)
119
+ return response
120
+
121
+ @staticmethod
122
+ def __check_fields(data: Union[dict, List], required_fields: List, allowed_fields: List):
123
+ if isinstance(data, dict):
124
+ data = data.keys()
125
+
126
+ for field in data:
127
+ if field not in allowed_fields and field not in required_fields:
128
+ warnings.warn('Field {field} is not implemented. Optional fields are: {allowed_fields}'.format(field=field, allowed_fields=tuple(allowed_fields)))
129
+
130
+ for field in required_fields:
131
+ if field not in data:
132
+ raise ValueError('Field {field} is required. Required fields are: {required_fields}'.format(field=field, required_fields=tuple(required_fields)))
@@ -0,0 +1,86 @@
1
+ import pandas as pd
2
+ from typing import List, Any
3
+ import requests
4
+
5
+
6
+ class GetData:
7
+ def __init__(self, vplan):
8
+ """
9
+ Initialize the GetData class.
10
+ Args: vplan: contains the vplan object with the headers and base_url
11
+ """
12
+ self.vplan = vplan
13
+
14
+ def get_data(self, endpoint: str, limit: int = 1000, filter: str = None, with_array: str = None, show: str = None, hide: str = None, archived: bool = False) -> pd.DataFrame:
15
+ """
16
+ Fetch data from a specified table from vPlan
17
+
18
+ This method constructs a request URL based on the table name and optional filter,
19
+ sends a request to the vPlan API, parses the JSON response, and converts it
20
+ to a pandas DataFrame. The DataFrame columns are cleaned to remove unnecessary
21
+ characters and duplicate columns are handled.
22
+
23
+ Args: endpoint (str): The name of the endpoint to fetch data from.
24
+ filter (str, optional): On the list endpoints it is possible to filter the result set given. To apply a filter use the filter query parameter with the format: field:operator:value
25
+ Example: ?filter=created_at:gt:2020-09-26,and,(id:not:starts_with:0000,or,id:contains:FFFF)
26
+
27
+ Colon : is the separator between the field, operator(s) and value.
28
+ Comma , can be used to combine filter statements by using ,and, or ,or,.
29
+ Braces (...) can be used to group filter statements.
30
+
31
+ The following operators are supported
32
+
33
+ operator description
34
+ eq equal to the given value
35
+ gt greater than the given value
36
+ gte greater than or equal to the given value
37
+ lt lesser than the given value
38
+ lte lesser than or equal to the given value
39
+ not negation of the operator that follows it
40
+ contains has occurrence of the given value
41
+ starts_with starts with the given value
42
+ end_with ends with the given value
43
+
44
+ warning: Currently the comma , and colon : are not supported within the filter value
45
+
46
+ show (str, optional): On the list endpoints it is possible to select the fields that should be returned. To apply a show use the show query parameter with the format: field1,field2,field3
47
+ hide (str, optional): On the list endpoints it is possible to hide the fields that should not be returned. To apply a hide use the hide query parameter with the format: field1,field2,field3
48
+
49
+ Returns: pd.DataFrame: The fetched data as a pandas DataFrame.
50
+ """
51
+ # Initial URL with subscription-key
52
+ url = f"{self.vplan.base_url}{endpoint}?"
53
+ if limit:
54
+ url += f'limit={limit}&'
55
+ if filter:
56
+ url += f'filter={filter}&'
57
+ if with_array:
58
+ url += f'with={with_array}&'
59
+ if show:
60
+ url += f'show={show}&'
61
+ if hide:
62
+ url += f'hide={hide}&'
63
+ if archived:
64
+ url += f'archived={archived}&'
65
+
66
+ all_data = []
67
+ offset = 0
68
+ received = 0
69
+ while True:
70
+ final_url = f'{url}offset={offset}'
71
+ response = requests.get(url=final_url, headers=self.vplan.headers)
72
+ response.raise_for_status()
73
+ result = response.json()
74
+ data = result.get('data', result)
75
+ all_data.extend(data)
76
+
77
+ # Determine if we need to continue fetching data
78
+ count = result.get('count', 0)
79
+ received += len(data)
80
+ offset += limit
81
+ if received >= count:
82
+ break
83
+
84
+
85
+ df = pd.DataFrame(all_data)
86
+ return df
@@ -0,0 +1,141 @@
1
+ import requests
2
+ from typing import Union, List
3
+ import warnings
4
+ import json
5
+ from .get_data import GetData
6
+
7
+
8
+ class Item:
9
+ def __init__(self, vplan):
10
+ """
11
+ Initialize the GetData class.
12
+ Args: vplan: contains the vplan object with the headers and base_url
13
+ """
14
+ self.vplan = vplan
15
+ self.get_data = GetData(vplan)
16
+
17
+ def get_item_list(self, filter: str = None, show: str = None, hide: str = None):
18
+ """
19
+ Get all the items from vPlan: https://developer.vplan.com/documentation/#tag/item/paths/~1item/get
20
+ Args: filter (str, optional): On the list endpoints it is possible to filter the result set given. To apply a filter use the filter query parameter with the format: field:operator:value
21
+ Example: ?filter=created_at:gt:2020-09-26,and,(id:not:starts_with:0000,or,id:contains:FFFF)
22
+
23
+ Colon : is the separator between the field, operator(s) and value.
24
+ Comma , can be used to combine filter statements by using ,and, or ,or,.
25
+ Braces (...) can be used to group filter statements.
26
+
27
+ The following operators are supported
28
+
29
+ operator description
30
+ eq equal to the given value
31
+ gt greater than the given value
32
+ gte greater than or equal to the given value
33
+ lt lesser than the given value
34
+ lte lesser than or equal to the given value
35
+ not negation of the operator that follows it
36
+ contains has occurrence of the given value
37
+ starts_with starts with the given value
38
+ end_with ends with the given value
39
+
40
+ warning: Currently the comma , and colon : are not supported within the filter value
41
+
42
+ show (str, optional): On the list endpoints it is possible to select the fields that should be returned. To apply a show use the show query parameter with the format: field1,field2,field3
43
+ hide (str, optional): On the list endpoints it is possible to hide the fields that should not be returned. To apply a hide use the hide query parameter with the format: field1,field2,field3
44
+
45
+ Returns: pd.DataFrame: The fetched data as a pandas DataFrame.
46
+ """
47
+ df = self.get_data.get_data(endpoint='item', filter=filter, show=show, hide=hide)
48
+ return df
49
+
50
+ def post_item(self, data: dict) -> requests.Response:
51
+ """
52
+ Create a new item in vPlan: https://developer.vplan.com/documentation/#tag/item/paths/~1item/post
53
+
54
+ This method constructs a request URL based on the endpoint and sends a POST request
55
+ to the vPlan API with the provided data.
56
+
57
+ Args: endpoint (str): The name of the endpoint to create a new item in.
58
+ data (dict): The data to create the new item with.
59
+
60
+ Returns: requests.Response: The response from the vPlan API.
61
+ """
62
+ required_fields = ['code', 'description']
63
+ allowed_fields = ['stock_management', 'unit', 'type', 'location', 'note', 'external_ref']
64
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
65
+
66
+ url = f"{self.vplan.base_url}item"
67
+
68
+ base_body = {
69
+ "code": data['code'],
70
+ "description": data['description']
71
+ }
72
+ # Add fields that you want to update a dict (adding to body itself is too much text)
73
+ base_body.update({"code": data['code']}) if 'code' in data else base_body
74
+ base_body.update({"description": data['description']}) if 'description' in data else base_body
75
+ base_body.update({"stock_management": data['stock_management']}) if 'stock_management' in data else base_body
76
+ base_body.update({"unit": data['unit']}) if 'unit' in data else base_body
77
+ base_body.update({"type": data['type']}) if 'type' in data else base_body
78
+ base_body.update({"location": data['location']}) if 'location' in data else base_body
79
+ base_body.update({"note": data['note']}) if 'note' in data else base_body
80
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
81
+
82
+ response = requests.request('POST', url, headers=self.vplan.post_headers, data=base_body, timeout=self.vplan.timeout)
83
+ return response
84
+
85
+ def update_item(self, item_id: str, data: dict) -> requests.Response:
86
+ """
87
+ Update an existing item in vPlan: https://developer.vplan.com/documentation/#tag/item/paths/~1item~1%7Bitem_id%7D/put
88
+
89
+ This method constructs a request URL based on the endpoint and sends a POST request
90
+ to the vPlan API with the provided data.
91
+
92
+ Args: endpoint (str): The name of the endpoint to create a new item in.
93
+ data (dict): The data to create the new item with.
94
+
95
+ Returns: requests.Response: The response from the vPlan API.
96
+ """
97
+ required_fields = ['code', 'description']
98
+ allowed_fields = ['stock_management', 'unit', 'type', 'location', 'note', 'external_ref']
99
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
100
+
101
+ url = f"{self.vplan.base_url}item/{item_id}"
102
+
103
+ base_body = {
104
+ "code": data['code'],
105
+ "description": data['description']
106
+ }
107
+ # Add fields that you want to update a dict (adding to body itself is too much text)
108
+ base_body.update({"code": data['code']}) if 'code' in data else base_body
109
+ base_body.update({"description": data['description']}) if 'description' in data else base_body
110
+ base_body.update({"stock_management": data['stock_management']}) if 'stock_management' in data else base_body
111
+ base_body.update({"unit": data['unit']}) if 'unit' in data else base_body
112
+ base_body.update({"type": data['type']}) if 'type' in data else base_body
113
+ base_body.update({"location": data['location']}) if 'location' in data else base_body
114
+ base_body.update({"note": data['note']}) if 'note' in data else base_body
115
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
116
+
117
+ response = requests.request('PUT', url, headers=self.vplan.post_headers, data=json.dumps(base_body), timeout=self.vplan.timeout)
118
+ return response
119
+
120
+ def delete_item(self, item_id):
121
+ """
122
+ Delete an existing item in vPlan: https://developer.vplan.com/documentation/#tag/Item/paths/~1item~1%7Bitem_id%7D/delete
123
+ This method constructs a request URL based on the endpoint and sends a DELETE request to the vPlan API.
124
+ """
125
+ url = f"{self.vplan.base_url}item/{item_id}"
126
+ response = requests.request('DELETE', url, headers=self.vplan.headers, timeout=self.vplan.timeout)
127
+ return response
128
+
129
+
130
+ @staticmethod
131
+ def __check_fields(data: Union[dict, List], required_fields: List, allowed_fields: List):
132
+ if isinstance(data, dict):
133
+ data = data.keys()
134
+
135
+ for field in data:
136
+ if field not in allowed_fields and field not in required_fields:
137
+ warnings.warn('Field {field} is not implemented. Optional fields are: {allowed_fields}'.format(field=field, allowed_fields=tuple(allowed_fields)))
138
+
139
+ for field in required_fields:
140
+ if field not in data:
141
+ raise ValueError('Field {field} is required. Required fields are: {required_fields}'.format(field=field, required_fields=tuple(required_fields)))
@@ -0,0 +1,109 @@
1
+ import requests
2
+ from typing import Union, List, Any
3
+ import warnings
4
+ import json
5
+ from .get_data import GetData
6
+
7
+
8
+ class Leave:
9
+ def __init__(self, vplan):
10
+ """
11
+ Initialize the GetData class.
12
+ Args: vplan: contains the vplan object with the headers and base_url
13
+ """
14
+ self.vplan = vplan
15
+ self.get_data = GetData(vplan)
16
+
17
+ def get_leave(self, resource_id: str) -> requests.Response:
18
+ """
19
+ Get leave data for a specific resource.
20
+
21
+ Args:
22
+ resource_id (str): The id of the resource to get the leave from.
23
+
24
+ Returns:
25
+ requests.Response: The response from the vPlan API containing schedule deviations.
26
+ """
27
+ url = f"{self.vplan.base_url}resource/{resource_id}?with=schedule_deviations"
28
+ response = requests.request('GET', url, headers=self.vplan.headers)
29
+ return response
30
+
31
+ def post_leave(self, resource_id: str, data: dict) -> requests.Response:
32
+ """
33
+ There is no documentation for this method available
34
+
35
+ This method constructs a request URL based on the endpoint and sends a POST request
36
+ to the vPlan API with the provided data.
37
+
38
+ Args: resource_id (str): The resource id of the employee to add the leave to
39
+ data (dict): The data to create the new order with.
40
+
41
+ Returns: requests.Response: The response from the vPlan API.
42
+ """
43
+ required_fields = ['type', 'time', 'description', 'start_date', 'end_date']
44
+ allowed_fields = []
45
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
46
+
47
+ url = f"{self.vplan.base_url}resource/{resource_id}/schedule_deviation"
48
+ base_body = json.dumps({
49
+ "type": data['type'],
50
+ "time": data['time'],
51
+ "description": data['description'],
52
+ "start_date": data['start_date'],
53
+ "end_date": data['end_date']
54
+ })
55
+ response = requests.request('POST', url, headers=self.vplan.post_headers, data=base_body, timeout=self.vplan.timeout)
56
+ return response
57
+
58
+ def update_leave(self, resource_id: str, leave_id: str, data: dict) -> requests.Response:
59
+ """
60
+ There is no documentation for this method available
61
+
62
+ This method constructs a request URL based on the endpoint and sends a POST request
63
+ to the vPlan API with the provided data.
64
+
65
+ Args: resource_id (str): The resource id of the employee to add the leave to
66
+ leave_id (str): The id of the leave to update
67
+ data (dict): The data to create the new order with.
68
+
69
+ Returns: requests.Response: The response from the vPlan API.
70
+ """
71
+ required_fields = ['type', 'time', 'description', 'start_date', 'end_date']
72
+ allowed_fields = []
73
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
74
+
75
+ url = f"{self.vplan.base_url}resource/{resource_id}/schedule_deviation/{leave_id}"
76
+ base_body = json.dumps({
77
+ "type": data['type'],
78
+ "time": data['time'],
79
+ "description": data['description'],
80
+ "start_date": data['start_date'],
81
+ "end_date": data['end_date']
82
+ })
83
+ response = requests.request('PUT', url, headers=self.vplan.post_headers, data=base_body, timeout=self.vplan.timeout)
84
+ return response
85
+
86
+ def delete_leave(self, resource_id: str, leave_id: str):
87
+ """
88
+ There is no documentation for this method available
89
+ This method constructs a request URL based on the endpoint and sends a DELETE request to the vPlan API.
90
+ :param resource_id: The resource id of the employee to delete the the leave to
91
+ :param leave_id: The id of the leave to delete
92
+ """
93
+ url = f"{self.vplan.base_url}resource/{resource_id}/schedule_deviation/{leave_id}"
94
+ response = requests.request('DELETE', url, headers=self.vplan.headers, timeout=self.vplan.timeout)
95
+ return response
96
+
97
+
98
+ @staticmethod
99
+ def __check_fields(data: Union[dict, List], required_fields: List, allowed_fields: List):
100
+ if isinstance(data, dict):
101
+ data = data.keys()
102
+
103
+ for field in data:
104
+ if field not in allowed_fields and field not in required_fields:
105
+ warnings.warn('Field {field} is not implemented. Optional fields are: {allowed_fields}'.format(field=field, allowed_fields=tuple(allowed_fields)))
106
+
107
+ for field in required_fields:
108
+ if field not in data:
109
+ raise ValueError('Field {field} is required. Required fields are: {required_fields}'.format(field=field, required_fields=tuple(required_fields)))
@@ -0,0 +1,166 @@
1
+ import requests
2
+ from typing import Union, List, Any
3
+ import warnings
4
+ import json
5
+ from .get_data import GetData
6
+
7
+
8
+ class Order:
9
+ def __init__(self, vplan):
10
+ """
11
+ Initialize the GetData class.
12
+ Args: vplan: contains the vplan object with the headers and base_url
13
+ """
14
+ self.vplan = vplan
15
+ self.get_data = GetData(vplan)
16
+
17
+ def get_order_list(self, filter: str = None, with_array: str = None, show: str = None, hide: str = None):
18
+ """
19
+ Get all the orders from vPlan: https://developer.vplan.com/documentation/#tag/Order/paths/~1order/get
20
+ Args: filter (str, optional): On the list endpoints it is possible to filter the result set given. To apply a filter use the filter query parameter with the format: field:operator:value
21
+ Example: ?filter=created_at:gt:2020-09-26,and,(id:not:starts_with:0000,or,id:contains:FFFF)
22
+
23
+ Colon : is the separator between the field, operator(s) and value.
24
+ Comma , can be used to combine filter statements by using ,and, or ,or,.
25
+ Braces (...) can be used to group filter statements.
26
+
27
+ The following operators are supported
28
+
29
+ operator description
30
+ eq equal to the given value
31
+ gt greater than the given value
32
+ gte greater than or equal to the given value
33
+ lt lesser than the given value
34
+ lte lesser than or equal to the given value
35
+ not negation of the operator that follows it
36
+ contains has occurrence of the given value
37
+ starts_with starts with the given value
38
+ end_with ends with the given value
39
+
40
+ warning: Currently the comma , and colon : are not supported within the filter value
41
+
42
+ with_array (list, optional): object(s) included in the dataset, Possible options are: address, item, order_rows, collection, project, relation, warehouse, board, contact, user, activity, order, item, project, relation, warehouse, supplies_order_rows
43
+ show (str, optional): On the list endpoints it is possible to select the fields that should be returned. To apply a show use the show query parameter with the format: field1,field2,field3
44
+ hide (str, optional): On the list endpoints it is possible to hide the fields that should not be returned. To apply a hide use the hide query parameter with the format: field1,field2,field3
45
+
46
+ Returns: pd.DataFrame: The fetched data as a pandas DataFrame.
47
+ """
48
+ df = self.get_data.get_data(endpoint='order', filter=filter, with_array=with_array, show=show, hide=hide)
49
+ return df
50
+
51
+ def post_order(self, data: dict) -> requests.Response:
52
+ """
53
+ Create a new order in vPlan: https://developer.vplan.com/documentation/#tag/order/paths/~1order/post
54
+
55
+ This method constructs a request URL based on the endpoint and sends a POST request
56
+ to the vPlan API with the provided data.
57
+
58
+ Args: endpoint (str): The name of the endpoint to create a new order in.
59
+ data (dict): The data to create the new order with.
60
+
61
+ Returns: requests.Response: The response from the vPlan API.
62
+ """
63
+ required_fields = ['type', 'code', 'description']
64
+ allowed_fields = ['quantity', 'external_url', 'sub_type', 'status', 'note', 'contact', 'relation_ref', 'date', 'desired_date', 'promised_date',
65
+ 'delivered_date', 'collection_id', 'item_id', 'project_id', 'relation_id', 'warehouse_id', 'external_ref', 'board_id']
66
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
67
+
68
+ url = f"{self.vplan.base_url}order"
69
+
70
+ base_body = {
71
+ "type": data['type'],
72
+ "code": data['code'],
73
+ "description": data['description']
74
+ }
75
+ # Add fields that you want to update a dict (adding to body itself is too much text)
76
+ base_body.update({"quantity": data['quantity']}) if 'quantity' in data else base_body
77
+ base_body.update({"external_url": data['external_url']}) if 'external_url' in data else base_body
78
+ base_body.update({"sub_type": data['sub_type']}) if 'sub_type' in data else base_body
79
+ base_body.update({"status": data['status']}) if 'status' in data else base_body
80
+ base_body.update({"note": data['note']}) if 'note' in data else base_body
81
+ base_body.update({"contact": data['contact']}) if 'contact' in data else base_body
82
+ base_body.update({"relation_ref": data['relation_ref']}) if 'relation_ref' in data else base_body
83
+ base_body.update({"date": data['date']}) if 'date' in data else base_body
84
+ base_body.update({"desired_date": data['desired_date']}) if 'desired_date' in data else base_body
85
+ base_body.update({"promised_date": data['promised_date']}) if 'promised_date' in data else base_body
86
+ base_body.update({"delivered_date": data['delivered_date']}) if 'delivered_date' in data else base_body
87
+ base_body.update({"collection_id": data['collection_id']}) if 'collection_id' in data else base_body
88
+ base_body.update({"item_id": data['item_id']}) if 'item_id' in data else base_body
89
+ base_body.update({"project_id": data['project_id']}) if 'project_id' in data else base_body
90
+ base_body.update({"relation_id": data['relation_id']}) if 'relation_id' in data else base_body
91
+ base_body.update({"warehouse_id": data['warehouse_id']}) if 'warehouse_id' in data else base_body
92
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
93
+ base_body.update({"board_id": data['board_id']}) if 'board_id' in data else base_body
94
+
95
+ response = requests.request('POST', url, headers=self.vplan.post_headers, data=json.dumps(base_body), timeout=self.vplan.timeout)
96
+ return response
97
+
98
+ def update_order(self, order_id: str, data: dict) -> requests.Response:
99
+ """
100
+ Update an existing order in vPlan: https://developer.vplan.com/documentation/#tag/order/paths/~1order~1%7Border_id%7D/put
101
+
102
+ This method constructs a request URL based on the endpoint and sends a POST request
103
+ to the vPlan API with the provided data.
104
+
105
+ Args: endpoint (str): The name of the endpoint to create a new order in.
106
+ data (dict): The data to create the new order with.
107
+
108
+ Returns: requests.Response: The response from the vPlan API.
109
+ """
110
+ required_fields = []
111
+ allowed_fields = ['description' 'type', 'code', 'quantity', 'external_url', 'sub_type', 'status', 'note', 'contact', 'relation_ref', 'date', 'desired_date',
112
+ 'promised_date', 'delivered_date', 'collection_id', 'item_id', 'project_id', 'relation_id', 'warehouse_id', 'external_ref', 'board_id']
113
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
114
+
115
+ url = f"{self.vplan.base_url}order/{order_id}"
116
+
117
+ base_body = {
118
+ }
119
+ # Add fields that you want to update a dict (adding to body itself is too much text)
120
+ base_body.update({"code": data['code']}) if 'code' in data else base_body
121
+ base_body.update({"description": data['description']}) if 'description' in data else base_body
122
+ base_body.update({"type": data['type']}) if 'type' in data else base_body
123
+ base_body.update({"quantity": data['quantity']}) if 'quantity' in data else base_body
124
+ base_body.update({"external_url": data['external_url']}) if 'external_url' in data else base_body
125
+ base_body.update({"sub_type": data['sub_type']}) if 'sub_type' in data else base_body
126
+ base_body.update({"status": data['status']}) if 'status' in data else base_body
127
+ base_body.update({"note": data['note']}) if 'note' in data else base_body
128
+ base_body.update({"contact": data['contact']}) if 'contact' in data else base_body
129
+ base_body.update({"relation_ref": data['relation_ref']}) if 'relation_ref' in data else base_body
130
+ base_body.update({"date": data['date']}) if 'date' in data else base_body
131
+ base_body.update({"desired_date": data['desired_date']}) if 'desired_date' in data else base_body
132
+ base_body.update({"promised_date": data['promised_date']}) if 'promised_date' in data else base_body
133
+ base_body.update({"delivered_date": data['delivered_date']}) if 'delivered_date' in data else base_body
134
+ base_body.update({"collection_id": data['collection_id']}) if 'collection_id' in data else base_body
135
+ base_body.update({"item_id": data['item_id']}) if 'item_id' in data else base_body
136
+ base_body.update({"project_id": data['project_id']}) if 'project_id' in data else base_body
137
+ base_body.update({"relation_id": data['relation_id']}) if 'relation_id' in data else base_body
138
+ base_body.update({"warehouse_id": data['warehouse_id']}) if 'warehouse_id' in data else base_body
139
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
140
+ base_body.update({"board_id": data['board_id']}) if 'board_id' in data else base_body
141
+
142
+ response = requests.request('PUT', url, headers=self.vplan.post_headers, data=json.dumps(base_body), timeout=self.vplan.timeout)
143
+ return response
144
+
145
+ def delete_order(self, order_id):
146
+ """
147
+ Delete an existing order in vPlan: https://developer.vplan.com/documentation/#tag/order/paths/~1order~1%7Border_id%7D/delete
148
+ This method constructs a request URL based on the endpoint and sends a DELETE request to the vPlan API.
149
+ """
150
+ url = f"{self.vplan.base_url}order/{order_id}"
151
+ response = requests.request('DELETE', url, headers=self.vplan.headers, timeout=self.vplan.timeout)
152
+ return response
153
+
154
+
155
+ @staticmethod
156
+ def __check_fields(data: Union[dict, List], required_fields: List, allowed_fields: List):
157
+ if isinstance(data, dict):
158
+ data = data.keys()
159
+
160
+ for field in data:
161
+ if field not in allowed_fields and field not in required_fields:
162
+ warnings.warn('Field {field} is not implemented. Optional fields are: {allowed_fields}'.format(field=field, allowed_fields=tuple(allowed_fields)))
163
+
164
+ for field in required_fields:
165
+ if field not in data:
166
+ raise ValueError('Field {field} is required. Required fields are: {required_fields}'.format(field=field, required_fields=tuple(required_fields)))
@@ -0,0 +1,129 @@
1
+ import requests
2
+ from typing import Union, List
3
+ import warnings
4
+ import json
5
+ from .get_data import GetData
6
+
7
+
8
+ class Project:
9
+ def __init__(self, vplan):
10
+ """
11
+ Initialize the GetData class.
12
+ Args: vplan: contains the vplan object with the headers and base_url
13
+ """
14
+ self.vplan = vplan
15
+ self.get_data = GetData(vplan)
16
+
17
+ def get_project_list(self, filter: str = None, show: str = None, hide: str = None):
18
+ """
19
+ Get all the projects from vPlan: https://developer.vplan.com/documentation/#tag/Project/paths/~1project/get
20
+ Args: filter (str, optional): On the list endpoints it is possible to filter the result set given. To apply a filter use the filter query parameter with the format: field:operator:value
21
+ Example: ?filter=created_at:gt:2020-09-26,and,(id:not:starts_with:0000,or,id:contains:FFFF)
22
+
23
+ Colon : is the separator between the field, operator(s) and value.
24
+ Comma , can be used to combine filter statements by using ,and, or ,or,.
25
+ Braces (...) can be used to group filter statements.
26
+
27
+ The following operators are supported
28
+
29
+ operator description
30
+ eq equal to the given value
31
+ gt greater than the given value
32
+ gte greater than or equal to the given value
33
+ lt lesser than the given value
34
+ lte lesser than or equal to the given value
35
+ not negation of the operator that follows it
36
+ contains has occurrence of the given value
37
+ starts_with starts with the given value
38
+ end_with ends with the given value
39
+
40
+ warning: Currently the comma , and colon : are not supported within the filter value
41
+
42
+ show (str, optional): On the list endpoints it is possible to select the fields that should be returned. To apply a show use the show query parameter with the format: field1,field2,field3
43
+ hide (str, optional): On the list endpoints it is possible to hide the fields that should not be returned. To apply a hide use the hide query parameter with the format: field1,field2,field3
44
+
45
+ Returns: pd.DataFrame: The fetched data as a pandas DataFrame.
46
+ """
47
+ df = self.get_data.get_data(endpoint='project', filter=filter, show=show, hide=hide)
48
+ return df
49
+
50
+ def post_project(self, data: dict) -> requests.Response:
51
+ """
52
+ Create a new project in vPlan: https://developer.vplan.com/documentation/#tag/Project/paths/~1project/post
53
+
54
+ This method constructs a request URL based on the endpoint and sends a POST request
55
+ to the vPlan API with the provided data.
56
+
57
+ Args: endpoint (str): The name of the endpoint to create a new project in.
58
+ data (dict): The data to create the new project with.
59
+
60
+ Returns: requests.Response: The response from the vPlan API.
61
+ """
62
+ required_fields = ['name']
63
+ allowed_fields = ['code', 'description', 'external_ref']
64
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
65
+
66
+ url = f"{self.vplan.base_url}project"
67
+
68
+ base_body = {
69
+ "name": data['name']
70
+ }
71
+
72
+ # Add fields that you want to update a dict (adding to body itself is too much text)
73
+ base_body.update({"code": data['code']}) if 'code' in data else base_body
74
+ base_body.update({"description": data['description']}) if 'description' in data else base_body
75
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
76
+
77
+ response = requests.request('POST', url, headers=self.vplan.post_headers, data=json.dumps(base_body), timeout=self.vplan.timeout)
78
+ return response
79
+
80
+ def update_project(self, project_id: str, data: dict) -> requests.Response:
81
+ """
82
+ Update an existing project in vPlan: https://developer.vplan.com/documentation/#tag/Project/paths/~1project~1%7Bproject_id%7D/put
83
+
84
+ This method constructs a request URL based on the endpoint and sends a POST request
85
+ to the vPlan API with the provided data.
86
+
87
+ Args: endpoint (str): The name of the endpoint to create a new project in.
88
+ data (dict): The data to create the new project with.
89
+
90
+ Returns: requests.Response: The response from the vPlan API.
91
+ """
92
+ required_fields = ['name']
93
+ allowed_fields = ['code', 'description', 'external_ref']
94
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
95
+
96
+ url = f"{self.vplan.base_url}project/{project_id}"
97
+
98
+ base_body = {
99
+ "name": data['name']
100
+ }
101
+ # Add fields that you want to update a dict (adding to body itself is too much text)
102
+ base_body.update({"code": data['code']}) if 'code' in data else base_body
103
+ base_body.update({"description": data['description']}) if 'description' in data else base_body
104
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
105
+
106
+ response = requests.request('PUT', url, headers=self.vplan.post_headers, data=json.dumps(base_body), timeout=self.vplan.timeout)
107
+ return response
108
+
109
+ def delete_project(self, project_id):
110
+ """
111
+ Delete an existing project in vPlan: https://developer.vplan.com/documentation/#tag/Project/paths/~1project~1%7Bproject_id%7D/delete
112
+ This method constructs a request URL based on the endpoint and sends a DELETE request to the vPlan API.
113
+ """
114
+ url = f"{self.vplan.base_url}project/{project_id}"
115
+ response = requests.request('DELETE', url, headers=self.vplan.headers, timeout=self.vplan.timeout)
116
+ return response
117
+
118
+ @staticmethod
119
+ def __check_fields(data: Union[dict, List], required_fields: List, allowed_fields: List):
120
+ if isinstance(data, dict):
121
+ data = data.keys()
122
+
123
+ for field in data:
124
+ if field not in allowed_fields and field not in required_fields:
125
+ warnings.warn('Field {field} is not implemented. Optional fields are: {allowed_fields}'.format(field=field, allowed_fields=tuple(allowed_fields)))
126
+
127
+ for field in required_fields:
128
+ if field not in data:
129
+ raise ValueError('Field {field} is required. Required fields are: {required_fields}'.format(field=field, required_fields=tuple(required_fields)))
@@ -0,0 +1,142 @@
1
+ import requests
2
+ from typing import Union, List
3
+ import warnings
4
+ import json
5
+ from .get_data import GetData
6
+
7
+
8
+ class Resource:
9
+ def __init__(self, vplan):
10
+ """
11
+ Initialize the GetData class.
12
+ Args: vplan: contains the vplan object with the headers and base_url
13
+ """
14
+ self.vplan = vplan
15
+ self.get_data = GetData(vplan)
16
+
17
+ def get_resource_list(self, filter: str = None, show: str = None, hide: str = None, archived: bool = False):
18
+ """
19
+ Get all the resources from vPlan: https://developer.vplan.com/documentation/#tag/Resource/paths/~1resource/get.
20
+ Args: filter (str, optional): On the list endpoints it is possible to filter the result set given. To apply a filter use the filter query parameter with the format: field:operator:value
21
+ Example: ?filter=created_at:gt:2020-09-26,and,(id:not:starts_with:0000,or,id:contains:FFFF)
22
+
23
+ Colon : is the separator between the field, operator(s) and value.
24
+ Comma , can be used to combine filter statements by using ,and, or ,or,.
25
+ Braces (...) can be used to group filter statements.
26
+
27
+ The following operators are supported
28
+
29
+ operator description
30
+ eq equal to the given value
31
+ gt greater than the given value
32
+ gte greater than or equal to the given value
33
+ lt lesser than the given value
34
+ lte lesser than or equal to the given value
35
+ not negation of the operator that follows it
36
+ contains has occurrence of the given value
37
+ starts_with starts with the given value
38
+ end_with ends with the given value
39
+
40
+ warning: Currently the comma , and colon : are not supported within the filter value
41
+
42
+ show (str, optional): On the list endpoints it is possible to select the fields that should be returned. To apply a show use the show query parameter with the format: field1,field2,field3
43
+ hide (str, optional): On the list endpoints it is possible to hide the fields that should not be returned. To apply a hide use the hide query parameter with the format: field1,field2,field3
44
+
45
+ Returns: pd.DataFrame: The fetched data as a pandas DataFrame.
46
+ """
47
+ df = self.get_data.get_data(endpoint='resource', filter=filter, show=show, hide=hide, archived=archived)
48
+ return df
49
+
50
+ def post_resource(self, data: dict) -> requests.Response:
51
+ """
52
+ Create a new resource in vPlan: https://developer.vplan.com/documentation/#tag/Resource/paths/~1resource/post
53
+
54
+ This method constructs a request URL based on the endpoint and sends a POST request
55
+ to the vPlan API with the provided data.
56
+
57
+ Args: endpoint (str): The name of the endpoint to create a new resource in.
58
+ data (dict): The data to create the new resource with.
59
+
60
+ Returns: requests.Response: The response from the vPlan API.
61
+ """
62
+ required_fields = ['name']
63
+ allowed_fields = ['type', 'start_date', 'workdays', 'description', 'end_date', 'integration_schedule', 'avatar', 'color_hex', 'external_ref', 'archive']
64
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
65
+
66
+ url = f"{self.vplan.base_url}resource"
67
+
68
+ base_body = {
69
+ "name": data['name']
70
+ }
71
+ # Add fields that you want to update a dict (adding to body itself is too much text)
72
+ base_body.update({"type": data['type']}) if 'type' in data else base_body
73
+ base_body.update({"start_date": data['start_date']}) if 'start_date' in data else base_body
74
+ base_body.update({"workdays": eval(data['workdays'])}) if 'workdays' in data else base_body
75
+ base_body.update({"description": data['description']}) if 'description' in data else base_body
76
+ base_body.update({"end_date": data['end_date']}) if 'end_date' in data else base_body
77
+ base_body.update({"integration_schedule": data['integration_schedule']}) if 'integration_schedule' in data else base_body
78
+ base_body.update({"avatar": data['avatar']}) if 'avatar' in data else base_body
79
+ base_body.update({"color_hex": data['color_hex']}) if 'color_hex' in data else base_body
80
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
81
+ base_body.update({"archive": data['archive']}) if 'archive' in data else base_body
82
+
83
+ response = requests.request('POST', url, headers=self.vplan.post_headers, data=json.dumps(base_body), timeout=self.vplan.timeout)
84
+ return response
85
+
86
+ def update_resource(self, resource_id: str, data: dict) -> requests.Response:
87
+ """
88
+ Update an existing resource in vPlan: https://developer.vplan.com/documentation/#tag/Resource/paths/~1resource/put
89
+
90
+ This method constructs a request URL based on the endpoint and sends a POST request
91
+ to the vPlan API with the provided data.
92
+
93
+ Args: endpoint (str): The name of the endpoint to create a new resource in.
94
+ data (dict): The data to create the new resource with.
95
+
96
+ Returns: requests.Response: The response from the vPlan API.
97
+ """
98
+ required_fields = []
99
+ allowed_fields = ['name', 'type', 'start_date', 'workdays', 'description', 'end_date', 'integration_schedule', 'avatar', 'color_hex', 'external_ref', 'archive']
100
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
101
+
102
+ url = f"{self.vplan.base_url}resource/{resource_id}"
103
+
104
+ base_body = {}
105
+
106
+ # Add fields that you want to update a dict (adding to body itself is too much text)
107
+ base_body.update({"name": data['name']}) if 'name' in data else base_body
108
+ base_body.update({"type": data['type']}) if 'type' in data else base_body
109
+ base_body.update({"start_date": data['start_date']}) if 'start_date' in data else base_body
110
+ base_body.update({"workdays": eval(data['workdays'])}) if 'workdays' in data else base_body
111
+ base_body.update({"description": data['description']}) if 'description' in data else base_body
112
+ base_body.update({"end_date": data['end_date']}) if 'end_date' in data else base_body
113
+ base_body.update({"integration_schedule": data['integration_schedule']}) if 'integration_schedule' in data else base_body
114
+ base_body.update({"avatar": data['avatar']}) if 'avatar' in data else base_body
115
+ base_body.update({"color_hex": data['color_hex']}) if 'color_hex' in data else base_body
116
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
117
+ base_body.update({"archive": data['archive']}) if 'archive' in data else base_body
118
+
119
+ response = requests.request('PUT', url, headers=self.vplan.post_headers, data=json.dumps(base_body), timeout=self.vplan.timeout)
120
+ return response
121
+
122
+ def delete_resource(self, resource_id):
123
+ """
124
+ Delete an existing resource in vPlan: https://developer.vplan.com/documentation/#tag/Resource/paths/~1resource~1%7Bresource_id%7D/delete
125
+ This method constructs a request URL based on the endpoint and sends a DELETE request to the vPlan API.
126
+ """
127
+ url = f"{self.vplan.base_url}resource/{resource_id}"
128
+ response = requests.request('DELETE', url, headers=self.vplan.headers, timeout=self.vplan.timeout)
129
+ return response
130
+
131
+ @staticmethod
132
+ def __check_fields(data: Union[dict, List], required_fields: List, allowed_fields: List):
133
+ if isinstance(data, dict):
134
+ data = data.keys()
135
+
136
+ for field in data:
137
+ if field not in allowed_fields and field not in required_fields:
138
+ warnings.warn('Field {field} is not implemented. Optional fields are: {allowed_fields}'.format(field=field, allowed_fields=tuple(allowed_fields)))
139
+
140
+ for field in required_fields:
141
+ if field not in data:
142
+ raise ValueError('Field {field} is required. Required fields are: {required_fields}'.format(field=field, required_fields=tuple(required_fields)))
@@ -0,0 +1,144 @@
1
+ import requests
2
+ from typing import Union, List
3
+ import warnings
4
+ import json
5
+ from .get_data import GetData
6
+
7
+
8
+ class TimeTracking:
9
+ def __init__(self, vplan):
10
+ """
11
+ Initialize the GetData class.
12
+ Args: vplan: contains the vplan object with the headers and base_url
13
+ """
14
+ self.vplan = vplan
15
+ self.get_data = GetData(vplan)
16
+
17
+ def get_time_tracking_list(self, filter: str = None, show: str = None, hide: str = None):
18
+ """
19
+ Get all the time records from vPlan: https://developer.vplan.com/documentation/#tag/Time-Tracking/paths/~1time_tracking/get
20
+ Args: filter (str, optional): On the list endpoints it is possible to filter the result set given. To apply a filter use the filter query parameter with the format: field:operator:value
21
+ Example: ?filter=created_at:gt:2020-09-26,and,(id:not:starts_with:0000,or,id:contains:FFFF)
22
+
23
+ Colon : is the separator between the field, operator(s) and value.
24
+ Comma , can be used to combine filter statements by using ,and, or ,or,.
25
+ Braces (...) can be used to group filter statements.
26
+
27
+ The following operators are supported
28
+
29
+ operator description
30
+ eq equal to the given value
31
+ gt greater than the given value
32
+ gte greater than or equal to the given value
33
+ lt lesser than the given value
34
+ lte lesser than or equal to the given value
35
+ not negation of the operator that follows it
36
+ contains has occurrence of the given value
37
+ starts_with starts with the given value
38
+ end_with ends with the given value
39
+
40
+ warning: Currently the comma , and colon : are not supported within the filter value
41
+
42
+ show (str, optional): On the list endpoints it is possible to select the fields that should be returned. To apply a show use the show query parameter with the format: field1,field2,field3
43
+ hide (str, optional): On the list endpoints it is possible to hide the fields that should not be returned. To apply a hide use the hide query parameter with the format: field1,field2,field3
44
+
45
+ Returns: pd.DataFrame: The fetched data as a pandas DataFrame.
46
+ """
47
+ df = self.get_data.get_data(endpoint='time_tracking', filter=filter, show=show, hide=hide)
48
+ return df
49
+
50
+ def post_time_tracking(self, data: dict) -> requests.Response:
51
+ """
52
+ Create a new time record in vPlan: https://developer.vplan.com/documentation/#tag/Time-Tracking/paths/~1time_tracking/post
53
+
54
+ This method constructs a request URL based on the endpoint and sends a POST request
55
+ to the vPlan API with the provided data.
56
+
57
+ Args: endpoint (str): The name of the endpoint to create a new time record in.
58
+ data (dict): The data to create the new time record with.
59
+
60
+ Returns: requests.Response: The response from the vPlan API.
61
+ """
62
+ required_fields = ['activity_id', 'start']
63
+ allowed_fields = ['card_id', 'end', 'duration', 'status', 'user_id', 'note', 'locked', 'synchronized_at', 'external_ref', 'external_note', 'external_failed']
64
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
65
+
66
+ url = f"{self.vplan.base_url}time_tracking"
67
+
68
+ base_body = {
69
+ "activity_id": data['activity_id'],
70
+ "start": data['start']
71
+ }
72
+ # Add fields that you want to update a dict (adding to body itself is too much text)
73
+ base_body.update({"card_id": data['card_id']}) if 'card_id' in data else base_body
74
+ base_body.update({"end": data['end']}) if 'end' in data else base_body
75
+ base_body.update({"duration": data['duration']}) if 'duration' in data else base_body
76
+ base_body.update({"status": data['status']}) if 'status' in data else base_body
77
+ base_body.update({"user_id": data['user_id']}) if 'user_id' in data else base_body
78
+ base_body.update({"note": data['note']}) if 'note' in data else base_body
79
+ base_body.update({"locked": data['locked']}) if 'locked' in data else base_body
80
+ base_body.update({"synchronized_at": data['synchronized_at']}) if 'synchronized_at' in data else base_body
81
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
82
+ base_body.update({"external_note": data['external_note']}) if 'external_note' in data else base_body
83
+ base_body.update({"external_failed": data['external_failed']}) if 'external_failed' in data else base_body
84
+
85
+ response = requests.request('POST', url, headers=self.vplan.post_headers, data=base_body, timeout=self.vplan.timeout)
86
+ return response
87
+
88
+ def update_time_tracking(self, time_tracking_id: str, data: dict) -> requests.Response:
89
+ """
90
+ Update an existing time record in vPlan: https://developer.vplan.com/documentation/#tag/Time-Tracking/paths/~1time_tracking~1%7Btime_tracking_id%7D/put
91
+
92
+ This method constructs a request URL based on the endpoint and sends a POST request
93
+ to the vPlan API with the provided data.
94
+
95
+ Args: endpoint (str): The name of the endpoint to create a new time record in.
96
+ data (dict): The data to create the new time record with.
97
+
98
+ Returns: requests.Response: The response from the vPlan API.
99
+ """
100
+ required_fields = []
101
+ allowed_fields = ['activity_id', 'start', 'card_id', 'end', 'duration', 'status', 'user_id', 'note', 'locked', 'synchronized_at', 'external_ref', 'external_note', 'external_failed']
102
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
103
+
104
+ url = f"{self.vplan.base_url}time_tracking/{time_tracking_id}"
105
+
106
+ base_body = {
107
+ }
108
+ # Add fields that you want to update a dict (adding to body itself is too much text)
109
+ base_body.update({"activity_id": data['activity_id']}) if 'activity_id' in data else base_body
110
+ base_body.update({"start": data['start']}) if 'start' in data else base_body
111
+ base_body.update({"code": data['code']}) if 'code' in data else base_body
112
+ base_body.update({"description": data['description']}) if 'description' in data else base_body
113
+ base_body.update({"stock_management": data['stock_management']}) if 'stock_management' in data else base_body
114
+ base_body.update({"unit": data['unit']}) if 'unit' in data else base_body
115
+ base_body.update({"type": data['type']}) if 'type' in data else base_body
116
+ base_body.update({"location": data['location']}) if 'location' in data else base_body
117
+ base_body.update({"note": data['note']}) if 'note' in data else base_body
118
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
119
+
120
+ response = requests.request('PUT', url, headers=self.vplan.post_headers, json=base_body, timeout=self.vplan.timeout)
121
+ return response
122
+
123
+ def delete_time_tracking(self, time_tracking_id):
124
+ """
125
+ Delete an existing time record in vPlan: https://developer.vplan.com/documentation/#tag/Time-Tracking/paths/~1time_tracking~1%7Btime_tracking_id%7D/delete
126
+ This method constructs a request URL based on the endpoint and sends a DELETE request to the vPlan API.
127
+ """
128
+ url = f"{self.vplan.base_url}time_tracking/{time_tracking_id}"
129
+ response = requests.request('DELETE', url, headers=self.vplan.headers, timeout=self.vplan.timeout)
130
+ return response
131
+
132
+
133
+ @staticmethod
134
+ def __check_fields(data: Union[dict, List], required_fields: List, allowed_fields: List):
135
+ if isinstance(data, dict):
136
+ data = data.keys()
137
+
138
+ for field in data:
139
+ if field not in allowed_fields and field not in required_fields:
140
+ warnings.warn('Field {field} is not implemented. Optional fields are: {allowed_fields}'.format(field=field, allowed_fields=tuple(allowed_fields)))
141
+
142
+ for field in required_fields:
143
+ if field not in data:
144
+ raise ValueError('Field {field} is required. Required fields are: {required_fields}'.format(field=field, required_fields=tuple(required_fields)))
@@ -0,0 +1,141 @@
1
+ import requests
2
+ from typing import Union, List
3
+ import warnings
4
+ import json
5
+ from .get_data import GetData
6
+
7
+
8
+ class User:
9
+ def __init__(self, vplan):
10
+ """
11
+ Initialize the GetData class.
12
+ Args: vplan: contains the vplan object with the headers and base_url
13
+ """
14
+ self.vplan = vplan
15
+ self.get_data = GetData(vplan)
16
+
17
+ def get_user_list(self, filter: str = None, show: str = None, hide: str = None):
18
+ """
19
+ Get all the users from vPlan: https://developer.vplan.com/documentation/#tag/user/paths/~1user/get
20
+ Args: filter (str, optional): On the list endpoints it is possible to filter the result set given. To apply a filter use the filter query parameter with the format: field:operator:value
21
+ Example: ?filter=created_at:gt:2020-09-26,and,(id:not:starts_with:0000,or,id:contains:FFFF)
22
+
23
+ Colon : is the separator between the field, operator(s) and value.
24
+ Comma , can be used to combine filter statements by using ,and, or ,or,.
25
+ Braces (...) can be used to group filter statements.
26
+
27
+ The following operators are supported
28
+
29
+ operator description
30
+ eq equal to the given value
31
+ gt greater than the given value
32
+ gte greater than or equal to the given value
33
+ lt lesser than the given value
34
+ lte lesser than or equal to the given value
35
+ not negation of the operator that follows it
36
+ contains has occurrence of the given value
37
+ starts_with starts with the given value
38
+ end_with ends with the given value
39
+
40
+ warning: Currently the comma , and colon : are not supported within the filter value
41
+
42
+ show (str, optional): On the list endpoints it is possible to select the fields that should be returned. To apply a show use the show query parameter with the format: field1,field2,field3
43
+ hide (str, optional): On the list endpoints it is possible to hide the fields that should not be returned. To apply a hide use the hide query parameter with the format: field1,field2,field3
44
+
45
+ Returns: pd.DataFrame: The fetched data as a pandas DataFrame.
46
+ """
47
+ df = self.get_data.get_data(endpoint='user', filter=filter, show=show, hide=hide)
48
+ return df
49
+
50
+ def post_user(self, data: dict) -> requests.Response:
51
+ """
52
+ Create a new user in vPlan: https://developer.vplan.com/documentation/#tag/user/paths/~1user/post
53
+
54
+ This method constructs a request URL based on the endpoint and sends a POST request
55
+ to the vPlan API with the provided data.
56
+
57
+ Args: endpoint (str): The name of the endpoint to create a new user in.
58
+ data (dict): The data to create the new user with.
59
+
60
+ Returns: requests.Response: The response from the vPlan API.
61
+ """
62
+ required_fields = ['code', 'description']
63
+ allowed_fields = ['stock_management', 'unit', 'type', 'location', 'note', 'external_ref']
64
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
65
+
66
+ url = f"{self.vplan.base_url}user"
67
+
68
+ base_body = {
69
+ "code": data['code'],
70
+ "description": data['description']
71
+ }
72
+ # Add fields that you want to update a dict (adding to body itself is too much text)
73
+ base_body.update({"code": data['code']}) if 'code' in data else base_body
74
+ base_body.update({"description": data['description']}) if 'description' in data else base_body
75
+ base_body.update({"stock_management": data['stock_management']}) if 'stock_management' in data else base_body
76
+ base_body.update({"unit": data['unit']}) if 'unit' in data else base_body
77
+ base_body.update({"type": data['type']}) if 'type' in data else base_body
78
+ base_body.update({"location": data['location']}) if 'location' in data else base_body
79
+ base_body.update({"note": data['note']}) if 'note' in data else base_body
80
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
81
+
82
+ response = requests.request('POST', url, headers=self.vplan.post_headers, data=base_body, timeout=self.vplan.timeout)
83
+ return response
84
+
85
+ def update_user(self, user_id: str, data: dict) -> requests.Response:
86
+ """
87
+ Update an existing user in vPlan: https://developer.vplan.com/documentation/#tag/user/paths/~1user~1%7Buser_id%7D/put
88
+
89
+ This method constructs a request URL based on the endpoint and sends a POST request
90
+ to the vPlan API with the provided data.
91
+
92
+ Args: endpoint (str): The name of the endpoint to create a new user in.
93
+ data (dict): The data to create the new user with.
94
+
95
+ Returns: requests.Response: The response from the vPlan API.
96
+ """
97
+ required_fields = ['code', 'description']
98
+ allowed_fields = ['stock_management', 'unit', 'type', 'location', 'note', 'external_ref']
99
+ self.__check_fields(data=data, required_fields=required_fields, allowed_fields=allowed_fields)
100
+
101
+ url = f"{self.vplan.base_url}user/{user_id}"
102
+
103
+ base_body = {
104
+ "code": data['code'],
105
+ "description": data['description']
106
+ }
107
+ # Add fields that you want to update a dict (adding to body itself is too much text)
108
+ base_body.update({"code": data['code']}) if 'code' in data else base_body
109
+ base_body.update({"description": data['description']}) if 'description' in data else base_body
110
+ base_body.update({"stock_management": data['stock_management']}) if 'stock_management' in data else base_body
111
+ base_body.update({"unit": data['unit']}) if 'unit' in data else base_body
112
+ base_body.update({"type": data['type']}) if 'type' in data else base_body
113
+ base_body.update({"location": data['location']}) if 'location' in data else base_body
114
+ base_body.update({"note": data['note']}) if 'note' in data else base_body
115
+ base_body.update({"external_ref": data['external_ref']}) if 'external_ref' in data else base_body
116
+
117
+ response = requests.request('PUT', url, headers=self.vplan.post_headers, json=base_body, timeout=self.vplan.timeout)
118
+ return response
119
+
120
+ def delete_user(self, user_id):
121
+ """
122
+ Delete an existing user in vPlan: https://developer.vplan.com/documentation/#tag/user/paths/~1user~1%7Buser_id%7D/delete
123
+ This method constructs a request URL based on the endpoint and sends a DELETE request to the vPlan API.
124
+ """
125
+ url = f"{self.vplan.base_url}user/{user_id}"
126
+ response = requests.request('DELETE', url, headers=self.vplan.headers, timeout=self.vplan.timeout)
127
+ return response
128
+
129
+
130
+ @staticmethod
131
+ def __check_fields(data: Union[dict, List], required_fields: List, allowed_fields: List):
132
+ if isinstance(data, dict):
133
+ data = data.keys()
134
+
135
+ for field in data:
136
+ if field not in allowed_fields and field not in required_fields:
137
+ warnings.warn('Field {field} is not implemented. Optional fields are: {allowed_fields}'.format(field=field, allowed_fields=tuple(allowed_fields)))
138
+
139
+ for field in required_fields:
140
+ if field not in data:
141
+ raise ValueError('Field {field} is required. Required fields are: {required_fields}'.format(field=field, required_fields=tuple(required_fields)))
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: brynq_sdk_vplan
3
+ Version: 2.0.1
4
+ Summary: vPlan wrapper from BrynQ
5
+ Author: BrynQ
6
+ Author-email: support@brynq.com
7
+ License: BrynQ License
8
+ Requires-Dist: brynq-sdk-brynq<5,>=4
9
+ Requires-Dist: pandas<3,>=2
10
+ Dynamic: author
11
+ Dynamic: author-email
12
+ Dynamic: description
13
+ Dynamic: license
14
+ Dynamic: requires-dist
15
+ Dynamic: summary
16
+
17
+ vPlan wrapper from BrynQ
@@ -0,0 +1,14 @@
1
+ brynq_sdk_vplan/__init__.py,sha256=MrarXte0hUoGsw4qYVKiOMOvfv0ysgs9a3b1Oy_yfL4,1766
2
+ brynq_sdk_vplan/activity.py,sha256=Obnobz9-3TLbSF4YCrqo4yr8lUqrX_3FJEKoU657gzc,7399
3
+ brynq_sdk_vplan/get_data.py,sha256=jCc4l4l9ORcCebB_Trxm5Scju-HaB8Vy3BEcbf6NViw,4157
4
+ brynq_sdk_vplan/item.py,sha256=fY-xveKAwtSFiVzybDsAaMfVbay7YhZu2L1GyZ4qUq4,7954
5
+ brynq_sdk_vplan/leave.py,sha256=p72zg5XDiq5RYJflXph91QM0lyPcRE5Q5_G0g9G0l-Y,4729
6
+ brynq_sdk_vplan/order.py,sha256=3hM9I56Eq-cHo6N1_ZaPitcauSv0eUnJDgW-s4HVlN0,11010
7
+ brynq_sdk_vplan/project.py,sha256=oWgLBELcSAy8wSW6CuBlunWEkB3pI1pCdSIe9CR_xJw,6951
8
+ brynq_sdk_vplan/resource.py,sha256=siBSdRsR907ZHt9cB4NFz06yybSORLcixWn8_y8Qs98,8641
9
+ brynq_sdk_vplan/time_tracking.py,sha256=UXFl4mg_3M6a0Q3P5N77AlCLWyqmNib5yw5OfD_qYx4,8762
10
+ brynq_sdk_vplan/user.py,sha256=Gdc6XFxhCSEwilDiNdeQTo5gd_CsVKgM4H6yk3IDFxI,7942
11
+ brynq_sdk_vplan-2.0.1.dist-info/METADATA,sha256=DuOwWXCTiYd4zePph4aW4pvDMNuLCyAog7urOECzx_o,369
12
+ brynq_sdk_vplan-2.0.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
13
+ brynq_sdk_vplan-2.0.1.dist-info/top_level.txt,sha256=XPJLb-gXBFxypv8aGn2jSIj3z9ltI6c_ZMZaLsZzhBk,16
14
+ brynq_sdk_vplan-2.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.10.2)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ brynq_sdk_vplan