-
Notifications
You must be signed in to change notification settings - Fork 350
Expand file tree
/
Copy pathexport.py
More file actions
146 lines (125 loc) · 4.91 KB
/
Copy pathexport.py
File metadata and controls
146 lines (125 loc) · 4.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import logging
from datetime import datetime, timedelta
from normality import stringify
from sqlalchemy.dialects.postgresql import JSONB
from servicelayer.cache import make_key
from aleph.core import db
from aleph.model import Role, Collection
from aleph.model.common import IdModel, DatedModel, Status
log = logging.getLogger(__name__)
class Export(db.Model, IdModel, DatedModel):
"""A data export run in the background. The data is stored in a cloud
storage bucket and the user is given a link to download the data. The link
expires after a fixed duration and the exported data is deleted."""
DEFAULT_EXPIRATION = timedelta(days=30) # After 30 days
label = db.Column(db.Unicode)
operation = db.Column(db.Unicode)
creator_id = db.Column(db.Integer, db.ForeignKey("role.id"))
creator = db.relationship(Role, backref=db.backref("exports", lazy="dynamic"))
collection_id = db.Column(
db.Integer, db.ForeignKey("collection.id"), index=True, nullable=True
)
collection = db.relationship(
Collection, backref=db.backref("exports", lazy="dynamic")
)
expires_at = db.Column(db.DateTime, default=None, nullable=True)
deleted = db.Column(db.Boolean, default=False)
status = db.Column("export_status", db.Unicode, default=Status.DEFAULT)
content_hash = db.Column(db.Unicode(65), index=True, nullable=True)
file_size = db.Column(db.BigInteger, nullable=True) # In bytes
file_name = db.Column(db.Unicode, nullable=True)
mime_type = db.Column(db.Unicode)
meta = db.Column(JSONB, default={})
def to_dict(self):
data = self.to_dict_dates()
data.update(
{
"id": stringify(self.id),
"label": self.label,
"operation": self.operation,
"creator_id": stringify(self.creator_id),
"collection_id": self.collection_id,
"expires_at": self.expires_at,
"deleted": self.deleted,
"status": Status.LABEL.get(self.status),
"content_hash": self.content_hash,
"file_size": self.file_size,
"file_name": self.file_name,
"mime_type": self.mime_type,
"meta": self.meta,
}
)
return data
@classmethod
def create(
cls, operation, role_id, label, collection=None, mime_type=None, meta=None
):
export = cls()
export.creator_id = role_id
export.operation = operation
export.label = label
if collection is not None:
export.collection_id = collection.id
export.mime_type = mime_type
export.updated_at = datetime.utcnow()
export.expires_at = datetime.utcnow() + cls.DEFAULT_EXPIRATION
export.meta = meta or {}
db.session.add(export)
return export
@property
def namespace(self):
return make_key("role", self.creator_id)
def set_status(self, status):
self.status = status
self.updated_at = datetime.utcnow()
db.session.add(self)
def should_delete_publication(self):
"""Check whether the published export should be deleted from the archive
Since we store exports by contenthash, there may be other non-expired exports
that point to the same file in the archive"""
q = (
Export.all()
.filter(Export.content_hash == self.content_hash)
.filter(Export.deleted.isnot(True))
.filter(Export.id != self.id)
)
return q.first() is None
@classmethod
def get_expired(cls, deleted=False):
q = cls.all()
q = q.filter(cls.expires_at <= datetime.utcnow())
if not deleted:
q = q.filter(cls.deleted == deleted)
return q
@classmethod
def get_pending(cls):
q = cls.all()
q = q.filter(cls.status == Status.PENDING)
q = q.filter(cls.deleted == False) # noqa
return q
@classmethod
def by_id(cls, id, role_id=None, deleted=False):
q = cls.all().filter_by(id=id)
if role_id is not None:
q = q.filter(cls.creator_id == role_id)
if not deleted:
q = q.filter(cls.deleted == False) # noqa
return q.first()
@classmethod
def by_role_id(cls, role_id, deleted=False):
q = cls.all()
q = q.filter(cls.creator_id == role_id)
if not deleted:
q = q.filter(cls.deleted == False) # noqa
q = q.filter(cls.expires_at > datetime.utcnow())
q = q.order_by(cls.created_at.desc())
return q
@classmethod
def by_content_hash(cls, content_hash, deleted=False):
q = cls.all()
q = q.filter(cls.content_hash == content_hash)
if not deleted:
q = q.filter(cls.deleted == False) # noqa
return q
def __repr__(self):
return "<Export(%r, %r, %r)>" % (self.id, self.creator_id, self.label)