We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add documentation for creating and uploading embeddings through the pip package (non cli)
The CLI way of obtaining and uploading embeddings is described. It's pretty minimal but works. However, there is no information on how to do this yourself without using the CLI. 

I would prefer steps starting from having a pre-trained model.
```python
class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, 3, 1)
        self.conv2 = nn.Conv2d(32, 64, 3, 1)
        self.dropout1 = nn.Dropout(0.25)
        self.dropout2 = nn.Dropout(0.5)
        self.fc1 = nn.Linear(9216, 128)
        self.fc2 = nn.Linear(128, 10)

    def embed(self, x):
        x = self.conv1(x)
        x = F.relu(x)
        x = self.conv2(x)
        x = F.relu(x)
        x = F.max_pool2d(x, 2)
        x = self.dropout1(x)
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.dropout2(x)
        x = self.fc2(x)
        return x

    def forward(self, x):
        x = self.embed(x)
        output = F.log_softmax(x, dim=1)
        return output

model = Net()

# let's assume here model is pre-trained
# I can get the embeddings and predictions using
embedding = model.embed(x)
prediction = model(x)

# TODO: how to create embeddings and upload them to lightly
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lightly/api/api_workflow_client.py`
Content:
```
1 import time
2 import random
3 import time
4 import warnings
5 from io import IOBase
6 from typing import *
7 
8 import requests
9 from requests import Response
10 
11 from lightly.__init__ import __version__
12 from lightly.api.api_workflow_datasets import _DatasetsMixin
13 from lightly.api.api_workflow_download_dataset import _DownloadDatasetMixin
14 from lightly.api.api_workflow_sampling import _SamplingMixin
15 from lightly.api.api_workflow_upload_dataset import _UploadDatasetMixin
16 from lightly.api.api_workflow_upload_embeddings import _UploadEmbeddingsMixin
17 from lightly.api.utils import getenv
18 from lightly.api.version_checking import get_minimum_compatible_version, version_compare
19 from lightly.openapi_generated.swagger_client import TagData, ScoresApi, QuotaApi
20 from lightly.openapi_generated.swagger_client.api.datasets_api import DatasetsApi
21 from lightly.openapi_generated.swagger_client.api.embeddings_api import EmbeddingsApi
22 from lightly.openapi_generated.swagger_client.api.jobs_api import JobsApi
23 from lightly.openapi_generated.swagger_client.api.mappings_api import MappingsApi
24 from lightly.openapi_generated.swagger_client.api.samples_api import SamplesApi
25 from lightly.openapi_generated.swagger_client.api.samplings_api import SamplingsApi
26 from lightly.openapi_generated.swagger_client.api.tags_api import TagsApi
27 from lightly.openapi_generated.swagger_client.api_client import ApiClient
28 from lightly.openapi_generated.swagger_client.configuration import Configuration
29 from lightly.openapi_generated.swagger_client.models.dataset_data import DatasetData
30 
31 
32 class ApiWorkflowClient(_UploadEmbeddingsMixin, _SamplingMixin, _UploadDatasetMixin, _DownloadDatasetMixin,
33                         _DatasetsMixin):
34     """Provides a uniform interface to communicate with the api 
35     
36     The APIWorkflowClient is used to communicaate with the Lightly API. The client
37     can run also more complex workflows which include multiple API calls at once.
38     
39     The client can be used in combination with the active learning agent. 
40 
41     Args:
42         token:
43             the token of the user, provided in webapp
44         dataset_id:
45             the id of the dataset, provided in webapp. \
46             If it is not set, but used by a workflow, \
47             the last modfied dataset is taken by default.
48         embedding_id:
49             the id of the embedding to use. If it is not set, \
50             but used by a workflow, the newest embedding is taken by default
51     """
52 
53     def __init__(self, token: str, dataset_id: str = None, embedding_id: str = None):
54 
55         self.check_version_compatibility()
56 
57         configuration = Configuration()
58         configuration.host = getenv('LIGHTLY_SERVER_LOCATION', 'https://api.lightly.ai')
59         configuration.api_key = {'token': token}
60         api_client = ApiClient(configuration=configuration)
61         self.api_client = api_client
62 
63         self.token = token
64         if dataset_id is not None:
65             self._dataset_id = dataset_id
66         if embedding_id is not None:
67             self.embedding_id = embedding_id
68 
69         self.datasets_api = DatasetsApi(api_client=self.api_client)
70         self.samplings_api = SamplingsApi(api_client=self.api_client)
71         self.jobs_api = JobsApi(api_client=self.api_client)
72         self.tags_api = TagsApi(api_client=self.api_client)
73         self.embeddings_api = EmbeddingsApi(api_client=api_client)
74         self.mappings_api = MappingsApi(api_client=api_client)
75         self.scores_api = ScoresApi(api_client=api_client)
76         self.samples_api = SamplesApi(api_client=api_client)
77         self.quota_api = QuotaApi(api_client=api_client)
78 
79     def check_version_compatibility(self):
80         minimum_version = get_minimum_compatible_version()
81         if version_compare(__version__, minimum_version) < 0:
82             raise ValueError(f"Incompatible Version of lightly pip package. "
83                              f"Please upgrade to at least version {minimum_version} "
84                              f"to be able to access the api and webapp")
85 
86     @property
87     def dataset_id(self) -> str:
88         ''' Returns the dataset_id
89 
90         If the dataset_id is set, it is returned.
91         If it is unset, then the dataset_id of the last modified dataset is taken.
92 
93         '''
94         try:
95             return self._dataset_id
96         except AttributeError:
97             all_datasets: List[DatasetData] = self.datasets_api.get_datasets()
98             datasets_sorted = sorted(all_datasets, key=lambda dataset: dataset.last_modified_at)
99             last_modified_dataset = datasets_sorted[-1]
100             self._dataset_id = last_modified_dataset.id
101             warnings.warn(UserWarning(f"Dataset has not been specified, "
102                                       f"taking the last modified dataset {last_modified_dataset.name} as default dataset."))
103             return self._dataset_id
104 
105     def _get_all_tags(self) -> List[TagData]:
106         return self.tags_api.get_tags_by_dataset_id(self.dataset_id)
107 
108     def _order_list_by_filenames(self, filenames_for_list: List[str], list_to_order: List[object]) -> List[object]:
109         """Orders a list such that it is in the order of the filenames specified on the server.
110 
111         Args:
112             filenames_for_list:
113                 The filenames of samples in a specific order
114             list_to_order:
115                 Some values belonging to the samples
116 
117         Returns:
118             The list reordered. The same reorder applied on the filenames_for_list
119             would put them in the order of the filenames in self.filenames_on_server
120 
121         """
122         assert len(filenames_for_list) == len(list_to_order)
123         dict_by_filenames = dict(zip(filenames_for_list, list_to_order))
124         list_ordered = [dict_by_filenames[filename] for filename in self.filenames_on_server
125                         if filename in filenames_for_list]
126         return list_ordered
127 
128     @property
129     def filenames_on_server(self):
130         if not hasattr(self, "_filenames_on_server"):
131             self._filenames_on_server = self.mappings_api. \
132                 get_sample_mappings_by_dataset_id(dataset_id=self.dataset_id, field="fileName")
133         return self._filenames_on_server
134 
135     def upload_file_with_signed_url(self, file: IOBase, signed_write_url: str,
136                                     max_backoff: int = 32, max_retries: int = 5) -> Response:
137         """Uploads a file to a url via a put request.
138 
139         Args:
140             file:
141                 The file to upload.
142             signed_write_url:
143                 The url to upload the file to. As no authorization is used,
144                 the url must be a signed write url.
145             max_backoff:
146                 Maximal backoff before retrying.
147             max_retries:
148                 Maximum number of retries before timing out.
149 
150         Returns:
151             The response of the put request, usually a 200 for the success case.
152 
153         """
154 
155         response = requests.put(signed_write_url, data=file)
156 
157         if response.status_code != 200:
158             msg = f'Failed PUT request to {signed_write_url} with status_code'
159             msg += f'{response.status__code}!'
160             raise RuntimeError(msg)
161 
162         return response
163
```

Path: `lightly/api/api_workflow_upload_embeddings.py`
Content:
```
1 import csv
2 from typing import List
3 
4 from lightly.openapi_generated.swagger_client.models.dataset_embedding_data import DatasetEmbeddingData
5 from lightly.openapi_generated.swagger_client.models.write_csv_url_data import WriteCSVUrlData
6 
7 
8 
9 def _is_valid_filename(filename: str):
10     """Returns False if the filename is misformatted.
11 
12     """
13     invalid_characters = [',']
14     for character in invalid_characters:
15         if character in filename:
16             return False
17     return True
18 
19 
20 class _UploadEmbeddingsMixin:
21 
22     def set_embedding_id_by_name(self, embedding_name: str = None):
23         embeddings: List[DatasetEmbeddingData] = \
24             self.embeddings_api.get_embeddings_by_dataset_id(dataset_id=self.dataset_id)
25 
26         if embedding_name is None:
27             self.embedding_id = embeddings[-1].id
28             return
29 
30         try:
31             self.embedding_id = next(embedding.id for embedding in embeddings if embedding.name == embedding_name)
32         except StopIteration:
33             raise ValueError(f"No embedding with name {embedding_name} found on the server.")
34 
35     def upload_embeddings(self, path_to_embeddings_csv: str, name: str):
36         """Uploads embeddings to the server.
37 
38         First checks that the specified embedding name is not on ther server. If it is, the upload is aborted.
39         Then creates a new csv with the embeddings in the order specified on the server. Next it uploads it to the server.
40         The received embedding_id is saved as a property of self.
41         Args:
42             path_to_embeddings_csv: the filepath to the .csv containing the embeddings, e.g. "path/to/embeddings.csv"
43             name: The name of the embedding. If an embedding with such a name already exists on the server,
44                 the upload is aborted.
45 
46         Returns:
47             None
48 
49         """
50         # get the names of the current embeddings on the server:
51         embeddings_on_server: List[DatasetEmbeddingData] = \
52             self.embeddings_api.get_embeddings_by_dataset_id(dataset_id=self.dataset_id)
53         names_embeddings_on_server = [embedding.name for embedding in embeddings_on_server]
54 
55         if name in names_embeddings_on_server:
56             print(f"Aborting upload, embedding with name='{name}' already exists.")
57             self.embedding_id = next(embedding for embedding in embeddings_on_server if embedding.name == name).id
58             return
59 
60         # create a new csv with the filenames in the desired order
61         path_to_ordered_embeddings_csv = self._order_csv_by_filenames(
62             path_to_embeddings_csv=path_to_embeddings_csv)
63 
64         # get the URL to upload the csv to
65         response: WriteCSVUrlData = \
66             self.embeddings_api.get_embeddings_csv_write_url_by_id(self.dataset_id, name=name)
67         self.embedding_id = response.embedding_id
68         signed_write_url = response.signed_write_url
69 
70         # upload the csv to the URL
71         with open(path_to_ordered_embeddings_csv, 'rb') as file_ordered_embeddings_csv:
72             self.upload_file_with_signed_url(file=file_ordered_embeddings_csv, signed_write_url=signed_write_url)
73 
74     def _order_csv_by_filenames(self, path_to_embeddings_csv: str) -> str:
75         """Orders the rows in a csv according to the order specified on the server and saves it as a new file.
76 
77         Args:
78             path_to_embeddings_csv:
79                 the path to the csv to order
80 
81         Returns:
82             the filepath to the new csv
83 
84         """
85         with open(path_to_embeddings_csv, 'r') as f:
86             data = csv.reader(f)
87 
88             rows = list(data)
89             header_row = rows[0]
90             rows_without_header = rows[1:]
91             index_filenames = header_row.index('filenames')
92             filenames = [row[index_filenames] for row in rows_without_header]
93 
94             if len(filenames) != len(self.filenames_on_server):
95                 raise ValueError(f'There are {len(filenames)} rows in the embedding file, but '
96                                  f'{len(self.filenames_on_server)} filenames/samples on the server.')
97             if set(filenames) != set(self.filenames_on_server):
98                 raise ValueError(f'The filenames in the embedding file and the filenames on the server do not align')
99             invalid_filenames = [f for f in filenames if not _is_valid_filename(f)]
100             if len(invalid_filenames) > 0:
101                 raise ValueError(f'Invalid filename(s) in embedding file: {invalid_filenames}')
102 
103             rows_without_header_ordered = self._order_list_by_filenames(filenames, rows_without_header)
104 
105             rows_to_write = [header_row]
106             rows_to_write += rows_without_header_ordered
107 
108         path_to_ordered_embeddings_csv = path_to_embeddings_csv.replace('.csv', '_sorted.csv')
109         with open(path_to_ordered_embeddings_csv, 'w') as f:
110             writer = csv.writer(f)
111             writer.writerows(rows_to_write)
112 
113         return path_to_ordered_embeddings_csv
114
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lightly/api/api_workflow_client.py b/lightly/api/api_workflow_client.py
--- a/lightly/api/api_workflow_client.py
+++ b/lightly/api/api_workflow_client.py
@@ -85,12 +85,11 @@
 
     @property
     def dataset_id(self) -> str:
-        ''' Returns the dataset_id
+        '''The current dataset_id.
 
         If the dataset_id is set, it is returned.
-        If it is unset, then the dataset_id of the last modified dataset is taken.
-
-        '''
+        If it is not set, then the dataset_id of the last modified dataset is selected.
+        ''' 
         try:
             return self._dataset_id
         except AttributeError:
@@ -127,6 +126,9 @@
 
     @property
     def filenames_on_server(self):
+        '''The list of the filenames in the dataset.
+
+        '''
         if not hasattr(self, "_filenames_on_server"):
             self._filenames_on_server = self.mappings_api. \
                 get_sample_mappings_by_dataset_id(dataset_id=self.dataset_id, field="fileName")
diff --git a/lightly/api/api_workflow_upload_embeddings.py b/lightly/api/api_workflow_upload_embeddings.py
--- a/lightly/api/api_workflow_upload_embeddings.py
+++ b/lightly/api/api_workflow_upload_embeddings.py
@@ -20,6 +20,15 @@
 class _UploadEmbeddingsMixin:
 
     def set_embedding_id_by_name(self, embedding_name: str = None):
+        """Sets the embedding id of the client by embedding name.
+
+        Args:
+            embedding_name:
+                Name under which the embedding was uploaded.
+    
+        Raises:
+            ValueError if the embedding does not exist.
+        """
         embeddings: List[DatasetEmbeddingData] = \
             self.embeddings_api.get_embeddings_by_dataset_id(dataset_id=self.dataset_id)
 
@@ -38,14 +47,14 @@
         First checks that the specified embedding name is not on ther server. If it is, the upload is aborted.
         Then creates a new csv with the embeddings in the order specified on the server. Next it uploads it to the server.
         The received embedding_id is saved as a property of self.
+
         Args:
-            path_to_embeddings_csv: the filepath to the .csv containing the embeddings, e.g. "path/to/embeddings.csv"
-            name: The name of the embedding. If an embedding with such a name already exists on the server,
+            path_to_embeddings_csv:
+                The path to the .csv containing the embeddings, e.g. "path/to/embeddings.csv"
+            name:
+                The name of the embedding. If an embedding with such a name already exists on the server,
                 the upload is aborted.
 
-        Returns:
-            None
-
         """
         # get the names of the current embeddings on the server:
         embeddings_on_server: List[DatasetEmbeddingData] = \

{"golden_diff": "diff --git a/lightly/api/api_workflow_client.py b/lightly/api/api_workflow_client.py\n--- a/lightly/api/api_workflow_client.py\n+++ b/lightly/api/api_workflow_client.py\n@@ -85,12 +85,11 @@\n \n     @property\n     def dataset_id(self) -> str:\n-        ''' Returns the dataset_id\n+        '''The current dataset_id.\n \n         If the dataset_id is set, it is returned.\n-        If it is unset, then the dataset_id of the last modified dataset is taken.\n-\n-        '''\n+        If it is not set, then the dataset_id of the last modified dataset is selected.\n+        ''' \n         try:\n             return self._dataset_id\n         except AttributeError:\n@@ -127,6 +126,9 @@\n \n     @property\n     def filenames_on_server(self):\n+        '''The list of the filenames in the dataset.\n+\n+        '''\n         if not hasattr(self, \"_filenames_on_server\"):\n             self._filenames_on_server = self.mappings_api. \\\n                 get_sample_mappings_by_dataset_id(dataset_id=self.dataset_id, field=\"fileName\")\ndiff --git a/lightly/api/api_workflow_upload_embeddings.py b/lightly/api/api_workflow_upload_embeddings.py\n--- a/lightly/api/api_workflow_upload_embeddings.py\n+++ b/lightly/api/api_workflow_upload_embeddings.py\n@@ -20,6 +20,15 @@\n class _UploadEmbeddingsMixin:\n \n     def set_embedding_id_by_name(self, embedding_name: str = None):\n+        \"\"\"Sets the embedding id of the client by embedding name.\n+\n+        Args:\n+            embedding_name:\n+                Name under which the embedding was uploaded.\n+    \n+        Raises:\n+            ValueError if the embedding does not exist.\n+        \"\"\"\n         embeddings: List[DatasetEmbeddingData] = \\\n             self.embeddings_api.get_embeddings_by_dataset_id(dataset_id=self.dataset_id)\n \n@@ -38,14 +47,14 @@\n         First checks that the specified embedding name is not on ther server. If it is, the upload is aborted.\n         Then creates a new csv with the embeddings in the order specified on the server. Next it uploads it to the server.\n         The received embedding_id is saved as a property of self.\n+\n         Args:\n-            path_to_embeddings_csv: the filepath to the .csv containing the embeddings, e.g. \"path/to/embeddings.csv\"\n-            name: The name of the embedding. If an embedding with such a name already exists on the server,\n+            path_to_embeddings_csv:\n+                The path to the .csv containing the embeddings, e.g. \"path/to/embeddings.csv\"\n+            name:\n+                The name of the embedding. If an embedding with such a name already exists on the server,\n                 the upload is aborted.\n \n-        Returns:\n-            None\n-\n         \"\"\"\n         # get the names of the current embeddings on the server:\n         embeddings_on_server: List[DatasetEmbeddingData] = \\\n", "issue": "Add documentation for creating and uploading embeddings through the pip package (non cli)\nThe CLI way of obtaining and uploading embeddings is described. It's pretty minimal but works. However, there is no information on how to do this yourself without using the CLI. \r\n\r\nI would prefer steps starting from having a pre-trained model.\r\n```python\r\nclass Net(nn.Module):\r\n    def __init__(self):\r\n        super(Net, self).__init__()\r\n        self.conv1 = nn.Conv2d(1, 32, 3, 1)\r\n        self.conv2 = nn.Conv2d(32, 64, 3, 1)\r\n        self.dropout1 = nn.Dropout(0.25)\r\n        self.dropout2 = nn.Dropout(0.5)\r\n        self.fc1 = nn.Linear(9216, 128)\r\n        self.fc2 = nn.Linear(128, 10)\r\n\r\n    def embed(self, x):\r\n        x = self.conv1(x)\r\n        x = F.relu(x)\r\n        x = self.conv2(x)\r\n        x = F.relu(x)\r\n        x = F.max_pool2d(x, 2)\r\n        x = self.dropout1(x)\r\n        x = torch.flatten(x, 1)\r\n        x = self.fc1(x)\r\n        x = F.relu(x)\r\n        x = self.dropout2(x)\r\n        x = self.fc2(x)\r\n        return x\r\n\r\n    def forward(self, x):\r\n        x = self.embed(x)\r\n        output = F.log_softmax(x, dim=1)\r\n        return output\r\n\r\nmodel = Net()\r\n\r\n# let's assume here model is pre-trained\r\n# I can get the embeddings and predictions using\r\nembedding = model.embed(x)\r\nprediction = model(x)\r\n\r\n# TODO: how to create embeddings and upload them to lightly\r\n```\n", "before_files": [{"content": "import time\nimport random\nimport time\nimport warnings\nfrom io import IOBase\nfrom typing import *\n\nimport requests\nfrom requests import Response\n\nfrom lightly.__init__ import __version__\nfrom lightly.api.api_workflow_datasets import _DatasetsMixin\nfrom lightly.api.api_workflow_download_dataset import _DownloadDatasetMixin\nfrom lightly.api.api_workflow_sampling import _SamplingMixin\nfrom lightly.api.api_workflow_upload_dataset import _UploadDatasetMixin\nfrom lightly.api.api_workflow_upload_embeddings import _UploadEmbeddingsMixin\nfrom lightly.api.utils import getenv\nfrom lightly.api.version_checking import get_minimum_compatible_version, version_compare\nfrom lightly.openapi_generated.swagger_client import TagData, ScoresApi, QuotaApi\nfrom lightly.openapi_generated.swagger_client.api.datasets_api import DatasetsApi\nfrom lightly.openapi_generated.swagger_client.api.embeddings_api import EmbeddingsApi\nfrom lightly.openapi_generated.swagger_client.api.jobs_api import JobsApi\nfrom lightly.openapi_generated.swagger_client.api.mappings_api import MappingsApi\nfrom lightly.openapi_generated.swagger_client.api.samples_api import SamplesApi\nfrom lightly.openapi_generated.swagger_client.api.samplings_api import SamplingsApi\nfrom lightly.openapi_generated.swagger_client.api.tags_api import TagsApi\nfrom lightly.openapi_generated.swagger_client.api_client import ApiClient\nfrom lightly.openapi_generated.swagger_client.configuration import Configuration\nfrom lightly.openapi_generated.swagger_client.models.dataset_data import DatasetData\n\n\nclass ApiWorkflowClient(_UploadEmbeddingsMixin, _SamplingMixin, _UploadDatasetMixin, _DownloadDatasetMixin,\n                        _DatasetsMixin):\n    \"\"\"Provides a uniform interface to communicate with the api \n    \n    The APIWorkflowClient is used to communicaate with the Lightly API. The client\n    can run also more complex workflows which include multiple API calls at once.\n    \n    The client can be used in combination with the active learning agent. \n\n    Args:\n        token:\n            the token of the user, provided in webapp\n        dataset_id:\n            the id of the dataset, provided in webapp. \\\n            If it is not set, but used by a workflow, \\\n            the last modfied dataset is taken by default.\n        embedding_id:\n            the id of the embedding to use. If it is not set, \\\n            but used by a workflow, the newest embedding is taken by default\n    \"\"\"\n\n    def __init__(self, token: str, dataset_id: str = None, embedding_id: str = None):\n\n        self.check_version_compatibility()\n\n        configuration = Configuration()\n        configuration.host = getenv('LIGHTLY_SERVER_LOCATION', 'https://api.lightly.ai')\n        configuration.api_key = {'token': token}\n        api_client = ApiClient(configuration=configuration)\n        self.api_client = api_client\n\n        self.token = token\n        if dataset_id is not None:\n            self._dataset_id = dataset_id\n        if embedding_id is not None:\n            self.embedding_id = embedding_id\n\n        self.datasets_api = DatasetsApi(api_client=self.api_client)\n        self.samplings_api = SamplingsApi(api_client=self.api_client)\n        self.jobs_api = JobsApi(api_client=self.api_client)\n        self.tags_api = TagsApi(api_client=self.api_client)\n        self.embeddings_api = EmbeddingsApi(api_client=api_client)\n        self.mappings_api = MappingsApi(api_client=api_client)\n        self.scores_api = ScoresApi(api_client=api_client)\n        self.samples_api = SamplesApi(api_client=api_client)\n        self.quota_api = QuotaApi(api_client=api_client)\n\n    def check_version_compatibility(self):\n        minimum_version = get_minimum_compatible_version()\n        if version_compare(__version__, minimum_version) < 0:\n            raise ValueError(f\"Incompatible Version of lightly pip package. \"\n                             f\"Please upgrade to at least version {minimum_version} \"\n                             f\"to be able to access the api and webapp\")\n\n    @property\n    def dataset_id(self) -> str:\n        ''' Returns the dataset_id\n\n        If the dataset_id is set, it is returned.\n        If it is unset, then the dataset_id of the last modified dataset is taken.\n\n        '''\n        try:\n            return self._dataset_id\n        except AttributeError:\n            all_datasets: List[DatasetData] = self.datasets_api.get_datasets()\n            datasets_sorted = sorted(all_datasets, key=lambda dataset: dataset.last_modified_at)\n            last_modified_dataset = datasets_sorted[-1]\n            self._dataset_id = last_modified_dataset.id\n            warnings.warn(UserWarning(f\"Dataset has not been specified, \"\n                                      f\"taking the last modified dataset {last_modified_dataset.name} as default dataset.\"))\n            return self._dataset_id\n\n    def _get_all_tags(self) -> List[TagData]:\n        return self.tags_api.get_tags_by_dataset_id(self.dataset_id)\n\n    def _order_list_by_filenames(self, filenames_for_list: List[str], list_to_order: List[object]) -> List[object]:\n        \"\"\"Orders a list such that it is in the order of the filenames specified on the server.\n\n        Args:\n            filenames_for_list:\n                The filenames of samples in a specific order\n            list_to_order:\n                Some values belonging to the samples\n\n        Returns:\n            The list reordered. The same reorder applied on the filenames_for_list\n            would put them in the order of the filenames in self.filenames_on_server\n\n        \"\"\"\n        assert len(filenames_for_list) == len(list_to_order)\n        dict_by_filenames = dict(zip(filenames_for_list, list_to_order))\n        list_ordered = [dict_by_filenames[filename] for filename in self.filenames_on_server\n                        if filename in filenames_for_list]\n        return list_ordered\n\n    @property\n    def filenames_on_server(self):\n        if not hasattr(self, \"_filenames_on_server\"):\n            self._filenames_on_server = self.mappings_api. \\\n                get_sample_mappings_by_dataset_id(dataset_id=self.dataset_id, field=\"fileName\")\n        return self._filenames_on_server\n\n    def upload_file_with_signed_url(self, file: IOBase, signed_write_url: str,\n                                    max_backoff: int = 32, max_retries: int = 5) -> Response:\n        \"\"\"Uploads a file to a url via a put request.\n\n        Args:\n            file:\n                The file to upload.\n            signed_write_url:\n                The url to upload the file to. As no authorization is used,\n                the url must be a signed write url.\n            max_backoff:\n                Maximal backoff before retrying.\n            max_retries:\n                Maximum number of retries before timing out.\n\n        Returns:\n            The response of the put request, usually a 200 for the success case.\n\n        \"\"\"\n\n        response = requests.put(signed_write_url, data=file)\n\n        if response.status_code != 200:\n            msg = f'Failed PUT request to {signed_write_url} with status_code'\n            msg += f'{response.status__code}!'\n            raise RuntimeError(msg)\n\n        return response\n", "path": "lightly/api/api_workflow_client.py"}, {"content": "import csv\nfrom typing import List\n\nfrom lightly.openapi_generated.swagger_client.models.dataset_embedding_data import DatasetEmbeddingData\nfrom lightly.openapi_generated.swagger_client.models.write_csv_url_data import WriteCSVUrlData\n\n\n\ndef _is_valid_filename(filename: str):\n    \"\"\"Returns False if the filename is misformatted.\n\n    \"\"\"\n    invalid_characters = [',']\n    for character in invalid_characters:\n        if character in filename:\n            return False\n    return True\n\n\nclass _UploadEmbeddingsMixin:\n\n    def set_embedding_id_by_name(self, embedding_name: str = None):\n        embeddings: List[DatasetEmbeddingData] = \\\n            self.embeddings_api.get_embeddings_by_dataset_id(dataset_id=self.dataset_id)\n\n        if embedding_name is None:\n            self.embedding_id = embeddings[-1].id\n            return\n\n        try:\n            self.embedding_id = next(embedding.id for embedding in embeddings if embedding.name == embedding_name)\n        except StopIteration:\n            raise ValueError(f\"No embedding with name {embedding_name} found on the server.\")\n\n    def upload_embeddings(self, path_to_embeddings_csv: str, name: str):\n        \"\"\"Uploads embeddings to the server.\n\n        First checks that the specified embedding name is not on ther server. If it is, the upload is aborted.\n        Then creates a new csv with the embeddings in the order specified on the server. Next it uploads it to the server.\n        The received embedding_id is saved as a property of self.\n        Args:\n            path_to_embeddings_csv: the filepath to the .csv containing the embeddings, e.g. \"path/to/embeddings.csv\"\n            name: The name of the embedding. If an embedding with such a name already exists on the server,\n                the upload is aborted.\n\n        Returns:\n            None\n\n        \"\"\"\n        # get the names of the current embeddings on the server:\n        embeddings_on_server: List[DatasetEmbeddingData] = \\\n            self.embeddings_api.get_embeddings_by_dataset_id(dataset_id=self.dataset_id)\n        names_embeddings_on_server = [embedding.name for embedding in embeddings_on_server]\n\n        if name in names_embeddings_on_server:\n            print(f\"Aborting upload, embedding with name='{name}' already exists.\")\n            self.embedding_id = next(embedding for embedding in embeddings_on_server if embedding.name == name).id\n            return\n\n        # create a new csv with the filenames in the desired order\n        path_to_ordered_embeddings_csv = self._order_csv_by_filenames(\n            path_to_embeddings_csv=path_to_embeddings_csv)\n\n        # get the URL to upload the csv to\n        response: WriteCSVUrlData = \\\n            self.embeddings_api.get_embeddings_csv_write_url_by_id(self.dataset_id, name=name)\n        self.embedding_id = response.embedding_id\n        signed_write_url = response.signed_write_url\n\n        # upload the csv to the URL\n        with open(path_to_ordered_embeddings_csv, 'rb') as file_ordered_embeddings_csv:\n            self.upload_file_with_signed_url(file=file_ordered_embeddings_csv, signed_write_url=signed_write_url)\n\n    def _order_csv_by_filenames(self, path_to_embeddings_csv: str) -> str:\n        \"\"\"Orders the rows in a csv according to the order specified on the server and saves it as a new file.\n\n        Args:\n            path_to_embeddings_csv:\n                the path to the csv to order\n\n        Returns:\n            the filepath to the new csv\n\n        \"\"\"\n        with open(path_to_embeddings_csv, 'r') as f:\n            data = csv.reader(f)\n\n            rows = list(data)\n            header_row = rows[0]\n            rows_without_header = rows[1:]\n            index_filenames = header_row.index('filenames')\n            filenames = [row[index_filenames] for row in rows_without_header]\n\n            if len(filenames) != len(self.filenames_on_server):\n                raise ValueError(f'There are {len(filenames)} rows in the embedding file, but '\n                                 f'{len(self.filenames_on_server)} filenames/samples on the server.')\n            if set(filenames) != set(self.filenames_on_server):\n                raise ValueError(f'The filenames in the embedding file and the filenames on the server do not align')\n            invalid_filenames = [f for f in filenames if not _is_valid_filename(f)]\n            if len(invalid_filenames) > 0:\n                raise ValueError(f'Invalid filename(s) in embedding file: {invalid_filenames}')\n\n            rows_without_header_ordered = self._order_list_by_filenames(filenames, rows_without_header)\n\n            rows_to_write = [header_row]\n            rows_to_write += rows_without_header_ordered\n\n        path_to_ordered_embeddings_csv = path_to_embeddings_csv.replace('.csv', '_sorted.csv')\n        with open(path_to_ordered_embeddings_csv, 'w') as f:\n            writer = csv.writer(f)\n            writer.writerows(rows_to_write)\n\n        return path_to_ordered_embeddings_csv\n", "path": "lightly/api/api_workflow_upload_embeddings.py"}], "after_files": [{"content": "import time\nimport random\nimport time\nimport warnings\nfrom io import IOBase\nfrom typing import *\n\nimport requests\nfrom requests import Response\n\nfrom lightly.__init__ import __version__\nfrom lightly.api.api_workflow_datasets import _DatasetsMixin\nfrom lightly.api.api_workflow_download_dataset import _DownloadDatasetMixin\nfrom lightly.api.api_workflow_sampling import _SamplingMixin\nfrom lightly.api.api_workflow_upload_dataset import _UploadDatasetMixin\nfrom lightly.api.api_workflow_upload_embeddings import _UploadEmbeddingsMixin\nfrom lightly.api.utils import getenv\nfrom lightly.api.version_checking import get_minimum_compatible_version, version_compare\nfrom lightly.openapi_generated.swagger_client import TagData, ScoresApi, QuotaApi\nfrom lightly.openapi_generated.swagger_client.api.datasets_api import DatasetsApi\nfrom lightly.openapi_generated.swagger_client.api.embeddings_api import EmbeddingsApi\nfrom lightly.openapi_generated.swagger_client.api.jobs_api import JobsApi\nfrom lightly.openapi_generated.swagger_client.api.mappings_api import MappingsApi\nfrom lightly.openapi_generated.swagger_client.api.samples_api import SamplesApi\nfrom lightly.openapi_generated.swagger_client.api.samplings_api import SamplingsApi\nfrom lightly.openapi_generated.swagger_client.api.tags_api import TagsApi\nfrom lightly.openapi_generated.swagger_client.api_client import ApiClient\nfrom lightly.openapi_generated.swagger_client.configuration import Configuration\nfrom lightly.openapi_generated.swagger_client.models.dataset_data import DatasetData\n\n\nclass ApiWorkflowClient(_UploadEmbeddingsMixin, _SamplingMixin, _UploadDatasetMixin, _DownloadDatasetMixin,\n                        _DatasetsMixin):\n    \"\"\"Provides a uniform interface to communicate with the api \n    \n    The APIWorkflowClient is used to communicaate with the Lightly API. The client\n    can run also more complex workflows which include multiple API calls at once.\n    \n    The client can be used in combination with the active learning agent. \n\n    Args:\n        token:\n            the token of the user, provided in webapp\n        dataset_id:\n            the id of the dataset, provided in webapp. \\\n            If it is not set, but used by a workflow, \\\n            the last modfied dataset is taken by default.\n        embedding_id:\n            the id of the embedding to use. If it is not set, \\\n            but used by a workflow, the newest embedding is taken by default\n    \"\"\"\n\n    def __init__(self, token: str, dataset_id: str = None, embedding_id: str = None):\n\n        self.check_version_compatibility()\n\n        configuration = Configuration()\n        configuration.host = getenv('LIGHTLY_SERVER_LOCATION', 'https://api.lightly.ai')\n        configuration.api_key = {'token': token}\n        api_client = ApiClient(configuration=configuration)\n        self.api_client = api_client\n\n        self.token = token\n        if dataset_id is not None:\n            self._dataset_id = dataset_id\n        if embedding_id is not None:\n            self.embedding_id = embedding_id\n\n        self.datasets_api = DatasetsApi(api_client=self.api_client)\n        self.samplings_api = SamplingsApi(api_client=self.api_client)\n        self.jobs_api = JobsApi(api_client=self.api_client)\n        self.tags_api = TagsApi(api_client=self.api_client)\n        self.embeddings_api = EmbeddingsApi(api_client=api_client)\n        self.mappings_api = MappingsApi(api_client=api_client)\n        self.scores_api = ScoresApi(api_client=api_client)\n        self.samples_api = SamplesApi(api_client=api_client)\n        self.quota_api = QuotaApi(api_client=api_client)\n\n    def check_version_compatibility(self):\n        minimum_version = get_minimum_compatible_version()\n        if version_compare(__version__, minimum_version) < 0:\n            raise ValueError(f\"Incompatible Version of lightly pip package. \"\n                             f\"Please upgrade to at least version {minimum_version} \"\n                             f\"to be able to access the api and webapp\")\n\n    @property\n    def dataset_id(self) -> str:\n        '''The current dataset_id.\n\n        If the dataset_id is set, it is returned.\n        If it is not set, then the dataset_id of the last modified dataset is selected.\n        ''' \n        try:\n            return self._dataset_id\n        except AttributeError:\n            all_datasets: List[DatasetData] = self.datasets_api.get_datasets()\n            datasets_sorted = sorted(all_datasets, key=lambda dataset: dataset.last_modified_at)\n            last_modified_dataset = datasets_sorted[-1]\n            self._dataset_id = last_modified_dataset.id\n            warnings.warn(UserWarning(f\"Dataset has not been specified, \"\n                                      f\"taking the last modified dataset {last_modified_dataset.name} as default dataset.\"))\n            return self._dataset_id\n\n    def _get_all_tags(self) -> List[TagData]:\n        return self.tags_api.get_tags_by_dataset_id(self.dataset_id)\n\n    def _order_list_by_filenames(self, filenames_for_list: List[str], list_to_order: List[object]) -> List[object]:\n        \"\"\"Orders a list such that it is in the order of the filenames specified on the server.\n\n        Args:\n            filenames_for_list:\n                The filenames of samples in a specific order\n            list_to_order:\n                Some values belonging to the samples\n\n        Returns:\n            The list reordered. The same reorder applied on the filenames_for_list\n            would put them in the order of the filenames in self.filenames_on_server\n\n        \"\"\"\n        assert len(filenames_for_list) == len(list_to_order)\n        dict_by_filenames = dict(zip(filenames_for_list, list_to_order))\n        list_ordered = [dict_by_filenames[filename] for filename in self.filenames_on_server\n                        if filename in filenames_for_list]\n        return list_ordered\n\n    @property\n    def filenames_on_server(self):\n        '''The list of the filenames in the dataset.\n\n        '''\n        if not hasattr(self, \"_filenames_on_server\"):\n            self._filenames_on_server = self.mappings_api. \\\n                get_sample_mappings_by_dataset_id(dataset_id=self.dataset_id, field=\"fileName\")\n        return self._filenames_on_server\n\n    def upload_file_with_signed_url(self, file: IOBase, signed_write_url: str,\n                                    max_backoff: int = 32, max_retries: int = 5) -> Response:\n        \"\"\"Uploads a file to a url via a put request.\n\n        Args:\n            file:\n                The file to upload.\n            signed_write_url:\n                The url to upload the file to. As no authorization is used,\n                the url must be a signed write url.\n            max_backoff:\n                Maximal backoff before retrying.\n            max_retries:\n                Maximum number of retries before timing out.\n\n        Returns:\n            The response of the put request, usually a 200 for the success case.\n\n        \"\"\"\n\n        response = requests.put(signed_write_url, data=file)\n\n        if response.status_code != 200:\n            msg = f'Failed PUT request to {signed_write_url} with status_code'\n            msg += f'{response.status__code}!'\n            raise RuntimeError(msg)\n\n        return response\n", "path": "lightly/api/api_workflow_client.py"}, {"content": "import csv\nfrom typing import List\n\nfrom lightly.openapi_generated.swagger_client.models.dataset_embedding_data import DatasetEmbeddingData\nfrom lightly.openapi_generated.swagger_client.models.write_csv_url_data import WriteCSVUrlData\n\n\n\ndef _is_valid_filename(filename: str):\n    \"\"\"Returns False if the filename is misformatted.\n\n    \"\"\"\n    invalid_characters = [',']\n    for character in invalid_characters:\n        if character in filename:\n            return False\n    return True\n\n\nclass _UploadEmbeddingsMixin:\n\n    def set_embedding_id_by_name(self, embedding_name: str = None):\n        \"\"\"Sets the embedding id of the client by embedding name.\n\n        Args:\n            embedding_name:\n                Name under which the embedding was uploaded.\n    \n        Raises:\n            ValueError if the embedding does not exist.\n        \"\"\"\n        embeddings: List[DatasetEmbeddingData] = \\\n            self.embeddings_api.get_embeddings_by_dataset_id(dataset_id=self.dataset_id)\n\n        if embedding_name is None:\n            self.embedding_id = embeddings[-1].id\n            return\n\n        try:\n            self.embedding_id = next(embedding.id for embedding in embeddings if embedding.name == embedding_name)\n        except StopIteration:\n            raise ValueError(f\"No embedding with name {embedding_name} found on the server.\")\n\n    def upload_embeddings(self, path_to_embeddings_csv: str, name: str):\n        \"\"\"Uploads embeddings to the server.\n\n        First checks that the specified embedding name is not on ther server. If it is, the upload is aborted.\n        Then creates a new csv with the embeddings in the order specified on the server. Next it uploads it to the server.\n        The received embedding_id is saved as a property of self.\n\n        Args:\n            path_to_embeddings_csv:\n                The path to the .csv containing the embeddings, e.g. \"path/to/embeddings.csv\"\n            name:\n                The name of the embedding. If an embedding with such a name already exists on the server,\n                the upload is aborted.\n\n        \"\"\"\n        # get the names of the current embeddings on the server:\n        embeddings_on_server: List[DatasetEmbeddingData] = \\\n            self.embeddings_api.get_embeddings_by_dataset_id(dataset_id=self.dataset_id)\n        names_embeddings_on_server = [embedding.name for embedding in embeddings_on_server]\n\n        if name in names_embeddings_on_server:\n            print(f\"Aborting upload, embedding with name='{name}' already exists.\")\n            self.embedding_id = next(embedding for embedding in embeddings_on_server if embedding.name == name).id\n            return\n\n        # create a new csv with the filenames in the desired order\n        path_to_ordered_embeddings_csv = self._order_csv_by_filenames(\n            path_to_embeddings_csv=path_to_embeddings_csv)\n\n        # get the URL to upload the csv to\n        response: WriteCSVUrlData = \\\n            self.embeddings_api.get_embeddings_csv_write_url_by_id(self.dataset_id, name=name)\n        self.embedding_id = response.embedding_id\n        signed_write_url = response.signed_write_url\n\n        # upload the csv to the URL\n        with open(path_to_ordered_embeddings_csv, 'rb') as file_ordered_embeddings_csv:\n            self.upload_file_with_signed_url(file=file_ordered_embeddings_csv, signed_write_url=signed_write_url)\n\n    def _order_csv_by_filenames(self, path_to_embeddings_csv: str) -> str:\n        \"\"\"Orders the rows in a csv according to the order specified on the server and saves it as a new file.\n\n        Args:\n            path_to_embeddings_csv:\n                the path to the csv to order\n\n        Returns:\n            the filepath to the new csv\n\n        \"\"\"\n        with open(path_to_embeddings_csv, 'r') as f:\n            data = csv.reader(f)\n\n            rows = list(data)\n            header_row = rows[0]\n            rows_without_header = rows[1:]\n            index_filenames = header_row.index('filenames')\n            filenames = [row[index_filenames] for row in rows_without_header]\n\n            if len(filenames) != len(self.filenames_on_server):\n                raise ValueError(f'There are {len(filenames)} rows in the embedding file, but '\n                                 f'{len(self.filenames_on_server)} filenames/samples on the server.')\n            if set(filenames) != set(self.filenames_on_server):\n                raise ValueError(f'The filenames in the embedding file and the filenames on the server do not align')\n            invalid_filenames = [f for f in filenames if not _is_valid_filename(f)]\n            if len(invalid_filenames) > 0:\n                raise ValueError(f'Invalid filename(s) in embedding file: {invalid_filenames}')\n\n            rows_without_header_ordered = self._order_list_by_filenames(filenames, rows_without_header)\n\n            rows_to_write = [header_row]\n            rows_to_write += rows_without_header_ordered\n\n        path_to_ordered_embeddings_csv = path_to_embeddings_csv.replace('.csv', '_sorted.csv')\n        with open(path_to_ordered_embeddings_csv, 'w') as f:\n            writer = csv.writer(f)\n            writer.writerows(rows_to_write)\n\n        return path_to_ordered_embeddings_csv\n", "path": "lightly/api/api_workflow_upload_embeddings.py"}]}