<commit_before>import csv
import io
import sys
import tempfile

import pytest
from django.core.management import call_command
from django.test import TestCase

from ..management.commands import bulkimportusers as b
from ..management.commands.bulkexportusers import labels
from .helpers import create_dummy_facility_data
from kolibri.core.auth.constants import demographics
from kolibri.core.auth.constants import role_kinds
from kolibri.core.auth.models import Classroom
from kolibri.core.auth.models import FacilityUser


CLASSROOMS = 2

# validators


def test_number_range_validator():
    check = b.number_range(1900, 2050, allow_null=False)
    with pytest.raises(ValueError):
        check(3)
    with pytest.raises(ValueError):
        check(None)
    with pytest.raises(ValueError):
        check(2051)

    check = b.number_range(1900, 2050, allow_null=True)
    assert check("1969") is None
    assert check("2050") is None
    assert check(None) is None


def test_value_length_validator():
    check = b.value_length(4, allow_null=False)
    with pytest.raises(ValueError):
        assert check(None)

    check = b.value_length(4, allow_null=True)
    with pytest.raises(ValueError):
        check("Learning")
    assert check(None) is None
    assert check("ok") is None
    assert check("") is None


def test_enumeration_validator():
    check = b.enumeration("LEARNER", "ADMIN", "COACH")
    assert check("aDMin") is None
    with pytest.raises(ValueError):
        check("other")
    check = b.enumeration("kolibri")
    assert check("b") is None


def test_valid_name_validator():
    check = b.valid_name()
    # pytest.set_trace()
    assert check("bob123") is None
    with pytest.raises(ValueError):
        check("bob 123")

    check = b.valid_name(username=False)
    check("bob 123") is None
    with pytest.raises(ValueError):
        check("bob123-..")
    with pytest.raises(ValueError):
        check(None)

    check = b.valid_name(allow_null=True)
    check(None) is None


class ImportTestCase(TestCase):
    def setUp(self):
        self.data = create_dummy_facility_data(
            classroom_count=CLASSROOMS, learnergroup_count=1
        )
        self.facility = self.data["facility"]

        _, self.filepath = tempfile.mkstemp(suffix=".csv")
        call_command(
            "bulkexportusers",
            output_file=self.filepath,
            overwrite=True,
            facility=self.facility.id,
        )

        FacilityUser.objects.all().delete()
        Classroom.objects.all().delete()

    def create_csv(self, filepath, rows):
        header_labels = list(labels.values())

        if sys.version_info[0] < 3:
            csv_file = io.open(filepath, "wb")
        else:
            csv_file = io.open(filepath, "w", newline="")

        with csv_file as f:
            writer = csv.writer(f)
            writer.writerow(header_labels)
            for item in rows:
                writer.writerow(item)

    def import_exported_csv(self):
        # import exported csv
        call_command(
            "bulkimportusers", self.filepath, facility=self.facility.id,
        )
        current_classes = Classroom.objects.filter(parent_id=self.facility).all()
        for classroom in current_classes:
            assert len(classroom.get_members()) == CLASSROOMS
            assert len(classroom.get_coaches()) == 1

    def test_dryrun_from_export_csv(self):
        with open(self.filepath, "r") as source:
            header = next(csv.reader(source, strict=True))
        header_translation = {l.partition("(")[2].partition(")")[0]: l for l in header}
        cmd = b.Command()

        with open(self.filepath) as source:
            reader = csv.DictReader(source, strict=True)
            per_line_errors, classes, users, roles = cmd.csv_values_validation(
                reader, header_translation
            )

        assert len(users) == 13
        assert per_line_errors == []
        assert roles[role_kinds.ADMIN] == ["facadmin"]
        assert roles[role_kinds.COACH] == ["faccoach"]
        assert "classcoach0" in roles[role_kinds.ASSIGNABLE_COACH]
        assert "classcoach1" in roles[role_kinds.ASSIGNABLE_COACH]
        enrolled_classes = classes[0]
        assert enrolled_classes["classroom0"] == ["learnerag", "learnerclass0group0"]
        assert enrolled_classes["classroom1"] == ["learnerag", "learnerclass1group0"]
        assigned_classes = classes[1]
        assert assigned_classes["classroom0"] == ["classcoach0"]
        assert assigned_classes["classroom1"] == ["classcoach1"]

    def test_delete_users_and_classes(self):
        self.import_exported_csv()

        # new csv to import and clear classes and delete non-admin users:
        _, new_filepath = tempfile.mkstemp(suffix=".csv")
        rows = []
        rows.append(
            [
                "new_learner",
                None,
                None,
                "LEARNER",
                None,
                "2001",
                "FEMALE",
                "new_class",
                None,
            ]
        )<commit_msg>Don't trust the order of the elements in the list<commit_after>import csv
import io
import sys
import tempfile

import pytest
from django.core.management import call_command
from django.test import TestCase

from ..management.commands import bulkimportusers as b
from ..management.commands.bulkexportusers import labels
from .helpers import create_dummy_facility_data
from kolibri.core.auth.constants import demographics
from kolibri.core.auth.constants import role_kinds
from kolibri.core.auth.models import Classroom
from kolibri.core.auth.models import FacilityUser


CLASSROOMS = 2

# validators


def test_number_range_validator():
    check = b.number_range(1900, 2050, allow_null=False)
    with pytest.raises(ValueError):
        check(3)
    with pytest.raises(ValueError):
        check(None)
    with pytest.raises(ValueError):
        check(2051)

    check = b.number_range(1900, 2050, allow_null=True)
    assert check("1969") is None
    assert check("2050") is None
    assert check(None) is None


def test_value_length_validator():
    check = b.value_length(4, allow_null=False)
    with pytest.raises(ValueError):
        assert check(None)

    check = b.value_length(4, allow_null=True)
    with pytest.raises(ValueError):
        check("Learning")
    assert check(None) is None
    assert check("ok") is None
    assert check("") is None


def test_enumeration_validator():
    check = b.enumeration("LEARNER", "ADMIN", "COACH")
    assert check("aDMin") is None
    with pytest.raises(ValueError):
        check("other")
    check = b.enumeration("kolibri")
    assert check("b") is None


def test_valid_name_validator():
    check = b.valid_name()
    # pytest.set_trace()
    assert check("bob123") is None
    with pytest.raises(ValueError):
        check("bob 123")

    check = b.valid_name(username=False)
    check("bob 123") is None
    with pytest.raises(ValueError):
        check("bob123-..")
    with pytest.raises(ValueError):
        check(None)

    check = b.valid_name(allow_null=True)
    check(None) is None


class ImportTestCase(TestCase):
    def setUp(self):
        self.data = create_dummy_facility_data(
            classroom_count=CLASSROOMS, learnergroup_count=1
        )
        self.facility = self.data["facility"]

        _, self.filepath = tempfile.mkstemp(suffix=".csv")
        call_command(
            "bulkexportusers",
            output_file=self.filepath,
            overwrite=True,
            facility=self.facility.id,
        )

        FacilityUser.objects.all().delete()
        Classroom.objects.all().delete()

    def create_csv(self, filepath, rows):
        header_labels = list(labels.values())

        if sys.version_info[0] < 3:
            csv_file = io.open(filepath, "wb")
        else:
            csv_file = io.open(filepath, "w", newline="")

        with csv_file as f:
            writer = csv.writer(f)
            writer.writerow(header_labels)
            for item in rows:
                writer.writerow(item)

    def import_exported_csv(self):
        # import exported csv
        call_command(
            "bulkimportusers", self.filepath, facility=self.facility.id,
        )
        current_classes = Classroom.objects.filter(parent_id=self.facility).all()
        for classroom in current_classes:
            assert len(classroom.get_members()) == CLASSROOMS
            assert len(classroom.get_coaches()) == 1

    def test_dryrun_from_export_csv(self):
        with open(self.filepath, "r") as source:
            header = next(csv.reader(source, strict=True))
        header_translation = {l.partition("(")[2].partition(")")[0]: l for l in header}
        cmd = b.Command()

        with open(self.filepath) as source:
            reader = csv.DictReader(source, strict=True)
            per_line_errors, classes, users, roles = cmd.csv_values_validation(
                reader, header_translation
            )

        assert len(users) == 13
        assert per_line_errors == []
        assert roles[role_kinds.ADMIN] == ["facadmin"]
        assert roles[role_kinds.COACH] == ["faccoach"]
        assert "classcoach0" in roles[role_kinds.ASSIGNABLE_COACH]
        assert "classcoach1" in roles[role_kinds.ASSIGNABLE_COACH]
        enrolled_classes = classes[0]
        assert "learnerag" in enrolled_classes["classroom0"]
        assert "learnerclass0group0" in enrolled_classes["classroom0"]
        assert "learnerag" in enrolled_classes["classroom1"]
        assert "learnerclass1group0" in enrolled_classes["classroom1"]
        assigned_classes = classes[1]
        assert assigned_classes["classroom0"] == ["classcoach0"]
        assert assigned_classes["classroom1"] == ["classcoach1"]

    def test_delete_users_and_classes(self):
        self.import_exported_csv()

        # new csv to import and clear classes and delete non-admin users:
        _, new_filepath = tempfile.mkstemp(suffix=".csv")
        rows = []
        rows.append(
            [
                "new_learner",
                None,
                None,
                "LEARNER",
                None,
                "2001",
                "FEMALE",
                "new_class",
                None,
            ]
        )
{'fraction_non_alphanumeric': 0.10517642497091896, 'fraction_numerical': 0.013765025203567274, 'mean_word_length': 2.5748440748440746, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 3, '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': '5888944', 'n_tokens_mistral': 3254, 'n_tokens_neox': 3111, 'n_words': 665}