File size: 15,630 Bytes
2609fac
d35b11d
fe99633
2609fac
 
 
 
fe99633
 
2609fac
fe99633
 
 
2609fac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94004b3
e5cd1a4
 
2609fac
 
 
 
 
 
 
 
 
 
de297e0
2609fac
 
 
 
 
 
 
 
 
 
 
 
 
 
fe99633
2609fac
 
 
41e198b
fe99633
2609fac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fe99633
2609fac
 
 
 
d35b11d
3eb9275
2609fac
 
 
 
 
 
 
 
 
 
fe99633
2609fac
 
 
 
 
 
 
 
 
 
 
 
fe99633
2609fac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41e198b
2609fac
 
 
 
 
8af9f02
fe99633
8af9f02
fe99633
8af9f02
 
2609fac
 
 
 
 
 
 
 
 
 
fe99633
2609fac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
de297e0
2609fac
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
import streamlit as st
from datetime import datetime
from supabase import create_client, Client
from utilities_option_menu.option_menu import option_menu
import utilities_database.user_database_utils as db_utils
from utilities_cookies.encrypted_cookie_manager import EncryptedCookieManager

DB_URL = st.secrets['SUPABASE_URL']
DB_KEY = st.secrets['SUPABASE_KEY']

Client = create_client(DB_URL, DB_KEY)
user_login_table = Client.table('UserLogIn')
user_save_text_table = Client.table('TaskData')
st.set_page_config(page_title='GenLexTasksEnter', layout="wide", page_icon=':es:')

login_call = 'Зарегистрироваться'


class LogIn:
    """
    Builds the UI for the Login/ Sign Up page.
    """

    def __init__(self,
                 auth_token: str,
                 company_name: str,
                 width, height,
                 logout_button_name: str = 'Logout',
                 hide_menu_bool: bool = False,
                 hide_footer_bool: bool = False,
                 lottie_url: str = "https://assets8.lottiefiles.com/packages/lf20_ktwnwv5m.json"):
        """
        Arguments:
        -----------
        1. self
        2. auth_token : The unique authorization token received from - https://www.courier.com/email-api/
        3. company_name : This is the name of the person/ organization which will send the password reset email.
        4. width : Width of the animation on the login page.
        5. height : Height of the animation on the login page.
        6. logout_button_name : The logout button name.
        7. hide_menu_bool : Pass True if the streamlit menu should be hidden.
        8. hide_footer_bool : Pass True if the 'made with streamlit' footer should be hidden.
        9. lottie_url : The lottie animation you would like to use on the login page.
            Explore animations at - https://lottiefiles.com/featured
        """
        self.auth_token = auth_token
        self.company_name = company_name
        self.width = width
        self.height = height
        self.logout_button_name = logout_button_name
        self.hide_menu_bool = hide_menu_bool
        self.hide_footer_bool = hide_footer_bool
        self.lottie_url = lottie_url

        self.cookies = EncryptedCookieManager(
            prefix="streamlit_login_ui_yummy_cookies",
            password='9d68d6f2-4258-45c9-96eb-2d6bc74ddbb5-d8f49cab-edbb-404a-94d0-b25b1d4a564b')

        if not self.cookies.ready():
            st.stop()

    def get_user_name(self):
        if not st.session_state['LOGOUT_BUTTON_HIT']:
            fetched_cookies = self.cookies
            if '__streamlit_login_signup_ui_username__' in fetched_cookies.keys():
                user_name = fetched_cookies['__streamlit_login_signup_ui_username__']
                return user_name

    def login_widget(self) -> None:
        """
        Creates the login widget, checks and sets cookies, authenticates the users.
        """

        # Checks if cookie exists.
        if not st.session_state['LOGGED_IN'] and not st.session_state['LOGOUT_BUTTON_HIT']:
            fetched_cookies = self.cookies
            if '__streamlit_login_signup_ui_username__' in fetched_cookies.keys():
                if fetched_cookies['__streamlit_login_signup_ui_username__'] \
                        != '1c9a923f-fb21-4a91-b3f3-5f18e3f01182':
                    st.session_state['LOGGED_IN'] = True

        if not st.session_state['LOGGED_IN']:
            st.session_state['LOGOUT_BUTTON_HIT'] = False

            del_login = st.empty()
            with del_login.form("Login Form"):
                user_name = st.text_input("Имя пользователя", placeholder='Ваше имя пользователя')
                password = st.text_input("Пароль", placeholder='Ваш пароль', type='password')

                login_submit_button = st.form_submit_button(label='Войти')

                if login_submit_button:
                    authenticate_user_check = db_utils.check_usr_pass(user_log_in_database=user_login_table,
                                                                      user_name=user_name,
                                                                      password=password)

                    if not authenticate_user_check:
                        st.error("Неверное имя пользователя или пароль!")

                    else:
                        st.session_state['LOGGED_IN'] = True
                        st.session_state['-USER_NAME-'] = user_name
                        self.cookies['__streamlit_login_signup_ui_username__'] = user_name
                        self.cookies.save()
                        del_login.empty()
                        st.rerun()

    @staticmethod
    def sign_up_widget() -> None:
        """
        Creates the sign-up widget and stores the user info in a secure way in the _secret_auth_.json file.
        """
        with st.form("Sign Up Form"):
            name_sign_up = st.text_input("Имя *",
                                         placeholder='Введите Ваше имя')
            valid_name_check = db_utils.check_valid_name(name_sign_up)

            email_sign_up = st.text_input("E-mail *",
                                          placeholder='Введите Ваш e-mail')
            valid_email_check = db_utils.check_valid_email(email_sign_up)
            unique_email_check = db_utils.check_unique_email(user_log_in_database=user_login_table,
                                                             email_sign_up=email_sign_up)

            user_name_sign_up = st.text_input("Имя пользователя *",
                                              placeholder='Введите имя пользователя (латинские буквы и символы)')
            unique_user_name_check = db_utils.check_unique_usr(user_log_in_database=user_login_table,
                                                               user_name_sign_up=user_name_sign_up)

            password_sign_up = st.text_input("Пароль *",
                                             placeholder='Введите пароль',
                                             type='password')
            professional_level = st.radio('Вы являетесь преподавателем испанского языка? *',
                                          options=['Да', 'Нет'],
                                          index=1,
                                          horizontal=True)

            st.markdown("\* Обязательное поле")
            sign_up_submit_button = st.form_submit_button(label=login_call)

            if sign_up_submit_button:
                if not valid_name_check:
                    st.error("Пожалуйста, ведите Ваше имя!")

                elif not valid_email_check:
                    st.error("Пожалуйста, введите действующий е-mail!")

                elif not unique_email_check:
                    st.error("Пользователь с этим e-mail уже зарегистрирован!")

                elif not unique_user_name_check:
                    st.error(f'Извините, пользователь с таким именем ({user_name_sign_up}) уже существует!')

                elif unique_user_name_check is None:
                    st.error('Пожалуйста, введите имя пользователя!')

                if valid_name_check:
                    if valid_email_check and unique_email_check and unique_user_name_check:
                        db_utils.register_new_usr(user_log_in_database=user_login_table,
                                                  name_sign_up=name_sign_up,
                                                  email_sign_up=email_sign_up,
                                                  user_name_sign_up=user_name_sign_up,
                                                  password_sign_up=password_sign_up,
                                                  professional_level=professional_level,
                                                  created_at=str(datetime.now())[:-7])
                        st.success("Регистрация прошла успешно!")

    def forgot_password(self) -> None:
        """
        Creates the forgot password widget and after user authentication (e-mail), triggers an e-mail to the user
        containing a random password.
        """
        with st.form("Forgot Password Form"):
            email_forgot_passwd = st.text_input("Email", placeholder='Введите Ваш email')
            email_exists_check, user_name_forgot_passwd = db_utils.check_email_exists(
                user_log_in_database=user_login_table,
                email_forgot_passwd=email_forgot_passwd)

            forgot_passwd_submit_button = st.form_submit_button(label='Получить пароль')

            if forgot_passwd_submit_button:
                if not email_exists_check:
                    st.error("Пользователя с таким e-mail не существует!")

                if email_exists_check:
                    random_password = db_utils.generate_random_passwd()
                    db_utils.send_passwd_in_email(self.auth_token, user_name_forgot_passwd, email_forgot_passwd,
                                                  self.company_name, random_password)
                    db_utils.change_passwd(user_log_in_database=user_login_table,
                                           email_forgot_passwd=email_forgot_passwd,
                                           random_password=random_password)
                    st.success("Временный пароль выслан Вам на почту!")

    @staticmethod
    def reset_password() -> None:
        """
        Creates the reset password widget and after user authentication
        (e-mail and the password shared over that e-mail),
        resets the password and updates the same in the _secret_auth_.json file.
        """
        with st.form("Reset Password Form"):
            email_reset_passwd = st.text_input("Email", placeholder='Please enter your email')

            current_passwd = st.text_input("Временный пароль",
                                           placeholder='Введите пароль, который вы получили в письме')

            new_passwd = st.text_input("Новый пароль", placeholder='Введите новый пароль',
                                       type='password')

            new_passwd_1 = st.text_input("Повторите новый пароль", placeholder='Повторите пароль',
                                         type='password')

            reset_passwd_submit_button = st.form_submit_button(label='Изменить пароль')

            if reset_passwd_submit_button:
                email_exists_check, user_name_reset_passwd = db_utils.check_email_exists(
                    user_log_in_database=user_login_table,
                    email_forgot_passwd=email_reset_passwd)
                current_passwd_check = db_utils.check_current_passwd(user_log_in_database=user_login_table,
                                                                     email_reset_passwd=email_reset_passwd,
                                                                     current_passwd=current_passwd)
                if not email_exists_check:
                    st.error("Пользователя с таким e-mail не существует!")

                elif not current_passwd_check:
                    st.error("Неверный временный пароль!")

                elif new_passwd != new_passwd_1:
                    st.error("Пароли не совпадают!")

                if email_exists_check and current_passwd_check:
                    db_utils.change_passwd(user_log_in_database=user_login_table,
                                           email_forgot_passwd=email_reset_passwd,
                                           random_password=new_passwd)
                    st.success("Пароль успешно изменен!")

    def logout_widget(self) -> None:
        """
        Creates the logout widget in the sidebar only if the user is logged in.
        """
        if st.session_state['LOGGED_IN']:
            del_logout = st.sidebar.empty()
            del_logout.markdown("#")
            logout_click_check = del_logout.button(self.logout_button_name)

            if logout_click_check:
                st.session_state['LOGOUT_BUTTON_HIT'] = True
                st.session_state['LOGGED_IN'] = False
                self.cookies['__streamlit_login_signup_ui_username__'] = '1c9a923f-fb21-4a91-b3f3-5f18e3f01182'
                del_logout.empty()
                st.rerun()

    @staticmethod
    def navigation():
        """
        Creates the side navigation bar
        """
        selected_option = option_menu(
            menu_title='Навигация',
            menu_icon='list-columns-reverse',
            icons=['box-arrow-in-right', 'person-plus', 'x-circle', 'arrow-counterclockwise'],
            options=['Вход', login_call, 'Забыли пароль?', 'Восстановление пароля'],
            default_index=0,
            styles={
                "container": {"padding": "10px", "text-align": "left"},
                "nav-link": {"font-size": "16px", "text-align": "left", "margin": "0px"}})
        return selected_option

    @staticmethod
    def hide_menu() -> None:
        """
        Hides the streamlit menu situated in the top right.
        """
        st.markdown(""" <style>
        #MainMenu {visibility: hidden;}
        </style> """, unsafe_allow_html=True)

    @staticmethod
    def hide_header() -> None:
        """
        Hides the 'made with streamlit' footer.
        """
        st.markdown(""" <style>
        header {visibility: hidden;}
        </style> """, unsafe_allow_html=True)

    @staticmethod
    def hide_footer() -> None:
        """
        Hides the 'made with streamlit' footer.
        """
        st.markdown(""" <style>
        footer {visibility: hidden;}
        </style> """, unsafe_allow_html=True)

    def build_login_ui(self):
        """
        Brings everything together, calls important functions.
        """
        if 'LOGGED_IN' not in st.session_state:
            st.session_state['LOGGED_IN'] = False

        if 'LOGOUT_BUTTON_HIT' not in st.session_state:
            st.session_state['LOGOUT_BUTTON_HIT'] = False

        selected_option = self.navigation()

        if selected_option == 'Вход':
            c1, c2 = st.columns([7, 3])
            with c1:
                self.login_widget()
            with c2:
                if not st.session_state['LOGGED_IN']:
                    pass
                    # self.animation()

        if selected_option == login_call:
            self.sign_up_widget()

        if selected_option == 'Забыли пароль?':
            self.forgot_password()

        if selected_option == 'Восстановление пароля':
            self.reset_password()

        self.logout_widget()

        if st.session_state['LOGGED_IN']:
            pass

        if self.hide_menu_bool:
            self.hide_menu()

        if self.hide_footer_bool:
            self.hide_footer()

        return st.session_state['LOGGED_IN']