|
1 | 1 | import json |
2 | 2 | import time |
3 | 3 | from uuid import UUID, uuid4 |
| 4 | +import functools |
4 | 5 |
|
5 | 6 | import logging |
6 | 7 | from pathlib import Path |
@@ -113,6 +114,60 @@ class BulkImportRequest(DbObject): |
113 | 114 | project = Relationship.ToOne("Project") |
114 | 115 | created_by = Relationship.ToOne("User", False, "created_by") |
115 | 116 |
|
| 117 | + @property |
| 118 | + def inputs(self) -> Optional[List[Dict[str, str]]]: |
| 119 | + """ |
| 120 | + Inputs for each individual annotation uploaded. |
| 121 | + This should match the ndjson annotations that you have uploaded. |
| 122 | + |
| 123 | + Returns: |
| 124 | + Uploaded ndjsons. |
| 125 | +
|
| 126 | + * This information will expire after 24 hours. |
| 127 | + """ |
| 128 | + return self._fetch_remote_ndjson(self.input_file_url) |
| 129 | + |
| 130 | + @property |
| 131 | + def errors(self) -> Optional[List[Dict[str, str]]]: |
| 132 | + """ |
| 133 | + Errors for each individual annotation uploaded. |
| 134 | +
|
| 135 | + Returns: |
| 136 | + Empty list if there are no errors and None if the update is still running. |
| 137 | + If there are errors, and the job has completed then a list of dicts containing the error messages will be returned. |
| 138 | +
|
| 139 | + * This information will expire after 24 hours. |
| 140 | + """ |
| 141 | + return self._fetch_remote_ndjson(self.error_file_url) |
| 142 | + |
| 143 | + @property |
| 144 | + def statuses(self) -> Optional[List[Dict[str, str]]]: |
| 145 | + """ |
| 146 | + Status for each individual annotation uploaded. |
| 147 | +
|
| 148 | + Returns: |
| 149 | + A status for each annotation if the upload is done running and was successful. Otherwise it returns None. |
| 150 | +
|
| 151 | + * This information will expire after 24 hours. |
| 152 | + """ |
| 153 | + return self._fetch_remote_ndjson(self.status_file_url) |
| 154 | + |
| 155 | + @functools.lru_cache() |
| 156 | + def _fetch_remote_ndjson( |
| 157 | + self, url: Optional[str]) -> Optional[List[Dict[str, str]]]: |
| 158 | + """ |
| 159 | + Fetches the remote ndjson file and caches the results. |
| 160 | +
|
| 161 | + Args: |
| 162 | + url (str): either the input_file_url, error_file_url, status_file_url, or None |
| 163 | + urls are None when the file is unavailable. |
| 164 | + Returns: |
| 165 | + None if the url is None or the ndjson as a list of dicts. |
| 166 | + """ |
| 167 | + if url is not None: |
| 168 | + return ndjson.loads(requests.get(url).text) |
| 169 | + return None |
| 170 | + |
116 | 171 | def refresh(self) -> None: |
117 | 172 | """Synchronizes values of all fields with the database. |
118 | 173 | """ |
@@ -632,15 +687,14 @@ def validate_subclasses(cls, value, field): |
632 | 687 | #Create uuid and datarow id so we don't have to define classification objects twice |
633 | 688 | #This is caused by the fact that we require these ids for top level classifications but not for subclasses |
634 | 689 | results = [] |
| 690 | + dummy_id = 'child'.center(25, '_') |
635 | 691 | for row in value: |
636 | | - copied_row = row.copy() |
637 | | - copied_row.update({ |
638 | | - 'dataRow': { |
639 | | - 'id': 'child'.center(25, '_') |
| 692 | + results.append({ |
| 693 | + **row, 'dataRow': { |
| 694 | + 'id': dummy_id |
640 | 695 | }, |
641 | 696 | 'uuid': str(uuid4()) |
642 | 697 | }) |
643 | | - results.append(copied_row) |
644 | 698 | return results |
645 | 699 |
|
646 | 700 |
|
|
0 commit comments