summaryrefslogtreecommitdiffstats
path: root/victoria/models.py
blob: 6a8dc89611f9d91dcbd28332efa4fc35d45119ca (plain) (blame)
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
147
148
149
150
151
152
153
154
155
156
157
158
from victoria import db, login_manager
from flask_login import UserMixin
from datetime import datetime

# for sqlalchemy_media
import json
import functools
from os import path, getcwd
from sqlalchemy import TypeDecorator, Unicode
from sqlalchemy_media import File, Image, ImageValidator, ImageProcessor, ImageAnalyzer, StoreManager, \
    FileSystemStore
from sqlalchemy_media.constants import MB, KB

WORKING_DIR = path.abspath(getcwd())
TEMP_PATH = path.join(WORKING_DIR, 'static', 'media')

StoreManager.register(
    'fs',
    functools.partial(FileSystemStore, TEMP_PATH, 'http://localhost:5000/static/media'),
    default=True
)

# JSON mutable type for SQLite
class Json(TypeDecorator):
    impl = Unicode

    def process_bind_param(self, value, engine):
        return json.dumps(value)

    def process_result_value(self, value, engine):
        if value is None:
            return None
        return json.loads(value)

class ProfilePicture(Image):
    __pre_processors__ = [
        ImageAnalyzer(),
        ImageValidator(
            minimum=(10, 10),
            maximum=(3840, 3840),
            content_types=('image/jpeg', 'image/png', 'image/gif')
        ),
        ImageProcessor(
            fmt='jpeg'
        )
    ]
    __max_length__ = 10 * MB
    __min_length__ = 10 * KB

class MediaFile(Image):
    __pre_processors__ = [
        ImageAnalyzer(),
        ImageValidator(
            minimum=(10, 10),
            maximum=(3840, 3840),
            content_types=('image/jpeg', 'image/png', 'image/gif')
        ),
        ImageProcessor(
            fmt='jpeg'
        )
    ]
    __max_length__ = 20 * MB
    __min_length__ = 10 * KB


# login_manager
@login_manager.user_loader
def load_user(artist_id):
    return Artist.query.get(int(artist_id))

# helper tables
artist_work = db.Table('artist_work',
    db.Column('artist_id', db.Integer, db.ForeignKey('artist.id'), primary_key=True),
    db.Column('work_id', db.Integer, db.ForeignKey('work.id'), primary_key=True)
)
artist_series = db.Table('artist_series',
    db.Column('artist_id', db.Integer, db.ForeignKey('artist.id'), primary_key=True),
    db.Column('series_id', db.Integer, db.ForeignKey('series.id'), primary_key=True)
)
series_work = db.Table('series_work',
    db.Column('series_id', db.Integer, db.ForeignKey('series.id'), primary_key=True),
    db.Column('work_id', db.Integer, db.ForeignKey('work.id'), primary_key=True)
)
tags = db.Table('tags',
    db.Column('tag', db.String(20), db.ForeignKey('tag.tag'), primary_key=True),
    db.Column('work_id', db.Integer, db.ForeignKey('work.id'), primary_key=True)
)

# models
class Artist(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    name = db.Column(db.String(120))
    profile_picture = db.Column(ProfilePicture.as_mutable(Json))
    bio = db.Column(db.Text)

    works = db.relationship('Work', secondary=artist_work, lazy='subquery',
        backref=db.backref('artist', lazy=True))
    series = db.relationship('Series', secondary=artist_series, lazy='subquery',
        backref=db.backref('artist', lazy=True))

    def __repr__(self):
        return f"Artist('{self.username}', '{self.email}', '{self.name}')"

class Work(db.Model):
    id = db.Column(db.Integer, primary_key=True)

    media = db.relationship('Media', backref='work', lazy=True)

    title = db.Column(db.String(120), nullable=False, default='Untitled')
    date = db.Column(db.DateTime)
    date_added = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    description = db.Column(db.Text)

    # tags
    tags = db.relationship('Tag', secondary=tags, lazy='subquery',
        backref=db.backref('work', lazy=True))
    # license
    license_id = db.Column(db.String(20), db.ForeignKey('license.id'))

    def __repr__(self):
        return f"Work('{self.title}', '{self.image_file}', '{self.alt_text}')"

# Media table, as each work can have multiple images.
class Media(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    media = db.Column(MediaFile.as_mutable(Json), nullable=False)
    alt_text = db.Column(db.Text)
    work_id = db.Column(db.Integer, db.ForeignKey('work.id'), nullable=False)

class Series(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(120), nullable=False, default='Untitled')
    date = db.Column(db.DateTime)
    date_added = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
    location = db.Column(db.String(120))
    description = db.Column(db.Text)

    # tree self-relationship
    children = db.relationship('Series', backref='parent', lazy=True, remote_side=[id])
    parent_id = db.Column(db.Integer, db.ForeignKey('series.id'))

    # many-to-many relationships
    # works
    works = db.relationship('Work', secondary=series_work, lazy='subquery',
        backref=db.backref('series', lazy=True))


class Tag(db.Model):
    tag = db.Column(db.String(20), primary_key=True)

class License(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(20), nullable=False)
    text = db.Column(db.Text)
    works = db.relationship('Work', backref='license', lazy=True)