<commit_before>from functools import wraps

from flask import jsonify, request, current_app
from flask_restful import Api, Resource as FlaskRestfulResource, abort, \
    reqparse, inputs
from ipaddr import IPAddress
from sqlalchemy.exc import OperationalError

from pycroft import config
from pycroft.lib.finance import build_transactions_query
from pycroft.lib.membership import make_member_of, remove_member_of
from pycroft.lib.traffic import effective_traffic_group, NoTrafficGroup
from pycroft.lib.user import encode_type2_user_id, edit_email, change_password, \
    status
from pycroft.model import session
from pycroft.model.host import IP, Interface
from pycroft.model.types import IPAddress
from pycroft.model.user import User, IllegalEmailError

api = Api()


def parse_authorization_header(value):
    if not value:
        return None

    try:
        auth_type, api_key = value.split(maxsplit=1)
        return api_key if auth_type.lower() == 'apikey' else None
    except ValueError:
        return None


def authenticate(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        auth = request.headers.get('authorization')
        api_key = parse_authorization_header(auth)

        if api_key is None:
            abort(401, message="Missing API key.")

        if current_app.config['PYCROFT_API_KEY'] != api_key:
            abort(401, message="Invalid API key.")

        return func(*args, **kwargs)

    return wrapper


class Resource(FlaskRestfulResource):
    method_decorators = [authenticate]


def get_user_or_404(user_id):
    user = User.q.get(user_id)
    if user is None:
        abort(404, message="User {} does not exist".format(user_id))
    return user


def get_authenticated_user(user_id, password):
    user = get_user_or_404(user_id)
    if user is None or not user.check_password(password):
        abort(401, message="Authentication failed")
    return user


def get_interface_or_404(interface_id):
    interface = Interface.q.get(interface_id)
    if interface is None:
        abort(404, message="Interface {} does not exist".format(interface_id))
    return interface


def generate_user_data(user):
    try:
        traffic_maxmium = effective_traffic_group(user).credit_limit
    except NoTrafficGroup:
        traffic_maxmium = None

    props = {prop.property_name for prop in user.current_properties}
    user_status = status(user)

    finance_history = [{
        'valid_on': split.transaction.posted_at,
        'amount': split.amount,
        'description': split.transaction.description
    } for split in build_transactions_query(user.account)]
    last_finance_update = finance_history[-1]['valid_on'] if finance_history \
        else None

    return jsonify(
        id=user.id,
        user_id=encode_type2_user_id(user.id),
        name=user.name,
        login=user.login,
        status={
            'member': user_status.member,
            'traffic_exceeded': user_status.traffic_exceeded,
            'network_access': user_status.network_access,
            'account_balanced': user_status.account_balanced,
            'violation': user_status.violation
        },
        room=str(user.room),
        interfaces=[
            {'mac': str(i.mac), 'ips': [str(ip.address) for ip in i.ips]}
            for h in user.hosts for i in h.interfaces
        ],
        mail=user.email,
        cache='cache_access' in props,
        # TODO: make `has_property` use `current_property`
        properties=list(props),
        traffic_balance=user.current_credit,
        traffic_maximum=traffic_maxmium,
        # TODO: think about better way for credit
        finance_balance=-user.account.balance,
        finance_history=finance_history,
        last_finance_update=last_finance_update
    )


class UserResource(Resource):
    def get(self, user_id):
        user = get_user_or_404(user_id)
        return generate_user_data(user)


api.add_resource(UserResource, '/user/<int:user_id>')
<commit_msg>Add traffic history to sipa API<commit_after>from datetime import timedelta
from functools import wraps

from flask import jsonify, request, current_app
from flask_restful import Api, Resource as FlaskRestfulResource, abort, \
    reqparse, inputs
from ipaddr import IPAddress
from sqlalchemy.exc import OperationalError

from pycroft import config
from pycroft.lib.finance import build_transactions_query
from pycroft.lib.membership import make_member_of, remove_member_of
from pycroft.lib.traffic import effective_traffic_group, NoTrafficGroup
from pycroft.lib.user import encode_type2_user_id, edit_email, change_password, \
    status, traffic_history as func_traffic_history
from pycroft.model import session
from pycroft.model.host import IP, Interface
from pycroft.model.types import IPAddress
from pycroft.model.user import User, IllegalEmailError

api = Api()


def parse_authorization_header(value):
    if not value:
        return None

    try:
        auth_type, api_key = value.split(maxsplit=1)
        return api_key if auth_type.lower() == 'apikey' else None
    except ValueError:
        return None


def authenticate(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        auth = request.headers.get('authorization')
        api_key = parse_authorization_header(auth)

        if api_key is None:
            abort(401, message="Missing API key.")

        if current_app.config['PYCROFT_API_KEY'] != api_key:
            abort(401, message="Invalid API key.")

        return func(*args, **kwargs)

    return wrapper


class Resource(FlaskRestfulResource):
    method_decorators = [authenticate]


def get_user_or_404(user_id):
    user = User.q.get(user_id)
    if user is None:
        abort(404, message="User {} does not exist".format(user_id))
    return user


def get_authenticated_user(user_id, password):
    user = get_user_or_404(user_id)
    if user is None or not user.check_password(password):
        abort(401, message="Authentication failed")
    return user


def get_interface_or_404(interface_id):
    interface = Interface.q.get(interface_id)
    if interface is None:
        abort(404, message="Interface {} does not exist".format(interface_id))
    return interface


def generate_user_data(user):
    try:
        traffic_maxmium = effective_traffic_group(user).credit_limit
    except NoTrafficGroup:
        traffic_maxmium = None

    props = {prop.property_name for prop in user.current_properties}
    user_status = status(user)

    interval = timedelta(days=7)
    step = timedelta(days=1)
    traffic_history = func_traffic_history(user.id,
                                          session.utcnow() - interval + step,
                                          interval, step)

    finance_history = [{
        'valid_on': split.transaction.posted_at,
        'amount': split.amount,
        'description': split.transaction.description
    } for split in build_transactions_query(user.account)]
    last_finance_update = finance_history[-1]['valid_on'] if finance_history \
        else None

    return jsonify(
        id=user.id,
        user_id=encode_type2_user_id(user.id),
        name=user.name,
        login=user.login,
        status={
            'member': user_status.member,
            'traffic_exceeded': user_status.traffic_exceeded,
            'network_access': user_status.network_access,
            'account_balanced': user_status.account_balanced,
            'violation': user_status.violation
        },
        room=str(user.room),
        interfaces=[
            {'mac': str(i.mac), 'ips': [str(ip.address) for ip in i.ips]}
            for h in user.hosts for i in h.interfaces
        ],
        mail=user.email,
        cache='cache_access' in props,
        # TODO: make `has_property` use `current_property`
        properties=list(props),
        traffic_balance=user.current_credit,
        traffic_maximum=traffic_maxmium,
        traffic_history=[e.__dict__ for e in traffic_history],
        # TODO: think about better way for credit
        finance_balance=-user.account.balance,
        finance_history=finance_history,
        last_finance_update=last_finance_update
    )


class UserResource(Resource):
    def get(self, user_id):
        user = get_user_or_404(user_id)
        return generate_user_data(user)


api.add_resource(UserResource, '/user/<int:user_id>')

{'fraction_non_alphanumeric': 0.08692528735632184, 'fraction_numerical': 0.007662835249042145, 'mean_word_length': 3.1536548980606662, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6164341', 'n_tokens_mistral': 2557, 'n_tokens_neox': 2456, 'n_words': 584}