001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 *     http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.gbif.api.model.occurrence;
015
016import org.gbif.api.model.common.DOI;
017import org.gbif.api.model.registry.PostPersist;
018import org.gbif.api.model.registry.PrePersist;
019import org.gbif.api.vocabulary.License;
020
021import java.io.Serializable;
022import java.util.Date;
023import java.util.EnumSet;
024
025import javax.annotation.Nullable;
026import javax.validation.constraints.NotNull;
027import javax.validation.constraints.Null;
028
029import com.fasterxml.jackson.annotation.JsonIgnore;
030import com.fasterxml.jackson.annotation.JsonInclude;
031
032import io.swagger.v3.oas.annotations.Hidden;
033import io.swagger.v3.oas.annotations.media.Schema;
034import lombok.EqualsAndHashCode;
035import lombok.ToString;
036
037import static com.fasterxml.jackson.annotation.JsonInclude.*;
038
039@ToString
040@EqualsAndHashCode
041public class Download implements Serializable {
042
043  /**
044   * Reflects the possibles statuses of a download during its execution.
045   * The download statuses are:
046   * - PREPARING: the download is in a queue, waiting for other downloads to complete
047   * - RUNNING: the download is being processed
048   * - SUCCEEDED: the file is ready to be downloaded
049   * - CANCELLED: the download was cancelled by the user
050   * - KILLED: the download was killed by the workflow engine
051   * - FAILED: the download failed
052   * - SUSPENDED: the download was paused and its execution will be resumed later
053   * - FILE_ERASED: the download was successful, but the download file has been deleted
054   */
055  @Schema(
056    description = "The status of the download.\n\n" +
057      "* `PREPARING`: the download is in a queue, waiting for other downloads to complete\n" +
058      "* `RUNNING`: the download is being processed\n" +
059      "* `SUCCEEDED`: the download has completed and the file is ready to be downloaded\n" +
060      "* `CANCELLED`: the download was cancelled by the user\n" +
061      "* `KILLED`: the download was killed by the download engine\n" +
062      "* `FAILED`: the download failed\n" +
063      "* `SUSPENDED`: the download was paused and its execution will be resumed later\n" +
064      "* `FILE_ERASED`: the download was successful, but the download file " +
065      "[has since been deleted](https://www.gbif.org/faq?question=for-how-long-will-does-gbif-store-downloads)."
066  )
067  public enum Status {
068    PREPARING,
069    RUNNING,
070    SUCCEEDED,
071    CANCELLED,
072    KILLED,
073    FAILED,
074    SUSPENDED,
075    FILE_ERASED;
076
077    /**
078     * Statuses that represent a download that that hasn't finished.
079     */
080    public static final EnumSet<Status> EXECUTING_STATUSES = EnumSet.of(PREPARING, RUNNING, SUSPENDED);
081
082    /**
083     * Statuses that represent a download that that has finished.
084     */
085    public static final EnumSet<Status> FINISH_STATUSES = EnumSet.of(SUCCEEDED, CANCELLED, KILLED, FAILED, FILE_ERASED);
086  }
087
088  @Schema(
089    description = "The GBIF key assigned to the download.\n\n" +
090      "Note that citations should instead use the download DOI."
091  )
092  private String key;
093
094  @Schema(
095    description = "The primary Digital Object Identifier (DOI) for this download.",
096    implementation = String.class,
097    pattern = "(10(?:\\.[0-9]+)+)" + "/(.+)"
098  )
099  private DOI doi;
100
101  @Schema(
102    description = "The licence applied to the download.  This is calculated as the most restrictive licence of any " +
103      " of the data included in the download, other data may be less restrictive."
104  )
105  private License license;
106
107  @Schema(
108    description = "The filter used to request this download."
109  )
110  private DownloadRequest request;
111
112  @Schema(
113    description = "The time the download request was submitted."
114  )
115  private Date created;
116
117  @Schema(
118    description = "The time the download was last modified."
119  )
120  private Date modified;
121
122  @Schema(
123    description = "A time after which the download [may be deleted](https://www.gbif.org/faq?question=for-how-long-will-does-gbif-store-downloads)."
124  )
125  private Date eraseAfter;
126
127  @Schema(
128    description = "The most recent time when the creator of the download was notified the download is due to be deleted."
129  )
130  private Date erasureNotification;
131
132  // Documented in the enumeration above
133  private Status status;
134
135  @Schema(
136    description = "A link to download the data, if the status is `SUCCEEDED`."
137  )
138  private String downloadLink;
139
140  @Schema(
141    description = "The size, in bytes, of the download data."
142  )
143  private long size;
144
145  @Schema(
146    description = "The total number of occurrence records included in the download."
147  )
148  private long totalRecords;
149
150  @Schema(
151    description = "The total number of datasets from which occurrence records were drawn."
152  )
153  @JsonInclude(Include.NON_NULL)
154  private Long numberDatasets;
155
156  @Schema(
157    description = "The total number of organizations from which occurrence records were drawn."
158  )
159  @JsonInclude(Include.NON_NULL)
160  private Long numberOrganizations;
161
162  @Schema(
163    description = "The total number of publishing countries from which occurrence records were drawn."
164  )
165  @JsonInclude(Include.NON_NULL)
166  private Long numberPublishingCountries;
167
168  @Hidden
169  private String source;
170
171  /**
172   * @return timestamp when the download was created
173   */
174  @Null(groups = {PrePersist.class})
175  @NotNull(groups = {PostPersist.class})
176  @Nullable
177  public Date getCreated() {
178    return created;
179  }
180
181  /**
182   * @return the downloadLink
183   */
184  @Nullable
185  public String getDownloadLink() {
186    return downloadLink;
187  }
188
189  /**
190   * @return unique key that can be used in the REST service to retrieve its status
191   */
192  @NotNull
193  public String getKey() {
194    return key;
195  }
196
197  /**
198   * @return timestamp when the download result was last modified
199   */
200  @Null(groups = {PrePersist.class})
201  @NotNull(groups = {PostPersist.class})
202  @Nullable
203  public Date getModified() {
204    return modified;
205  }
206
207  /**
208   * @return timestamp after which the download file may be erased
209   */
210  @Nullable
211  public Date getEraseAfter() {
212    return eraseAfter;
213  }
214
215  /**
216   * @return timestamp when a notification of an impending file erasure was sent
217   */
218  @Nullable
219  public Date getErasureNotification() {
220    return erasureNotification;
221  }
222
223  /**
224   * Request that originated the download.
225   */
226  @NotNull
227  public DownloadRequest getRequest() {
228    return request;
229  }
230
231  /**
232   * @return the current download status
233   */
234  @NotNull
235  public Status getStatus() {
236    return status;
237  }
238
239  /**
240   * @return true if the download is completed and a result is available
241   */
242  @JsonIgnore
243  public boolean isAvailable() {
244    return status == Status.SUCCEEDED;
245  }
246
247  /**
248   * Size in bytes of the produced file.
249   */
250  public long getSize() {
251    return size;
252  }
253
254  /**
255   * Number of occurrence records in the download file.
256   */
257  public long getTotalRecords() {
258    return totalRecords;
259  }
260
261  /**
262   * Number of datasets in the download file.
263   */
264  @Nullable
265  public Long getNumberDatasets() {
266    return numberDatasets;
267  }
268
269  /**
270   * Source of the download to determine how the download was originated.
271   */
272  public String getSource() {
273    return source;
274  }
275
276  /**
277   * Occurrence download Digital Object Identifier.
278   */
279  @Nullable
280  public DOI getDoi() {
281    return doi;
282  }
283
284  /**
285   * Get the license assigned to this occurrence download.
286   */
287  public License getLicense() {
288    return license;
289  }
290
291  public void setCreated(Date created) {
292    this.created = created;
293  }
294
295  public void setDownloadLink(String downloadLink) {
296    this.downloadLink = downloadLink;
297  }
298
299  public void setKey(String key) {
300    this.key = key;
301  }
302
303  public void setDoi(DOI doi) {
304    this.doi = doi;
305  }
306
307  public void setLicense(License license) {
308    this.license = license;
309  }
310
311  public void setModified(Date modified) {
312    this.modified = modified;
313  }
314
315  public void setEraseAfter(Date eraseAfter) {
316    this.eraseAfter = eraseAfter;
317  }
318
319  public void setErasureNotification(Date erasureNotification) {
320    this.erasureNotification = erasureNotification;
321  }
322
323  public void setRequest(DownloadRequest request) {
324    this.request = request;
325  }
326
327  public void setStatus(Status status) {
328    this.status = status;
329  }
330
331  public void setSize(long size) {
332    this.size = size;
333  }
334
335  public void setTotalRecords(long totalRecords) {
336    this.totalRecords = totalRecords;
337  }
338
339  public void setNumberDatasets(Long numberDatasets) {
340    this.numberDatasets = numberDatasets;
341  }
342
343  public void setSource(String source) {
344    this.source = source;
345  }
346
347  @Nullable
348  public Long getNumberOrganizations() {
349    return numberOrganizations;
350  }
351
352  public void setNumberOrganizations(Long numberOrganizations) {
353    this.numberOrganizations = numberOrganizations;
354  }
355
356  @Nullable
357  public Long getNumberPublishingCountries() {
358    return numberPublishingCountries;
359  }
360
361  public void setNumberPublishingCountries(Long numberPublishingCountries) {
362    this.numberPublishingCountries = numberPublishingCountries;
363  }
364}