~homeworkprod/byceps

ref: 4237b3ec9496efe95dcce82bea3207ab9de4d520 byceps/byceps/services/snippet/models/snippet.py -rw-r--r-- 4.7 KiB
4237b3ec — Jochen Kupperschmidt Move ticketing blueprint into `site` subpackage 2 years ago
                                                                                
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
"""
byceps.services.snippet.models.snippet
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Snippets of database-stored content. Can contain HTML and template
engine syntax. Can be embedded in other templates or mounted as full
page.

:Copyright: 2006-2020 Jochen Kupperschmidt
:License: Modified BSD, see LICENSE for details.
"""

from datetime import datetime
from typing import Optional

from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.hybrid import hybrid_property

from ....database import BaseQuery, db, generate_uuid
from ....typing import UserID
from ....util.instances import ReprBuilder

from ...user.models.user import User

from ..transfer.models import Scope, SnippetType


class Snippet(db.Model):
    """A snippet.

    Each snippet is expected to have at least one version (the initial
    one).
    """

    __tablename__ = 'snippets'
    __table_args__ = (
        db.Index('ix_snippets_scope', 'scope_type', 'scope_name'),
        db.UniqueConstraint('scope_type', 'scope_name', 'name'),
    )

    id = db.Column(db.Uuid, default=generate_uuid, primary_key=True)
    scope_type = db.Column(db.UnicodeText, nullable=False)
    scope_name = db.Column(db.UnicodeText, nullable=False)
    name = db.Column(db.UnicodeText, index=True, nullable=False)
    _type = db.Column('type', db.UnicodeText, nullable=False)
    current_version = association_proxy('current_version_association', 'version')

    def __init__(self, scope: Scope, name: str, type_: SnippetType) -> None:
        self.scope_type = scope.type_
        self.scope_name = scope.name
        self.name = name
        self.type_ = type_

    @property
    def scope(self) -> Scope:
        return Scope(self.scope_type, self.scope_name)

    @hybrid_property
    def type_(self) -> SnippetType:
        return SnippetType[self._type]

    @type_.setter
    def type_(self, type_: SnippetType) -> None:
        assert type_ is not None
        self._type = type_.name

    @property
    def is_document(self) -> bool:
        return self.type_ == SnippetType.document

    @property
    def is_fragment(self) -> bool:
        return self.type_ == SnippetType.fragment

    def __repr__(self) -> str:
        return ReprBuilder(self) \
            .add_with_lookup('id') \
            .add_with_lookup('scope_type') \
            .add_with_lookup('scope_name') \
            .add_with_lookup('name') \
            .add('type', self._type) \
            .build()


class SnippetVersionQuery(BaseQuery):

    def latest_first(self) -> BaseQuery:
        return self.order_by(SnippetVersion.created_at.desc())


class SnippetVersion(db.Model):
    """A snapshot of a snippet at a certain time."""

    __tablename__ = 'snippet_versions'
    query_class = SnippetVersionQuery

    id = db.Column(db.Uuid, default=generate_uuid, primary_key=True)
    snippet_id = db.Column(db.Uuid, db.ForeignKey('snippets.id'), index=True, nullable=False)
    snippet = db.relationship(Snippet)
    created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
    creator_id = db.Column(db.Uuid, db.ForeignKey('users.id'), nullable=False)
    creator = db.relationship(User)
    title = db.Column(db.UnicodeText, nullable=True)
    head = db.Column(db.UnicodeText, nullable=True)
    body = db.Column(db.UnicodeText, nullable=False)
    image_url_path = db.Column(db.UnicodeText, nullable=True)

    def __init__(
        self,
        snippet: Snippet,
        creator_id: UserID,
        title: Optional[str],
        head: Optional[str],
        body: str,
        image_url_path: Optional[str],
    ) -> None:
        self.snippet = snippet
        self.creator_id = creator_id
        self.title = title
        self.head = head
        self.body = body
        self.image_url_path = image_url_path

    @property
    def is_current(self) -> bool:
        """Return `True` if this version is the current version of the
        snippet it belongs to.
        """
        return self.id == self.snippet.current_version.id

    def __repr__(self) -> str:
        return ReprBuilder(self) \
            .add_with_lookup('id') \
            .add_with_lookup('snippet') \
            .add_with_lookup('created_at') \
            .build()


class CurrentVersionAssociation(db.Model):
    __tablename__ = 'snippet_current_versions'

    snippet_id = db.Column(db.Uuid, db.ForeignKey('snippets.id'), primary_key=True)
    snippet = db.relationship(Snippet, backref=db.backref('current_version_association', uselist=False))
    version_id = db.Column(db.Uuid, db.ForeignKey('snippet_versions.id'), unique=True, nullable=False)
    version = db.relationship(SnippetVersion)

    def __init__(self, snippet: Snippet, version: SnippetVersion) -> None:
        self.snippet = snippet
        self.version = version