Coretex
artifact.py
1 # Copyright (C) 2023 Coretex LLC
2 
3 # This file is part of Coretex.ai
4 
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
9 
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Affero General Public License for more details.
14 
15 # You should have received a copy of the GNU Affero General Public License
16 # along with this program. If not, see <https://www.gnu.org/licenses/>.
17 
18 from typing import Optional, Dict, List, Union, Any
19 from typing_extensions import Self
20 from enum import IntEnum
21 from pathlib import Path
22 
23 from ..._folder_manager import folder_manager
24 from ...codable import Codable, KeyDescriptor
25 from ...networking import networkManager, FileData
26 from ...utils import guessMimeType
27 
28 
29 class ArtifactType(IntEnum):
30 
31  """
32  Available types of Artifacts on Coretex
33  """
34 
35  directory = 1
36  file = 2
37 
38 
40 
41  """
42  Artifact class represents a single result of a run\n
43  Result can be file of any kind
44 
45  Properties
46  ----------
47  artifactType : ArtifactType
48  type of Artifact
49  remoteFilePath : str
50  path of Artifact on Coretex
51  size : Optional[int]
52  size of Artifact in bytes (not required)
53  mimeType : str
54  mimeType of Artifact
55  timestamp : int
56  current timestamp
57  taskRunId : int
58  id of run
59  """
60 
61  artifactType: ArtifactType
62  remoteFilePath: str
63  size: Optional[int]
64  mimeType: str
65  timestamp: int
66  taskRunId: int
67 
68  @property
69  def localFilePath(self) -> Path:
70  """
71  Represents the local path of the Artifact
72 
73  Returns
74  -------
75  Path -> local path to Artifact
76  """
77 
78  return folder_manager.getArtifactsFolder(self.taskRunId) / self.remoteFilePath
79 
80  @property
81  def isDirectory(self) -> bool:
82  """
83  Returns
84  -------
85  bool -> True if Artifact type is directory
86  """
87 
88  return self.artifactTypeartifactType == ArtifactType.directory
89 
90  @property
91  def isFile(self) -> bool:
92  """
93  Returns
94  -------
95  bool -> True if Artifact type is file
96  """
97 
98  return self.artifactTypeartifactType == ArtifactType.file
99 
100  @classmethod
101  def _keyDescriptors(cls) -> Dict[str, KeyDescriptor]:
102  descriptors = super()._keyDescriptors()
103 
104  descriptors["artifactType"] = KeyDescriptor("type", ArtifactType)
105  descriptors["remoteFilePath"] = KeyDescriptor("path")
106  descriptors["timestamp"] = KeyDescriptor("ts")
107  descriptors["taskRunId"] = KeyDescriptor(isEncodable = False)
108 
109  return descriptors
110 
111  @classmethod
112  def create(
113  cls,
114  taskRunId: int,
115  localFilePath: Union[Path, str],
116  remoteFilePath: str,
117  mimeType: Optional[str] = None
118  ) -> Optional[Self]:
119 
120  if mimeType is None:
121  # If guessing fails, fallback to binary type
122  try:
123  mimeType = guessMimeType(localFilePath)
124  except:
125  mimeType = "application/octet-stream"
126 
127  parameters = {
128  "model_queue_id": taskRunId,
129  "path": remoteFilePath
130  }
131 
132  files = [
133  FileData.createFromPath("file", localFilePath, mimeType = mimeType)
134  ]
135 
136  response = networkManager.formData("artifact/upload-file", parameters, files)
137  if response.hasFailed():
138  return None
139 
140  artifact = cls.decodedecode(response.getJson(dict))
141  artifact.taskRunId = taskRunId
142 
143  return artifact
144 
145  def download(self) -> bool:
146  """
147  Downloads Artifact from Coretex.ai
148 
149  Returns
150  -------
151  bool -> False if response has failed, True otherwise
152  """
153 
154  params = {
155  "model_queue_id": self.taskRunId,
156  "path": self.remoteFilePath
157  }
158 
159  return not networkManager.download("artifact/download-file", str(self.localFilePathlocalFilePath), params).hasFailed()
160 
161  @classmethod
162  def fetchAll(cls, taskRunId: int, path: Optional[str] = None, recursive: bool = False) -> List[Self]:
163  """
164  Fetch all Artifacts from Coretex.ai for the specified run
165 
166  Parameters
167  ----------
168  taskRunId : int
169  id of run
170  path : Optional[str]
171  local path where u want to store fetched Artifacts
172  recursive : bool
173  True if you want list to be sorted recursively, False otherwise
174  """
175 
176  params: Dict[str, Any] = {
177  "model_queue_id": taskRunId,
178  }
179 
180  if path is not None:
181  params["path"] = path
182 
183  response = networkManager.get("artifact/list-contents", params)
184  if response.hasFailed():
185  return []
186 
187  artifacts = [cls.decodedecode(element) for element in response.getJson(list)]
188 
189  for artifact in artifacts:
190  artifact.taskRunId = taskRunId
191 
192  if recursive and artifact.isDirectory:
193  artifacts.extend(
194  cls.fetchAllfetchAll(taskRunId, artifact.remoteFilePath)
195  )
196 
197  return artifacts
Self decode(cls, Dict[str, Any] encodedObject)
Definition: codable.py:239
List[Self] fetchAll(cls, int taskRunId, Optional[str] path=None, bool recursive=False)
Definition: artifact.py:162