Skip to content

Commit 7760b89

Browse files
committed
new pandas resource
1 parent ce1a402 commit 7760b89

16 files changed

+81
-74
lines changed

content/pages/03-data/16-pandas.markdown

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,9 @@ is a data structures and analysis library.
141141
[object-relational mapper (ORM)](/object-relational-mappers-orms.html)
142142
to load the data and perform analysis in pandas.
143143

144+
* Real-world Excel spreadsheets are often a mess of unstructured data, so
145+
this tutorial on
146+
[Reading Poorly Structured Excel Files with Pandas](https://pbpython.com/pandas-excel-range.html)
147+
gives example code for extracting only part of a file as well
148+
as reading ranges and tables.
149+

content/pages/examples/flask/flask-app-immutabledict.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ so that values cannot be modified after initially being set.
2020
and <a href="/flask-app-headers-examples.html">Headers</a>
2121
are several other callables with code examples from the same `flask.app` package.
2222

23-
These topics are also useful while reading the `ImmutableDict` examples:
23+
You should read up on these subjects along with these `ImmutableDict` examples:
2424

2525
* [web development](/web-development.html) and [web design](/web-design.html)
2626
* [Flask](/flask.html) and [web framework](/web-frameworks.html) concepts

content/pages/examples/flask/flask-ctx-after-this-request.markdown

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ from .decorators import anonymous_user_required, auth_required, unauth_csrf
5353
from .forms import Form, Required, get_form_field_label
5454
from .quart_compat import get_quart_status
5555
from .signals import us_profile_changed, us_security_token_sent
56-
from .twofactor import is_tf_setup, tf_login
56+
from .twofactor import (
57+
is_tf_setup,
58+
tf_login,
59+
tf_verify_validility_token,
60+
)
5761
from .utils import (
5862
_,
5963
SmsSenderFactory,
@@ -63,10 +67,6 @@ from .utils import (
6367
do_flash,
6468
find_user,
6569
get_identity_attributes,
66-
get_post_login_redirect,
67-
get_post_verify_redirect,
68-
get_message,
69-
get_url,
7070

7171

7272
## ... source file abbreviated to get to after_this_request examples ...
@@ -127,30 +127,30 @@ def us_signin_send_code():
127127
## ... source file abbreviated to get to after_this_request examples ...
128128

129129

130-
else:
131-
return redirect(get_post_login_redirect())
132-
133-
form_class = _security.us_signin_form
134-
135-
if request.is_json:
136-
if request.content_length:
137-
form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf())
138-
else:
139-
form = form_class(formdata=None, meta=suppress_form_csrf())
140-
else:
141-
form = form_class(meta=suppress_form_csrf())
142-
form.submit.data = True
143-
144130
if form.validate_on_submit():
131+
145132
remember_me = form.remember.data if "remember" in form else None
146-
if (
147-
config_value("TWO_FACTOR")
148-
and form.authn_via in config_value("US_MFA_REQUIRED")
149-
and (config_value("TWO_FACTOR_REQUIRED") or is_tf_setup(form.user))
133+
if config_value("TWO_FACTOR") and form.authn_via in config_value(
134+
"US_MFA_REQUIRED"
150135
):
151-
return tf_login(
152-
form.user, remember=remember_me, primary_authn_via=form.authn_via
136+
if request.is_json and request.content_length:
137+
tf_validity_token = request.get_json().get("tf_validity_token", None)
138+
else:
139+
tf_validity_token = request.cookies.get("tf_validity", default=None)
140+
141+
tf_validity_token_is_valid = tf_verify_validility_token(
142+
tf_validity_token, form.user.fs_uniquifier
153143
)
144+
if config_value("TWO_FACTOR_REQUIRED") or is_tf_setup(form.user):
145+
if config_value("TWO_FACTOR_ALWAYS_VALIDATE") or (
146+
not tf_validity_token_is_valid
147+
):
148+
149+
return tf_login(
150+
form.user,
151+
remember=remember_me,
152+
primary_authn_via=form.authn_via,
153+
)
154154

155155
~~ after_this_request(_commit)
156156
login_user(form.user, remember=remember_me, authn_via=[form.authn_via])
@@ -173,10 +173,10 @@ def us_signin_send_code():
173173
return redirect(get_post_login_redirect())
174174

175175
form.passcode.data = None
176-
return _security.render_template(
177-
config_value("US_SIGNIN_TEMPLATE"),
178-
us_signin_form=form,
179-
available_methods=config_value("US_ENABLED_METHODS"),
176+
177+
if form.requires_confirmation and _security.requires_confirmation_error_view:
178+
do_flash(*get_message("CONFIRMATION_REQUIRED"))
179+
return redirect(get_url(_security.requires_confirmation_error_view))
180180

181181

182182
## ... source file abbreviated to get to after_this_request examples ...

content/pages/examples/flask/flask-globals-current-app.markdown

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,7 +1612,7 @@ logger = logging.getLogger(__name__)
16121612

16131613
def generate_csrf(secret_key=None, token_key=None):
16141614

1615-
secret_key = _get_config(
1615+
~~ secret_key = _get_config(
16161616
~~ secret_key, 'WTF_CSRF_SECRET_KEY', current_app.secret_key,
16171617
message='A secret key is required to use CSRF.'
16181618
)
@@ -1640,7 +1640,7 @@ def generate_csrf(secret_key=None, token_key=None):
16401640

16411641
def validate_csrf(data, secret_key=None, time_limit=None, token_key=None):
16421642

1643-
secret_key = _get_config(
1643+
~~ secret_key = _get_config(
16441644
~~ secret_key, 'WTF_CSRF_SECRET_KEY', current_app.secret_key,
16451645
message='A secret key is required to use CSRF.'
16461646
)
@@ -1887,6 +1887,7 @@ from .forms import (
18871887
## ... source file abbreviated to get to current_app examples ...
18881888

18891889

1890+
UnifiedSigninSetupForm,
18901891
UnifiedSigninSetupValidateForm,
18911892
UnifiedVerifyForm,
18921893
us_send_security_token,
@@ -1899,7 +1900,6 @@ from .utils import (
18991900
FsPermNeed,
19001901
csrf_cookie_handler,
19011902
default_want_json,
1902-
default_password_validator,
19031903
get_config,
19041904
get_identity_attribute,
19051905
get_identity_attributes,
@@ -1927,6 +1927,7 @@ _default_config = {
19271927
"FLASH_MESSAGES": True,
19281928
"I18N_DOMAIN": "flask_security",
19291929
"I18N_DIRNAME": pkg_resources.resource_filename("flask_security", "translations"),
1930+
"EMAIL_VALIDATOR_ARGS": None,
19301931
"PASSWORD_HASH": "bcrypt",
19311932
"PASSWORD_SALT": None,
19321933
"PASSWORD_SINGLE_HASH": {
@@ -1936,20 +1937,11 @@ _default_config = {
19361937
"django_pbkdf2_sha1",
19371938
"django_bcrypt",
19381939
"django_salted_md5",
1939-
"django_salted_sha1",
19401940

19411941

19421942
## ... source file abbreviated to get to current_app examples ...
19431943

19441944

1945-
"SEND_CONFIRMATION_TEMPLATE": "security/send_confirmation.html",
1946-
"SEND_LOGIN_TEMPLATE": "security/send_login.html",
1947-
"VERIFY_TEMPLATE": "security/verify.html",
1948-
"TWO_FACTOR_VERIFY_CODE_TEMPLATE": "security/two_factor_verify_code.html",
1949-
"TWO_FACTOR_SETUP_TEMPLATE": "security/two_factor_setup.html",
1950-
"CONFIRMABLE": False,
1951-
"REGISTERABLE": False,
1952-
"RECOVERABLE": False,
19531945
"TRACKABLE": False,
19541946
"PASSWORDLESS": False,
19551947
"CHANGEABLE": False,
@@ -1962,6 +1954,14 @@ _default_config = {
19621954
"TWO_FACTOR_AUTHENTICATOR_VALIDITY": 120,
19631955
"TWO_FACTOR_MAIL_VALIDITY": 300,
19641956
"TWO_FACTOR_SMS_VALIDITY": 120,
1957+
"TWO_FACTOR_ALWAYS_VALIDATE": True,
1958+
"TWO_FACTOR_LOGIN_VALIDITY": "30 days",
1959+
"TWO_FACTOR_VALIDITY_SALT": "tf-validity-salt",
1960+
"TWO_FACTOR_VALIDITY_COOKIE": {
1961+
"httponly": True,
1962+
"secure": False,
1963+
"samesite": None,
1964+
},
19651965
"CONFIRM_EMAIL_WITHIN": "5 days",
19661966
"RESET_PASSWORD_WITHIN": "5 days",
19671967
"LOGIN_WITHOUT_CONFIRMATION": False,
@@ -2073,6 +2073,7 @@ _default_config = {
20732073

20742074
state._phone_util = state.phone_util_cls(app)
20752075
state._mail_util = state.mail_util_cls(app)
2076+
state._password_util = state.password_util_cls(app)
20762077

20772078
app.extensions["security"] = state
20782079

@@ -2086,7 +2087,6 @@ _default_config = {
20862087

20872088
for newc, oldc in [
20882089
("SECURITY_SMS_SERVICE", "SECURITY_TWO_FACTOR_SMS_SERVICE"),
2089-
("SECURITY_SMS_SERVICE_CONFIG", "SECURITY_TWO_FACTOR_SMS_SERVICE_CONFIG"),
20902090

20912091

20922092
## ... source file continues with no further current_app examples...
@@ -2144,6 +2144,7 @@ class UserManager(UserManager__Settings, UserManager__Utils, UserManager__Views)
21442144
## ... source file abbreviated to get to current_app examples ...
21452145

21462146

2147+
@app.before_request
21472148
def advance_session_timeout():
21482149
session.permanent = True # Timeout after app.permanent_session_lifetime period
21492150
session.modified = True # Advance session timeout each time a user visits a page
@@ -2168,7 +2169,7 @@ class UserManager(UserManager__Settings, UserManager__Utils, UserManager__Views)
21682169
def call_or_get(function_or_property):
21692170
return function_or_property() if callable(function_or_property) else function_or_property
21702171

2171-
return dict(
2172+
~~ return dict(
21722173
~~ user_manager=current_app.user_manager,
21732174
call_or_get=call_or_get,
21742175
)

content/pages/examples/flask/flask-globals-g.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -919,7 +919,7 @@ from flask_login import current_user
919919
from flask_login import COOKIE_NAME as REMEMBER_COOKIE_NAME
920920
from flask_principal import AnonymousIdentity, Identity, identity_changed, Need
921921
from flask_wtf import csrf
922-
from wtforms import validators, ValidationError
922+
from wtforms import ValidationError
923923
from itsdangerous import BadSignature, SignatureExpired
924924
from werkzeug.local import LocalProxy
925925
from werkzeug.datastructures import MultiDict

content/pages/examples/flask/flask-globals-request.markdown

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,6 +2204,7 @@ from wtforms import (
22042204
ValidationError,
22052205
validators,
22062206
)
2207+
from wtforms.validators import StopValidation
22072208

22082209
from .babel import is_lazy_string, make_lazy_string
22092210
from .confirmable import requires_confirmation
@@ -2213,7 +2214,6 @@ from .utils import (
22132214
config_value,
22142215
do_flash,
22152216
find_user,
2216-
get_identity_attribute,
22172217

22182218

22192219
## ... source file abbreviated to get to request examples ...
@@ -2260,16 +2260,16 @@ class ForgotPasswordForm(Form, UserEmailFormMixin):
22602260

22612261
submit = SubmitField(get_form_field_label("recover_password"))
22622262

2263+
def __init__(self, *args, **kwargs):
2264+
super().__init__(*args, **kwargs)
2265+
self.requires_confirmation = False
2266+
22632267
def validate(self):
22642268
if not super().validate():
22652269
return False
22662270
if not self.user.is_active:
22672271
self.email.errors.append(get_message("DISABLED_ACCOUNT")[0])
22682272
return False
2269-
if requires_confirmation(self.user):
2270-
self.email.errors.append(get_message("CONFIRMATION_REQUIRED")[0])
2271-
return False
2272-
return True
22732273

22742274

22752275
## ... source file abbreviated to get to request examples ...
@@ -2313,6 +2313,7 @@ class LoginForm(Form, NextFormMixin):
23132313
)
23142314
)
23152315
self.password.description = html
2316+
self.requires_confirmation = False
23162317

23172318
def validate(self):
23182319
if not super().validate():
@@ -2324,7 +2325,6 @@ class LoginForm(Form, NextFormMixin):
23242325
self.email.errors.append(get_message("USER_DOES_NOT_EXIST")[0])
23252326
hash_password(self.password.data)
23262327
return False
2327-
if not self.user.password:
23282328

23292329

23302330
## ... source file abbreviated to get to request examples ...
@@ -2366,7 +2366,7 @@ class ResetPasswordForm(Form, NewPasswordFormMixin, PasswordConfirmFormMixin):
23662366
if not super().validate():
23672367
return False
23682368

2369-
pbad = _security._password_validator(
2369+
pbad, self.password.data = _security._password_util.validate(
23702370
self.password.data, False, user=current_user
23712371
)
23722372
if pbad:

content/pages/examples/flask/flask-globals-session.markdown

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,8 +927,10 @@ from werkzeug.local import LocalProxy
927927
from .utils import (
928928
SmsSenderFactory,
929929
base_render_json,
930+
check_and_get_token_status,
930931
config_value,
931932
do_flash,
933+
get_within_delta,
932934
login_user,
933935
json_error_response,
934936
send_mail,

content/pages/examples/flask/flask-helpers-flash.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,7 @@ from flask_login import current_user
959959
from flask_login import COOKIE_NAME as REMEMBER_COOKIE_NAME
960960
from flask_principal import AnonymousIdentity, Identity, identity_changed, Need
961961
from flask_wtf import csrf
962-
from wtforms import validators, ValidationError
962+
from wtforms import ValidationError
963963
from itsdangerous import BadSignature, SignatureExpired
964964
from werkzeug.local import LocalProxy
965965
from werkzeug.datastructures import MultiDict

content/pages/examples/flask/flask-helpers-make-response.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ The Flask-Security-Too project is provided as open source under the
323323
```python
324324
# views.py
325325

326+
from functools import partial
326327
import time
327328

328329
from flask import (
@@ -356,7 +357,6 @@ from .unified_signin import (
356357
us_verify,
357358
us_verify_link,
358359
us_verify_send_code,
359-
)
360360

361361

362362
## ... source file abbreviated to get to make_response examples ...

content/pages/examples/flask/flask-helpers-url-for.markdown

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,7 @@ from flask_login import current_user
936936
from flask_login import COOKIE_NAME as REMEMBER_COOKIE_NAME
937937
from flask_principal import AnonymousIdentity, Identity, identity_changed, Need
938938
from flask_wtf import csrf
939-
from wtforms import validators, ValidationError
939+
from wtforms import ValidationError
940940
from itsdangerous import BadSignature, SignatureExpired
941941
from werkzeug.local import LocalProxy
942942
from werkzeug.datastructures import MultiDict
@@ -1238,9 +1238,9 @@ class Role(db.Model):
12381238
def to_json(self):
12391239
json_user = {
12401240
~~ 'url': url_for('api.get_user', id=self.id),
1241-
'username': self.username,
1242-
'member_since': self.member_since,
1243-
'last_seen': self.last_seen,
1241+
~~ 'username': self.username,
1242+
~~ 'member_since': self.member_since,
1243+
~~ 'last_seen': self.last_seen,
12441244
~~ 'posts_url': url_for('api.get_user_posts', id=self.id),
12451245
~~ 'followed_posts_url': url_for('api.get_user_followed_posts',
12461246
id=self.id),
@@ -1298,9 +1298,9 @@ class Post(db.Model):
12981298
def to_json(self):
12991299
json_post = {
13001300
~~ 'url': url_for('api.get_post', id=self.id),
1301-
'body': self.body,
1302-
'body_html': self.body_html,
1303-
'timestamp': self.timestamp,
1301+
~~ 'body': self.body,
1302+
~~ 'body_html': self.body_html,
1303+
~~ 'timestamp': self.timestamp,
13041304
~~ 'author_url': url_for('api.get_user', id=self.author_id),
13051305
~~ 'comments_url': url_for('api.get_post_comments', id=self.id),
13061306
'comment_count': self.comments.count()
@@ -1340,9 +1340,9 @@ class Comment(db.Model):
13401340
json_comment = {
13411341
~~ 'url': url_for('api.get_comment', id=self.id),
13421342
~~ 'post_url': url_for('api.get_post', id=self.post_id),
1343-
'body': self.body,
1344-
'body_html': self.body_html,
1345-
'timestamp': self.timestamp,
1343+
~~ 'body': self.body,
1344+
~~ 'body_html': self.body_html,
1345+
~~ 'timestamp': self.timestamp,
13461346
~~ 'author_url': url_for('api.get_user', id=self.author_id),
13471347
}
13481348
return json_comment

0 commit comments

Comments
 (0)