<commit_before># -*- coding: utf-8 -*-
# Copyright (c) 2015 The Pycroft Authors. See the AUTHORS file.
# This file is part of the Pycroft project and licensed under the terms of
# the Apache License, Version 2.0. See the LICENSE file for details.
import typing
from difflib import SequenceMatcher

from flask import url_for
from flask_wtf import FlaskForm as Form
from wtforms import Field
from wtforms.widgets import HTMLString

from pycroft.model.address import Address
from pycroft.model.facilities import Room
from web.blueprints.helpers.host import UniqueMac
from web.form.widgets import UserIDField
from wtforms.validators import (
    Regexp, ValidationError, DataRequired, Email, Optional)

from pycroft.model.host import Host
from pycroft.model.user import PropertyGroup, User
from web.blueprints.facilities.forms import building_query, SelectRoomForm, CreateAddressForm
from web.blueprints.properties.forms import property_group_query, property_group_user_create_query
from wtforms_widgets.fields.core import TextField, TextAreaField, BooleanField, \
    QuerySelectField, FormField, \
    QuerySelectMultipleField, DateField, IntegerField
from wtforms_widgets.fields.custom import MacField
from wtforms_widgets.fields.filters import empty_to_none, to_lowercase
from wtforms_widgets.fields.validators import OptionalIf, MacAddress

from ..helpers.form import confirmable_div, ConfirmCheckboxField


def user_query():
    return User.q.order_by(User.id)


def host_query():
    return Host.q.order_by(Host.id)


def group_query():
    return PropertyGroup.q.order_by(PropertyGroup.name)


def validate_unique_login(form, field):
    if User.q.filter_by(login=field.data).first():
        raise ValidationError(u"Nutzerlogin schon vergeben!")


class UniqueName:
    """Checks whether the name of a user in a given building is unique.

    If the form misses one of the fields ``room_number``, ``level``, ``building``,
    this validator is effectively disabled.

    :param force_field: The name of the “do it anyway” checkbox field
    """
    def __init__(self, force_field: typing.Optional[str] = 'force'):
        self.force_field = force_field
        self.building_field = 'building'
        self.level_field = 'level'
        self.room_number_field = 'room_number'
        self.ratio = 0.6

    def force_set(self, form: Form) -> bool:
        return self.force_field \
               and hasattr(form, self.force_field)\
               and getattr(form, self.force_field).data

    def try_get_room(self, form: Form) -> typing.Optional[Room]:
        try:
            number = getattr(form, self.room_number_field).data
            level = getattr(form, self.level_field).data
            building = getattr(form, self.building_field).data
        except AttributeError:
            return

        return Room.q.filter_by(number=number, level=level, building=building).one_or_none()

    def similar_users(self, our_name, room: Room):
        return [u for u in room.users
                if SequenceMatcher(None, our_name, u.name).ratio() > self.ratio]

    def __call__(self, form: Form, field: Field):
        if self.force_set(form):
            return
        if (room := self.try_get_room(form)) is None:
            return
        if not (conflicting_inhabitants := self.similar_users(field.data, room)):
            return

        user_links = ", ".join(
            f"""<a target="_blank" href="{url_for('user.user_show', user_id=user.id)}"/>
                  {user.name}
                </a>""" for user in conflicting_inhabitants
        )
        raise ValidationError(HTMLString(
            f'{confirmable_div(self.force_field)}'
            f'* Ähnliche Benutzer existieren bereits in diesem Zimmer:'
            f'<br/>Nutzer: {user_links}</div>'
        ))


class UniqueEmail:
    """Checks whether a given email is unique, i.e. already assigned to some user.

    :param force_field: The name of the “do it anyway” checkbox field
    """
    def __init__(self, force_field: typing.Optional[str] = 'force'):
        self.force_field = force_field

    def force_set(self, form: Form) -> bool:
        return self.force_field \
               and hasattr(form, self.force_field)\
               and getattr(form, self.force_field).data

    @staticmethod
    def get_conflicting_users(email: str) -> list[User]:
        return User.q.filter_by(email=email).all()

    def __call__(self, form: Form, field: Field):
        if self.force_set(form):
            return
        if not (conflicting_users := self.get_conflicting_users(field.data)):
            return

        user_links = ", ".join(
            f"""<a target="_blank" href="{url_for('user.user_show', user_id=user.id)}"/>
                {user.name}</a>""" for user in conflicting_users
        )
        raise ValidationError(HTMLString(
            f"{confirmable_div(self.force_field)}* E-Mail bereits in Verwendung!"
            f"<br/>Nutzer:{user_links}</div>"
        ))


class UserSearchForm(Form):
    id = TextField(u"Nutzer-ID")
    name = TextField(u"Name")<commit_msg>Escape some username in HTML renders<commit_after># -*- coding: utf-8 -*-
# Copyright (c) 2015 The Pycroft Authors. See the AUTHORS file.
# This file is part of the Pycroft project and licensed under the terms of
# the Apache License, Version 2.0. See the LICENSE file for details.
import typing
from difflib import SequenceMatcher

from flask import url_for
from flask_wtf import FlaskForm as Form
from markupsafe import escape
from wtforms import Field
from wtforms.widgets import HTMLString

from pycroft.model.address import Address
from pycroft.model.facilities import Room
from web.blueprints.helpers.host import UniqueMac
from web.form.widgets import UserIDField
from wtforms.validators import (
    Regexp, ValidationError, DataRequired, Email, Optional)

from pycroft.model.host import Host
from pycroft.model.user import PropertyGroup, User
from web.blueprints.facilities.forms import building_query, SelectRoomForm, CreateAddressForm
from web.blueprints.properties.forms import property_group_query, property_group_user_create_query
from wtforms_widgets.fields.core import TextField, TextAreaField, BooleanField, \
    QuerySelectField, FormField, \
    QuerySelectMultipleField, DateField, IntegerField
from wtforms_widgets.fields.custom import MacField
from wtforms_widgets.fields.filters import empty_to_none, to_lowercase
from wtforms_widgets.fields.validators import OptionalIf, MacAddress

from ..helpers.form import confirmable_div, ConfirmCheckboxField


def user_query():
    return User.q.order_by(User.id)


def host_query():
    return Host.q.order_by(Host.id)


def group_query():
    return PropertyGroup.q.order_by(PropertyGroup.name)


def validate_unique_login(form, field):
    if User.q.filter_by(login=field.data).first():
        raise ValidationError(u"Nutzerlogin schon vergeben!")


class UniqueName:
    """Checks whether the name of a user in a given building is unique.

    If the form misses one of the fields ``room_number``, ``level``, ``building``,
    this validator is effectively disabled.

    :param force_field: The name of the “do it anyway” checkbox field
    """
    def __init__(self, force_field: typing.Optional[str] = 'force'):
        self.force_field = force_field
        self.building_field = 'building'
        self.level_field = 'level'
        self.room_number_field = 'room_number'
        self.ratio = 0.6

    def force_set(self, form: Form) -> bool:
        return self.force_field \
               and hasattr(form, self.force_field)\
               and getattr(form, self.force_field).data

    def try_get_room(self, form: Form) -> typing.Optional[Room]:
        try:
            number = getattr(form, self.room_number_field).data
            level = getattr(form, self.level_field).data
            building = getattr(form, self.building_field).data
        except AttributeError:
            return

        return Room.q.filter_by(number=number, level=level, building=building).one_or_none()

    def similar_users(self, our_name, room: Room):
        return [u for u in room.users
                if SequenceMatcher(None, our_name, u.name).ratio() > self.ratio]

    def __call__(self, form: Form, field: Field):
        if self.force_set(form):
            return
        if (room := self.try_get_room(form)) is None:
            return
        if not (conflicting_inhabitants := self.similar_users(field.data, room)):
            return

        user_links = ", ".join(
            f"""<a target="_blank" href="{url_for('user.user_show', user_id=user.id)}"/>
                  {escape(user.name)}
                </a>""" for user in conflicting_inhabitants
        )
        raise ValidationError(HTMLString(
            f'{confirmable_div(self.force_field)}'
            f'* Ähnliche Benutzer existieren bereits in diesem Zimmer:'
            f'<br/>Nutzer: {user_links}</div>'
        ))


class UniqueEmail:
    """Checks whether a given email is unique, i.e. already assigned to some user.

    :param force_field: The name of the “do it anyway” checkbox field
    """
    def __init__(self, force_field: typing.Optional[str] = 'force'):
        self.force_field = force_field

    def force_set(self, form: Form) -> bool:
        return self.force_field \
               and hasattr(form, self.force_field)\
               and getattr(form, self.force_field).data

    @staticmethod
    def get_conflicting_users(email: str) -> list[User]:
        return User.q.filter_by(email=email).all()

    def __call__(self, form: Form, field: Field):
        if self.force_set(form):
            return
        if not (conflicting_users := self.get_conflicting_users(field.data)):
            return

        user_links = ", ".join(
            f"""<a target="_blank" href="{url_for('user.user_show', user_id=user.id)}"/>
                {escape(user.name)}</a>""" for user in conflicting_users
        )
        raise ValidationError(HTMLString(
            f"{confirmable_div(self.force_field)}* E-Mail bereits in Verwendung!"
            f"<br/>Nutzer:{user_links}</div>"
        ))


class UserSearchForm(Form):
    id = TextField(u"Nutzer-ID")
    name = TextField(u"Name")
{'fraction_non_alphanumeric': 0.10006840613700772, 'fraction_numerical': 0.001759014951627089, 'mean_word_length': 3.0870607028753994, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, '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': '96391', 'n_tokens_mistral': 3086, 'n_tokens_neox': 2957, 'n_words': 814}