1
- from app import db
1
+ from app import app , db
2
2
from app .model_types import GUID
3
3
from sqlalchemy .sql import func
4
+ from sqlalchemy .ext .hybrid import hybrid_property , hybrid_method
4
5
from sqlalchemy import event
6
+ from Crypto .Cipher import AES
7
+ import binascii
5
8
import uuid
6
9
import frontmatter
7
10
import re
8
11
9
12
13
+ key = app .config ['DB_ENCRYPTION_KEY' ]
14
+
15
+
16
+ def aes_encrypt (data ):
17
+ cipher = AES .new (key )
18
+ data = data + (" " * (16 - (len (data ) % 16 )))
19
+ return binascii .hexlify (cipher .encrypt (data ))
20
+
21
+ def aes_decrypt (data ):
22
+ try :
23
+ cipher = AES .new (key )
24
+ return cipher .decrypt (binascii .unhexlify (data )).rstrip ()
25
+ except :
26
+ # If data is not encrypted, just return it
27
+ return data
28
+
29
+
10
30
class User (db .Model ):
11
31
uuid = db .Column (GUID , primary_key = True , index = True , unique = True , default = lambda : uuid .uuid4 ())
12
32
username = db .Column (db .String (64 ), unique = True , nullable = False )
13
33
password_hash = db .Column (db .String (128 ), nullable = False )
14
- notes = db .relationship ('Note' , lazy = 'dynamic' )
34
+ notes = db .relationship ('Note' , lazy = 'dynamic' , cascade = 'all, delete, delete-orphan' )
35
+ tags = db .relationship ('Tag' , lazy = 'dynamic' , cascade = 'all, delete, delete-orphan' )
36
+ projects = db .relationship ('Project' , lazy = 'dynamic' , cascade = 'all, delete, delete-orphan' )
37
+ tasks = db .relationship ('Task' , lazy = 'dynamic' , cascade = 'all, delete, delete-orphan' )
15
38
16
39
def __repr__ (self ):
17
40
return '<User {}>' .format (self .uuid )
18
41
19
42
43
+ class Tag (db .Model ):
44
+ uuid = db .Column (GUID , primary_key = True , index = True , unique = True , default = lambda : uuid .uuid4 ())
45
+ user_id = db .Column (GUID , db .ForeignKey ('user.uuid' ), nullable = False )
46
+ note_id = db .Column (GUID , db .ForeignKey ('note.uuid' ), nullable = False )
47
+ name = db .Column (db .String )
48
+
49
+ def __repr__ (self ):
50
+ return '<Tag {}>' .format (self .uuid )
51
+
52
+
53
+ class Project (db .Model ):
54
+ uuid = db .Column (GUID , primary_key = True , index = True , unique = True , default = lambda : uuid .uuid4 ())
55
+ user_id = db .Column (GUID , db .ForeignKey ('user.uuid' ), nullable = False )
56
+ note_id = db .Column (GUID , db .ForeignKey ('note.uuid' ), nullable = False )
57
+ name = db .Column (db .String )
58
+
59
+ def __repr__ (self ):
60
+ return '<Project {}>' .format (self .uuid )
61
+
62
+
63
+ class Task (db .Model ):
64
+ uuid = db .Column (GUID , primary_key = True , index = True , unique = True , default = lambda : uuid .uuid4 ())
65
+ user_id = db .Column (GUID , db .ForeignKey ('user.uuid' ), nullable = False )
66
+ note_id = db .Column (GUID , db .ForeignKey ('note.uuid' ), nullable = False )
67
+ name = db .Column (db .String )
68
+ swimlane = db .Column (db .String )
69
+
70
+ def __repr__ (self ):
71
+ return '<Task {}>' .format (self .uuid )
72
+
73
+
20
74
class Note (db .Model ):
21
75
uuid = db .Column (GUID , primary_key = True , index = True , unique = True , default = lambda : uuid .uuid4 ())
22
- tags = db .Column (db .String )
23
- projects = db .Column (db .String )
24
76
user_id = db .Column (GUID , db .ForeignKey ('user.uuid' ), nullable = False )
25
77
data = db .Column (db .String )
26
78
title = db .Column (db .String (128 ), nullable = False , unique = True )
27
79
date = db .Column (db .DateTime (timezone = True ), server_default = func .now ())
28
80
is_date = db .Column (db .Boolean , default = False )
81
+ tags = db .relationship ('Tag' , lazy = 'dynamic' , cascade = 'all, delete, delete-orphan' )
82
+ projects = db .relationship ('Project' , lazy = 'dynamic' , cascade = 'all, delete, delete-orphan' )
83
+ tasks = db .relationship ('Task' , lazy = 'dynamic' , cascade = 'all, delete, delete-orphan' )
84
+
85
+ @hybrid_property
86
+ def text (self ):
87
+ return aes_decrypt (self .data )
88
+
89
+ @text .setter
90
+ def text (self , value ):
91
+ self .data = aes_encrypt (value )
29
92
30
93
def __repr__ (self ):
31
94
return '<Note {}>' .format (self .uuid )
@@ -53,32 +116,81 @@ def serialize_full(self):
53
116
}
54
117
55
118
119
+ # Update title automatically
56
120
def before_change_note (mapper , connection , target ):
57
- tags = ''
58
- projects = ''
59
121
title = None
60
122
61
- data = frontmatter .loads (target .data )
62
-
63
- if isinstance (data .get ('tags' ), list ):
64
- tags = ',' .join (set ([x .replace (',' , '\,' ) for x in data .get ('tags' )]))
65
- elif isinstance (data .get ('tags' ), str ):
66
- tags = ',' .join (set (map (str .strip , data ['tags' ].split (',' ))))
67
-
68
- if isinstance (data .get ('projects' ), list ):
69
- projects = ',' .join (set ([x .replace (',' , '\,' ) for x in data .get ('projects' )]))
70
- elif isinstance (data .get ('projects' ), str ):
71
- projects = ',' .join (set (map (str .strip , data ['projects' ].split (',' ))))
123
+ data = frontmatter .loads (target .text )
72
124
73
125
if isinstance (data .get ('title' ), str ) and len (data .get ('title' )) > 0 :
74
126
title = data .get ('title' )
75
-
76
- target .tags = tags
77
- target .projects = projects
78
127
79
128
if title and not target .is_date :
80
129
target .title = title
81
130
82
131
132
+ # Handle changes to tasks, projects, and tags
133
+ def after_change_note (mapper , connection , target ):
134
+ tags = []
135
+ projects = []
136
+ # tasks = []
137
+
138
+ data = frontmatter .loads (target .text )
139
+
140
+ if isinstance (data .get ('tags' ), list ):
141
+ tags = list (set ([x .replace (',' , '\,' ) for x in data .get ('tags' )]))
142
+ elif isinstance (data .get ('tags' ), str ):
143
+ tags = list (set (map (str .strip , data ['tags' ].split (',' ))))
144
+
145
+ if isinstance (data .get ('projects' ), list ):
146
+ projects = list (set ([x .replace (',' , '\,' ) for x in data .get ('projects' )]))
147
+ elif isinstance (data .get ('projects' ), str ):
148
+ projects = list (set (map (str .strip , data ['projects' ].split (',' ))))
149
+
150
+ # Parse out tasks here #
151
+
152
+ existing_tags = Tag .query .filter_by (note_id = target .uuid ).all ()
153
+ existing_projects = Project .query .filter_by (note_id = target .uuid ).all ()
154
+ # existing_tasks = Task.query.filter_by(note_id=target.uuid).all()
155
+
156
+ for tag in existing_tags :
157
+ if tag .name not in tags :
158
+ connection .execute (
159
+ 'DELETE FROM tag WHERE uuid = (?)' ,
160
+ '{}' .format (tag .uuid ).replace ('-' , '' )
161
+ )
162
+ else :
163
+ tags .remove (tag .name )
164
+
165
+ for tag in tags :
166
+ connection .execute (
167
+ 'INSERT INTO tag (uuid, user_id, note_id, name) VALUES (?, ?, ?, ?)' ,
168
+ '{}' .format (uuid .uuid4 ()).replace ('-' , '' ),
169
+ '{}' .format (target .user_id ).replace ('-' , '' ),
170
+ '{}' .format (target .uuid ).replace ('-' , '' ),
171
+ tag
172
+ )
173
+
174
+ for project in existing_projects :
175
+ if project .name not in projects :
176
+ connection .execute (
177
+ 'DELETE FROM project WHERE uuid = (?)' ,
178
+ '{}' .format (project .uuid ).replace ('-' , '' )
179
+ )
180
+ else :
181
+ projects .remove (project .name )
182
+
183
+ for project in projects :
184
+ connection .execute (
185
+ 'INSERT INTO project (uuid, user_id, note_id, name) VALUES (?, ?, ?, ?)' ,
186
+ '{}' .format (uuid .uuid4 ()).replace ('-' , '' ),
187
+ '{}' .format (target .user_id ).replace ('-' , '' ),
188
+ '{}' .format (target .uuid ).replace ('-' , '' ),
189
+ project
190
+ )
191
+
192
+
83
193
event .listen (Note , 'before_insert' , before_change_note )
84
- event .listen (Note , 'before_update' , before_change_note )
194
+ event .listen (Note , 'before_update' , before_change_note )
195
+ event .listen (Note , 'after_insert' , after_change_note )
196
+ event .listen (Note , 'after_update' , after_change_note )
0 commit comments