1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
import datetime |
3
|
|
|
import random |
4
|
|
|
import string |
5
|
|
|
from urllib.parse import urljoin |
6
|
|
|
from urllib.parse import urlencode |
7
|
|
|
|
8
|
|
|
from colour import Color |
9
|
|
|
import pytz |
10
|
|
|
from redis import Redis |
11
|
|
|
from rq import Queue |
12
|
|
|
import typing |
13
|
|
|
|
14
|
|
|
if typing.TYPE_CHECKING: |
15
|
|
|
from tracim_backend.config import CFG |
16
|
|
|
|
17
|
|
|
DATETIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ' |
18
|
|
|
DEFAULT_WEBDAV_CONFIG_FILE = "wsgidav.conf" |
19
|
|
|
DEFAULT_TRACIM_CONFIG_FILE = "development.ini" |
20
|
|
|
CONTENT_FRONTEND_URL_SCHEMA = 'workspaces/{workspace_id}/contents/{content_type}/{content_id}' # nopep8 |
21
|
|
|
WORKSPACE_FRONTEND_URL_SCHEMA = 'workspaces/{workspace_id}' # nopep8 |
22
|
|
|
FRONTEND_UI_SUBPATH = 'ui' |
23
|
|
|
LOGIN_SUBPATH = 'login' |
24
|
|
|
RESET_PASSWORD_SUBPATH = 'reset-password' |
25
|
|
|
|
26
|
|
|
def generate_documentation_swagger_tag(*sections: str) -> str: |
27
|
|
|
""" |
28
|
|
|
Generate documentation swagger tag according to section in order |
29
|
|
|
""" |
30
|
|
|
return ' > '.join(sections) |
31
|
|
|
|
32
|
|
|
|
33
|
|
|
def preview_manager_page_format(page_number: int) -> int: |
34
|
|
|
""" |
35
|
|
|
Convert page real number of page(begin at 1) to preview_manager page |
36
|
|
|
format(begin at 0) |
37
|
|
|
""" |
38
|
|
|
return page_number-1 |
39
|
|
|
|
40
|
|
|
|
41
|
|
|
def get_root_frontend_url(config: 'CFG') -> str: |
42
|
|
|
""" |
43
|
|
|
Return website base url with always '/' at the end |
44
|
|
|
""" |
45
|
|
|
return as_url_folder(config.WEBSITE_BASE_URL) |
46
|
|
|
|
47
|
|
|
|
48
|
|
|
def get_frontend_ui_base_url(config: 'CFG') -> str: |
49
|
|
|
""" |
50
|
|
|
Return ui base url |
51
|
|
|
""" |
52
|
|
|
return as_url_folder( |
53
|
|
|
urljoin(get_root_frontend_url(config), FRONTEND_UI_SUBPATH) |
54
|
|
|
) |
55
|
|
|
|
56
|
|
|
|
57
|
|
|
def get_login_frontend_url(config: 'CFG'): |
58
|
|
|
""" |
59
|
|
|
Return login page url |
60
|
|
|
""" |
61
|
|
|
return urljoin(get_frontend_ui_base_url(config), LOGIN_SUBPATH) |
62
|
|
|
|
63
|
|
|
|
64
|
|
|
def get_reset_password_frontend_url( |
65
|
|
|
config: 'CFG', |
66
|
|
|
token: str, |
67
|
|
|
email: str, |
68
|
|
|
) -> str: |
69
|
|
|
""" |
70
|
|
|
Return reset password url |
71
|
|
|
""" |
72
|
|
|
path = urljoin(get_frontend_ui_base_url(config), RESET_PASSWORD_SUBPATH) |
73
|
|
|
params = { |
74
|
|
|
'token': token, |
75
|
|
|
'email': email, |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
return '{path}?{params}'.format(path=path, params=urlencode(params)) |
79
|
|
|
|
80
|
|
|
|
81
|
|
|
def as_url_folder(url: str) -> str: |
82
|
|
|
if url[-1] != '/': |
83
|
|
|
url = '{}/'.format(url) |
84
|
|
|
return url |
85
|
|
|
|
86
|
|
|
|
87
|
|
|
def get_email_logo_frontend_url(config: 'CFG'): |
88
|
|
|
# TODO - G.M - 11-06-2018 - [emailTemplateURL] correct value for email_logo_frontend_url # nopep8 |
89
|
|
|
return 'data:image/png;base64,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' # nopep8' |
90
|
|
|
|
91
|
|
|
|
92
|
|
|
def get_redis_connection(config: 'CFG') -> Redis: |
93
|
|
|
""" |
94
|
|
|
:param config: current app_config |
95
|
|
|
:return: redis connection |
96
|
|
|
""" |
97
|
|
|
return Redis( |
98
|
|
|
host=config.EMAIL_SENDER_REDIS_HOST, |
99
|
|
|
port=config.EMAIL_SENDER_REDIS_PORT, |
100
|
|
|
db=config.EMAIL_SENDER_REDIS_DB, |
101
|
|
|
) |
102
|
|
|
|
103
|
|
|
|
104
|
|
|
def get_rq_queue(redis_connection: Redis, queue_name: str ='default') -> Queue: |
105
|
|
|
""" |
106
|
|
|
:param queue_name: name of queue |
107
|
|
|
:return: wanted queue |
108
|
|
|
""" |
109
|
|
|
|
110
|
|
|
return Queue(name=queue_name, connection=redis_connection) |
111
|
|
|
|
112
|
|
|
|
113
|
|
|
def cmp_to_key(mycmp): |
114
|
|
|
""" |
115
|
|
|
List sort related function |
116
|
|
|
|
117
|
|
|
Convert a cmp= function into a key= function |
118
|
|
|
""" |
119
|
|
|
class K(object): |
120
|
|
|
def __init__(self, obj, *args): |
121
|
|
|
self.obj = obj |
122
|
|
|
|
123
|
|
|
def __lt__(self, other): |
124
|
|
|
return mycmp(self.obj, other.obj) < 0 |
125
|
|
|
|
126
|
|
|
def __gt__(self, other): |
127
|
|
|
return mycmp(self.obj, other.obj) > 0 |
128
|
|
|
|
129
|
|
|
def __eq__(self, other): |
130
|
|
|
return mycmp(self.obj, other.obj) == 0 |
131
|
|
|
|
132
|
|
|
def __le__(self, other): |
133
|
|
|
return mycmp(self.obj, other.obj) <= 0 |
134
|
|
|
|
135
|
|
|
def __ge__(self, other): |
136
|
|
|
return mycmp(self.obj, other.obj) >= 0 |
137
|
|
|
|
138
|
|
|
def __ne__(self, other): |
139
|
|
|
return mycmp(self.obj, other.obj) != 0 |
140
|
|
|
|
141
|
|
|
return K |
142
|
|
|
|
143
|
|
|
|
144
|
|
|
def current_date_for_filename() -> str: |
145
|
|
|
""" |
146
|
|
|
ISO8601 current date, adapted to be used in filename (for |
147
|
|
|
webdav feature for example), with trouble-free characters. |
148
|
|
|
:return: current date as string like "2018-03-19T15.49.27.246592" |
149
|
|
|
""" |
150
|
|
|
# INFO - G.M - 19-03-2018 - As ':' is in transform_to_bdd method in |
151
|
|
|
# webdav utils, it may cause trouble. So, it should be replaced to |
152
|
|
|
# a character which will not change in bdd. |
153
|
|
|
return datetime.datetime.now().isoformat().replace(':', '.') |
154
|
|
|
|
155
|
|
|
|
156
|
|
|
class Timezone(object): |
157
|
|
|
def __init__(self, name): |
158
|
|
|
self.name = name |
159
|
|
|
|
160
|
|
|
|
161
|
|
|
def get_timezones_list() -> typing.List[Timezone]: |
162
|
|
|
tz_list = [] |
163
|
|
|
for tz_name in pytz.common_timezones: |
164
|
|
|
tz_list.append(Timezone(tz_name)) |
165
|
|
|
return tz_list |
166
|
|
|
|
167
|
|
|
# INFO - G.M - 2018-08-02 - Simple password generator, inspired by |
168
|
|
|
# https://gist.github.com/23maverick23/4131896 |
169
|
|
|
|
170
|
|
|
|
171
|
|
|
ALLOWED_AUTOGEN_PASSWORD_CHAR = string.ascii_letters + \ |
172
|
|
|
string.digits + \ |
173
|
|
|
string.punctuation |
174
|
|
|
|
175
|
|
|
DEFAULT_PASSWORD_GEN_CHAR_LENGTH = 12 |
176
|
|
|
|
177
|
|
|
|
178
|
|
|
def password_generator( |
179
|
|
|
length: int=DEFAULT_PASSWORD_GEN_CHAR_LENGTH, |
180
|
|
|
chars: str=ALLOWED_AUTOGEN_PASSWORD_CHAR |
181
|
|
|
) -> str: |
182
|
|
|
""" |
183
|
|
|
:param length: length of the new password |
184
|
|
|
:param chars: characters allowed |
185
|
|
|
:return: password as string |
186
|
|
|
""" |
187
|
|
|
return ''.join(random.choice(chars) for char_number in range(length)) |
188
|
|
|
|
189
|
|
|
|
190
|
|
|
COLOR_DARKEN_SCALE_FACTOR = 0.85 |
191
|
|
|
COLOR_LIGHTEN_SCALE_FACTOR = 1.15 |
192
|
|
|
|
193
|
|
|
|
194
|
|
|
def clamp(val: float, minimum: float = 0.0, maximum: float=1.0) -> float: |
195
|
|
|
""" Fix value between min an max""" |
196
|
|
|
if val < minimum: |
197
|
|
|
return minimum |
198
|
|
|
if val > maximum: |
199
|
|
|
return maximum |
200
|
|
|
return val |
201
|
|
|
|
202
|
|
|
|
203
|
|
|
class ExtendedColor(Color): |
204
|
|
|
|
205
|
|
|
@property |
206
|
|
|
def darken(self): |
207
|
|
|
new_color = ExtendedColor(self) |
208
|
|
|
new_color.luminance = clamp(COLOR_DARKEN_SCALE_FACTOR*self.luminance) |
209
|
|
|
return new_color |
210
|
|
|
|
211
|
|
|
@property |
212
|
|
|
def lighten(self): |
213
|
|
|
new_color = ExtendedColor(self) |
214
|
|
|
new_color.luminance = clamp(COLOR_LIGHTEN_SCALE_FACTOR*self.luminance) |
215
|
|
|
return new_color |
216
|
|
|
|