Moving anon user tracking from image element to form submit#433
Moving anon user tracking from image element to form submit#433iambibhas wants to merge 16 commits into
Conversation
| {%- if header_campaign %}{{ campaign_script() }}{% endif %} | ||
| {% assets "js_tinymce" %}<script src="{{ ASSET_URL }}" type="text/javascript"></script>{% endassets %} | ||
| {% block footerscripts %}{% endblock %} | ||
| {{ anon_user_script() }} |
There was a problem hiding this comment.
This should be in layout.html.jinja2 only. We have a problem if we’re copy pasting an entire block between templates.
There was a problem hiding this comment.
i went through the templates, and they need some reorganization. Some blocks have been redefined in several places. I can fix them, but seems a little off topic for this PR. Maybe a separate PR for it?
There was a problem hiding this comment.
This will change with @vidya-ram's PWA PR (#425). We should revisit this PR once that is merged.
| {%- if not g.user and not g.anon_user %} | ||
| <script type="text/javascript"> | ||
| $(function () { | ||
| var sniffurl = "{{ url_for('sniffle') }}"; |
There was a problem hiding this comment.
“Sniff” is the wrong term for this. We’re looking for an interactive session.
| } | ||
| window.onscroll = function (e) { | ||
| if (!sniffed) { | ||
| sniff("scolll"); |
|
|
||
|
|
||
| @app.route('/_sniffle.gif') | ||
| @app.route('/_sniffle', methods=['POST']) |
There was a problem hiding this comment.
This will now be /api/1/anonsession. This should also move into an views/api.py.
|
|
||
| 1. If there's g.user and session['anon_user'], it loads that anon_user and tags with user=g.user, then removes anon | ||
| 2. If there's no g.user and no session['anon_user'] and form submitted token matches session['au'], sets session['anon_user'] = 'test' | ||
| 3. If there's no g.user and there is session['au'] != form['token'], loads g.anon_user |
There was a problem hiding this comment.
2 and 3 seem to be reversed. Also, you don’t need a test token in session['au'] anymore. A valid form CSRF token is enough. Our entire test token mechanism can be removed.
| session['au'] = g.anon_user.id | ||
| session.permanent = True | ||
|
|
||
| return Response("OK") |
There was a problem hiding this comment.
Don’t reply with text. Return {'status': 'ok'} and wrap the view with render_with(json=True).
| if anon_user and g.user: | ||
| # we have anon user id in session['au'], set anon_user.user to current user | ||
| anon_user.user = g.user | ||
| g.db_commit_needed = True |
There was a problem hiding this comment.
Ugh, what is this? We should be doing automatic database commits if there’s a dirty session and no exception was raised. Better than such hacky flags.
| session.permanent = True | ||
| if 'impressions' in session: | ||
| elif not g.user: | ||
| session['au'] = u'test-' + unicode(uuid4()) |
There was a problem hiding this comment.
This is no longer needed since we’ll use the CSRF token cookie now.
| in the current event session (impressions or views) as a dictionary of {id: bgroup} | ||
| """ | ||
| if not g.esession.persistent: | ||
| if g.esession and not g.esession.persistent: |
| @@ -31,4 +32,5 @@ | |||
| <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script> | |||
| <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/c3/0.4.14/c3.min.js"></script> | |||
There was a problem hiding this comment.
@jace should these static files be in tablayout template? Shouldn't they be in individual templates that inherit them and needs c3/d3?
There was a problem hiding this comment.
We now use C3 enough that it should be in the top level requirements, and all these direct references should be removed.
jace
left a comment
There was a problem hiding this comment.
A few notes. Will need another review after the other PRs are merged.
| {%- if header_campaign %}{{ campaign_script() }}{% endif %} | ||
| {% assets "js_tinymce" %}<script src="{{ ASSET_URL }}" type="text/javascript"></script>{% endassets %} | ||
| {% block footerscripts %}{% endblock %} | ||
| {{ anon_user_script() }} |
There was a problem hiding this comment.
This will change with @vidya-ram's PWA PR (#425). We should revisit this PR once that is merged.
| @@ -31,4 +32,5 @@ | |||
| <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script> | |||
| <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/c3/0.4.14/c3.min.js"></script> | |||
There was a problem hiding this comment.
We now use C3 enough that it should be in the top level requirements, and all these direct references should be removed.
| db.session.commit() | ||
|
|
||
| if g.anon_user: | ||
| session['au'] = g.anon_user.id |
There was a problem hiding this comment.
Are we using session['au'] for two different sorts of content? That doesn't seem clean.
There was a problem hiding this comment.
I'm using session['au'] only to store anon user ID now. if it exists in cookie, it'll contain the anon user id.
| from ..utils import scrubemail, redactemail, cointoss | ||
|
|
||
|
|
||
| gif1x1 = 'R0lGODlhAQABAJAAAP8AAAAAACH5BAUQAAAALAAAAAABAAEAAAICBAEAOw=='.decode('base64') |
There was a problem hiding this comment.
This line can go. It's not needed anymore.
There was a problem hiding this comment.
@jace there is a view_application_email_gif endpoint where it's being used
hasjob/hasjob/views/listing.py
Line 397 in 41450f7
| {%- endwith %} | ||
| {%- endmacro -%} | ||
|
|
||
| {%- macro anon_user_script() -%} |
There was a problem hiding this comment.
Can this be moved into app.js? It adds bulk to every page otherwise (to pages served to bots).
There was a problem hiding this comment.
But if we move it to app.js then it'll load with all page load regardless of bots or humans? Also, will have to hardcode the api endpoint.
There was a problem hiding this comment.
It will load only once because app.js is cached.
| """ | ||
| Load anon user: | ||
|
|
||
| 1. If client returns valid csrf token, create and set g.anon_user |
| """ | ||
| csrf_form = FlaskForm() | ||
| if csrf_form.validate_on_submit(): | ||
| # This client sent us back valid csrf token |
| 2. if g.anon_user exists, set session['au'] to anon_user.id | ||
| """ | ||
| csrf_form = FlaskForm() | ||
| if csrf_form.validate_on_submit(): |
There was a problem hiding this comment.
if not g.user and not g.anon_user and csrf_form.validate_on_submit():
|
|
||
| if g.anon_user: | ||
| session['au'] = g.anon_user.id | ||
| g.esession.load_from_cache(session['au'], UserEvent) |
There was a problem hiding this comment.
What does this do? session['au'] contains a brand new id. How is it a cache reference?
| g.anon_user = None # Could change below | ||
| g.event_data = {} # Views can add data to the current pageview event | ||
| g.anon_user = None | ||
| g.event_data = {} |
| else: | ||
| if g.user: | ||
| anon_user.user = g.user | ||
| session.pop('au', None) |
There was a problem hiding this comment.
Under if g.user: add g.anon_user = None, and then an else clause before the following line. You don't want to have both g.user and g.anon_user
| if g.anon_user and 'impressions' in session: | ||
| # Run this in the foreground since we need this later in the request for A/B display consistency. | ||
| # This is most likely being called from the UI-non-blocking sniffle.gif anyway. | ||
| save_impressions(g.esession.id, session.pop('impressions').values(), now) |
There was a problem hiding this comment.
Move this to a function that is called both here and in the the anonsession endpoint after setting g.anon_user. Comment needs to change as well.
| else: | ||
| g.esession.save_to_cache(session['au']) | ||
| if 'au' in session: | ||
| g.esession.save_to_cache(session['au']) |
There was a problem hiding this comment.
Cache has to be saved before we have an anonymous user. It's not relevant after.
|
@iambibhas Here is how this is supposed to work. First, we recognise three states for a user's identity:
When the user transitions from stage 2 to stage 3, we want to preserve their browsing history. This is why we do The transition from stage 1 to 2 is trickier. At stage 1, it could be a scraping bot causing a lot of traffic. We don't want to track session history for that, but we also don't want to lose the history of a client that turns out to be an anon user. Therefore:
A bot will also create a Redis EventSession entry, but it will be auto-discarded in five minutes. |
| if 'impression' in session: | ||
| rq_save_impressions(g.esession.id, session.pop('impressions').values(), now, delay=False) | ||
|
|
||
| return Response({'status': 'ok'}) |
| if g.anon_user: | ||
| session['au'] = g.anon_user.id | ||
| if 'impression' in session: | ||
| rq_save_impressions(g.esession.id, session.pop('impressions').values(), now, delay=False) |
There was a problem hiding this comment.
Use the main save_impressions function.
| def rq_save_impressions(session_id, impressions, viewed_time, delay=True): | ||
| func = save_impressions.delay if delay else save_impressions | ||
| print func, session_id | ||
| func(session_id, impressions, viewed_time) |
There was a problem hiding this comment.
You don't need this function. Call save_impressions or save_impressions.delay directly from wherever you need it.
| if 'au' in session and session['au'] is not None: | ||
| if unicode(session['au']).startswith('test'): | ||
| # old test token that we no longer need | ||
| session.pop('au', None) |
There was a problem hiding this comment.
session.pop('au') since it's guaranteed present. If it's not, that's definitely an error worth raising for us to investigate.
| if g.anon_user and 'impressions' in session: | ||
| # Run this in the foreground | ||
| # since we need this later in the request for A/B display consistency. | ||
| rq_save_impressions(g.esession.id, session.pop('impressions').values(), now, delay=False) |
| @app.after_request | ||
| def record_views_and_events(response): | ||
| if hasattr(g, 'db_commit_needed') and g.db_commit_needed: | ||
| if len(db.session.dirty) > 0 or len(db.session.new) > 0: |
There was a problem hiding this comment.
I'd move this into a generic function in Coaster so that this call can be commit_if_necessary(db.session)
There was a problem hiding this comment.
Or we could just do db.session.commit() without an if condition. What is the impact of committing an empty transaction?
|
Bibhas seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
Anytime the site is loaded and there is -
the anon user session is set using a form submit.
Fixes #409.