models.py
# Copyright (C) 2017-2018 The Software Heritage developers
# See the AUTHORS file at the top-level directory of this distribution
# License: GNU General Public License version 3, or any later version
# See top-level LICENSE file for more information
# Generated from:
# cd swh_deposit && \
# python3 -m manage inspectdb
from django.contrib.postgres.fields import JSONField, ArrayField
from django.contrib.auth.models import User, UserManager
from django.db import models
from django.utils.timezone import now
from .config import (
DEPOSIT_STATUS_VERIFIED, DEPOSIT_STATUS_DEPOSITED, DEPOSIT_STATUS_PARTIAL,
DEPOSIT_STATUS_LOAD_SUCCESS, DEPOSIT_STATUS_LOAD_FAILURE,
DEPOSIT_STATUS_REJECTED, ARCHIVE_TYPE, METADATA_TYPE
)
class Dbversion(models.Model):
"""Db version
"""
version = models.IntegerField(primary_key=True)
release = models.DateTimeField(default=now, null=True)
description = models.TextField(blank=True, null=True)
class Meta:
db_table = 'dbversion'
def __str__(self):
return str({
'version': self.version,
'release': self.release,
'description': self.description
})
"""Possible status"""
DEPOSIT_STATUS = [
(DEPOSIT_STATUS_PARTIAL, DEPOSIT_STATUS_PARTIAL),
('expired', 'expired'),
(DEPOSIT_STATUS_DEPOSITED, DEPOSIT_STATUS_DEPOSITED),
(DEPOSIT_STATUS_VERIFIED, DEPOSIT_STATUS_VERIFIED),
(DEPOSIT_STATUS_REJECTED, DEPOSIT_STATUS_REJECTED),
('loading', 'loading'),
(DEPOSIT_STATUS_LOAD_SUCCESS, DEPOSIT_STATUS_LOAD_SUCCESS),
(DEPOSIT_STATUS_LOAD_FAILURE, DEPOSIT_STATUS_LOAD_FAILURE),
]
"""Possible status and the detailed meaning."""
DEPOSIT_STATUS_DETAIL = {
DEPOSIT_STATUS_PARTIAL: 'Deposit is partially received. To finalize it, '
'In-Progress header should be false',
'expired': 'Deposit has been there too long and is now '
'deemed ready to be garbage collected',
DEPOSIT_STATUS_DEPOSITED: 'Deposit is ready for additional checks '
'(tarball ok, metadata, etc...)',
DEPOSIT_STATUS_VERIFIED: 'Deposit is fully received, checked, and '
'ready for loading',
DEPOSIT_STATUS_REJECTED: 'Deposit failed the checks',
'loading': "Loading is ongoing on swh's side",
DEPOSIT_STATUS_LOAD_SUCCESS: 'The deposit has been successfully '
'loaded into the Software Heritage archive',
DEPOSIT_STATUS_LOAD_FAILURE: 'The deposit loading into the '
'Software Heritage archive failed',
}
class DepositClient(User):
"""Deposit client
"""
collections = ArrayField(models.IntegerField(), null=True)
objects = UserManager() # type: ignore
# this typing hint is due to a mypy/django-stubs limitation,
# see https://github.com/typeddjango/django-stubs/issues/174
provider_url = models.TextField(null=False)
domain = models.TextField(null=False)
class Meta:
db_table = 'deposit_client'
def __str__(self):
return str({
'id': self.id,
'collections': self.collections,
'username': super().username,
'domain': self.domain,
'provider_url': self.provider_url,
})
class Deposit(models.Model):
"""Deposit reception table
"""
id = models.BigAutoField(primary_key=True)
# First deposit reception date
reception_date = models.DateTimeField(auto_now_add=True)
# Date when the deposit is deemed complete and ready for loading
complete_date = models.DateTimeField(null=True)
# collection concerned by the deposit
collection = models.ForeignKey(
'DepositCollection', models.DO_NOTHING)
# Deposit's external identifier
external_id = models.TextField()
# Deposit client
client = models.ForeignKey('DepositClient', models.DO_NOTHING)
# SWH's loading result identifier
swh_id = models.TextField(blank=True, null=True)
swh_id_context = models.TextField(blank=True, null=True)
swh_anchor_id = models.TextField(blank=True, null=True)
swh_anchor_id_context = models.TextField(blank=True, null=True)
# Deposit's status regarding loading
status = models.TextField(
choices=DEPOSIT_STATUS,
default=DEPOSIT_STATUS_PARTIAL)
status_detail = JSONField(null=True)
# deposit can have one parent
parent = models.ForeignKey('self', on_delete=models.PROTECT, null=True)
check_task_id = models.TextField(
blank=True, null=True,
verbose_name="Scheduler's associated checking task id"
)
load_task_id = models.TextField(
blank=True, null=True,
verbose_name="Scheduler's associated loading task id"
)
class Meta:
db_table = 'deposit'
def __str__(self):
d = {
'id': self.id,
'reception_date': self.reception_date,
'collection': self.collection.name,
'external_id': self.external_id,
'client': self.client.username,
'status': self.status,
}
if self.status in (DEPOSIT_STATUS_REJECTED):
d['status_detail'] = self.status_detail
return str(d)
@property
def origin_url(self):
return '%s/%s' % (self.client.provider_url.rstrip('/'),
self.external_id)
def client_directory_path(instance, filename):
"""Callable to upload archive in MEDIA_ROOT/user_<id>/<filename>
Args:
instance (DepositRequest): DepositRequest concerned by the upload
filename (str): Filename of the uploaded file
Returns:
A path to be prefixed by the MEDIA_ROOT to access physically
to the file uploaded.
"""
return 'client_{0}/{1}'.format(instance.deposit.client.id, filename)
REQUEST_TYPES = [(ARCHIVE_TYPE, ARCHIVE_TYPE),
(METADATA_TYPE, METADATA_TYPE)]
class DepositRequest(models.Model):
"""Deposit request associated to one deposit.
"""
id = models.BigAutoField(primary_key=True)
# Deposit concerned by the request
deposit = models.ForeignKey(Deposit, models.DO_NOTHING)
date = models.DateTimeField(auto_now_add=True)
# Deposit request information on the data to inject
# this can be null when type is 'archive'
metadata = JSONField(null=True)
raw_metadata = models.TextField(null=True)
# this can be null when type is 'metadata'
archive = models.FileField(null=True, upload_to=client_directory_path)
type = models.CharField(max_length=8,
choices=REQUEST_TYPES,
null=True)
class Meta:
db_table = 'deposit_request'
def __str__(self):
meta = None
if self.metadata:
from json import dumps
meta = dumps(self.metadata)
archive_name = None
if self.archive:
archive_name = self.archive.name
return str({
'id': self.id,
'deposit': self.deposit,
'metadata': meta,
'archive': archive_name
})
class DepositCollection(models.Model):
id = models.BigAutoField(primary_key=True)
# Human readable name for the collection type e.g HAL, arXiv, etc...
name = models.TextField()
class Meta:
db_table = 'deposit_collection'
def __str__(self):
return str({'id': self.id, 'name': self.name})