<commit_before># This file is part of Indico.
# Copyright (C) 2002 - 2016 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import

import cPickle
import uuid
from datetime import datetime, timedelta

from flask import request, flash
from flask.sessions import SessionInterface, SessionMixin
from markupsafe import Markup
from werkzeug.datastructures import CallbackDict
from werkzeug.utils import cached_property

from indico.core.config import Config
from indico.modules.users import User
from indico.util.decorators import cached_writable_property
from indico.util.i18n import _, set_best_lang
from MaKaC.common.cache import GenericCache


class BaseSession(CallbackDict, SessionMixin):
    def __init__(self, initial=None, sid=None, new=False):
        def on_update(self):
            self.modified = True
        CallbackDict.__init__(self, initial, on_update)
        self.sid = sid
        self.new = new
        self.modified = False
        defaults = self._get_defaults()
        if defaults:
            self.update(defaults)

    def _get_defaults(self):
        # Note: This is called before there is a DB connection available!
        return None


# Hey, if you intend on adding a custom property to this class:
# - Only do it if you need logic behind it. Otherwise use the dict API!
# - Even if you do need logic, keep it to core stuff. Otherwise it probably does not belong here!
# - Always prefix the dict keys backing a property with an underscore (to prevent clashes with externally-set items)
# - When you store something like the avatar that involves a DB lookup, use cached_writable_property
class IndicoSession(BaseSession):
    @cached_writable_property('_user')
    def user(self):
        user_id = self.get('_user_id')
        user = User.get(user_id) if user_id is not None else None
        if user and user.is_deleted:
            merged_into_user = user.merged_into_user
            user = None
            # If the user is deleted and the request is likely to be seen by
            # the user, we forcefully log him out and inform him about it.
            if not request.is_xhr and request.blueprint != 'assets':
                self.clear()
                if merged_into_user:
                    msg = _(u'Your profile has been merged into <strong>{}</strong>. '
                            u'Please log in using that profile.')
                    flash(Markup(msg).format(merged_into_user.full_name), 'warning')
                else:
                    flash(_(u'Your profile has been deleted.'), 'error')
        return user

    @user.setter
    def user(self, user):
        if user is None:
            self.pop('_user_id', None)
        else:
            self['_user_id'] = user.id
        self._refresh_sid = True

    @property
    def avatar(self):
        return self.user.as_avatar if self.user else None

    @property
    def lang(self):
        if '_lang' in self:
            # explicit language set in the session
            return self['_lang']
        else:
            # guess language based on accept-languages or use default
            return set_best_lang(check_session=False)

    @lang.setter
    def lang(self, lang):
        self['_lang'] = lang

    @cached_property
    def csrf_token(self):
        if '_csrf_token' not in self:
            if not self.csrf_protected:
                # don't store a token in the session if we don't really need CSRF protection
                return '00000000-0000-0000-0000-000000000000'
            self['_csrf_token'] = str(uuid.uuid4())
        return self['_csrf_token']

    @property
    def csrf_protected(self):
        return self.user is not None

    @property
    def timezone(self):
        if '_timezone' in self:
            return self['_timezone']
        if '_user_id' not in self:
            return 'LOCAL'
        return Config.getInstance().getDefaultTimezone()

    @timezone.setter
    def timezone(self, tz):
        self['_timezone'] = tz


class IndicoSessionInterface(SessionInterface):
    pickle_based = True
    serializer = cPickle
    session_class = IndicoSession
    temporary_session_lifetime = timedelta(days=7)

    def __init__(self):<commit_msg>Add session.tzinfo for the current tzinfo<commit_after># This file is part of Indico.
# Copyright (C) 2002 - 2016 European Organization for Nuclear Research (CERN).
#
# Indico is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 3 of the
# License, or (at your option) any later version.
#
# Indico is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Indico; if not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import

import cPickle
import uuid
from datetime import datetime, timedelta

from flask import request, flash
from flask.sessions import SessionInterface, SessionMixin
from markupsafe import Markup
from werkzeug.datastructures import CallbackDict
from werkzeug.utils import cached_property

from indico.core.config import Config
from indico.modules.users import User
from indico.util.decorators import cached_writable_property
from indico.util.i18n import _, set_best_lang
from MaKaC.common.cache import GenericCache
from MaKaC.common.timezoneUtils import DisplayTZ


class BaseSession(CallbackDict, SessionMixin):
    def __init__(self, initial=None, sid=None, new=False):
        def on_update(self):
            self.modified = True
        CallbackDict.__init__(self, initial, on_update)
        self.sid = sid
        self.new = new
        self.modified = False
        defaults = self._get_defaults()
        if defaults:
            self.update(defaults)

    def _get_defaults(self):
        # Note: This is called before there is a DB connection available!
        return None


# Hey, if you intend on adding a custom property to this class:
# - Only do it if you need logic behind it. Otherwise use the dict API!
# - Even if you do need logic, keep it to core stuff. Otherwise it probably does not belong here!
# - Always prefix the dict keys backing a property with an underscore (to prevent clashes with externally-set items)
# - When you store something like the avatar that involves a DB lookup, use cached_writable_property
class IndicoSession(BaseSession):
    @cached_writable_property('_user')
    def user(self):
        user_id = self.get('_user_id')
        user = User.get(user_id) if user_id is not None else None
        if user and user.is_deleted:
            merged_into_user = user.merged_into_user
            user = None
            # If the user is deleted and the request is likely to be seen by
            # the user, we forcefully log him out and inform him about it.
            if not request.is_xhr and request.blueprint != 'assets':
                self.clear()
                if merged_into_user:
                    msg = _(u'Your profile has been merged into <strong>{}</strong>. '
                            u'Please log in using that profile.')
                    flash(Markup(msg).format(merged_into_user.full_name), 'warning')
                else:
                    flash(_(u'Your profile has been deleted.'), 'error')
        return user

    @user.setter
    def user(self, user):
        if user is None:
            self.pop('_user_id', None)
        else:
            self['_user_id'] = user.id
        self._refresh_sid = True

    @property
    def avatar(self):
        return self.user.as_avatar if self.user else None

    @property
    def lang(self):
        if '_lang' in self:
            # explicit language set in the session
            return self['_lang']
        else:
            # guess language based on accept-languages or use default
            return set_best_lang(check_session=False)

    @lang.setter
    def lang(self, lang):
        self['_lang'] = lang

    @cached_property
    def csrf_token(self):
        if '_csrf_token' not in self:
            if not self.csrf_protected:
                # don't store a token in the session if we don't really need CSRF protection
                return '00000000-0000-0000-0000-000000000000'
            self['_csrf_token'] = str(uuid.uuid4())
        return self['_csrf_token']

    @property
    def csrf_protected(self):
        return self.user is not None

    @property
    def timezone(self):
        if '_timezone' in self:
            return self['_timezone']
        if '_user_id' not in self:
            return 'LOCAL'
        return Config.getInstance().getDefaultTimezone()

    @timezone.setter
    def timezone(self, tz):
        self['_timezone'] = tz

    @property
    def tzinfo(self):
        """The tzinfo of the user's current timezone.

        This should only be used in places where no other timezone
        such as from an event or category is available.
        """
        return DisplayTZ().getDisplayTZ(as_timezone=True)


class IndicoSessionInterface(SessionInterface):
    pickle_based = True
    serializer = cPickle
    session_class = IndicoSession
    temporary_session_lifetime = timedelta(days=7)

    def __init__(self):
{'fraction_non_alphanumeric': 0.07144278606965174, 'fraction_numerical': 0.008955223880597015, 'mean_word_length': 2.6350813743218806, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '939718', 'n_tokens_mistral': 2836, 'n_tokens_neox': 2681, 'n_words': 1038}