# Generated by Django 4.2.20 on 2025-06-30 15:11

import datetime

import django.utils.timezone
import mptt.fields
from django.conf import settings
from django.db import migrations, models

import NEMO.fields
import NEMO.utilities
from NEMO.migrations_utils import news_for_version_forward, news_for_version_reverse


def rebuild_area_tree(apps, schema_editor):
    manager = mptt.managers.TreeManager()
    Area = apps.get_model("NEMO", "Area")
    manager.model = Area
    mptt.register(Area, parent_attr="parent_area")
    manager.contribute_to_class(Area, "objects")
    manager.rebuild()


def copy_badge_number_data(apps, schema_editor):
    User = apps.get_model(settings.AUTH_USER_MODEL)
    for user in User.objects.all():
        user.badge_number = user.badge_number_tmp
        user.save()


def rename_buddy_board_customization(apps, schema_editor):
    Customization = apps.get_model("NEMO", "Customization")
    try:
        buddy_board_desc_old = Customization.objects.get(name="buddy_board_disclaimer")
        Customization.objects.create(name="buddy_board_description", value=buddy_board_desc_old.value)
        buddy_board_desc_old.delete()
    except:
        pass


def migrate_exceptions_to_closures(apps, schema_editor):
    Closure = apps.get_model("NEMO", "Closure")
    ClosureTime = apps.get_model("NEMO", "ClosureTime")
    try:
        for closure in Closure.objects.all():
            ClosureTime.objects.create(closure=closure, start_time=closure.start_time, end_time=closure.end_time)
    except:
        pass


def migrate_staff_availability_categories(apps, schema_editor):
    # We are creating new staff availability categories based on the previous char fields and setting them
    StaffAvailability = apps.get_model("NEMO", "StaffAvailability")
    StaffAvailabilityCategory = apps.get_model("NEMO", "StaffAvailabilityCategory")
    categories = list(
        set(list(StaffAvailability.objects.filter(category__isnull=False).values_list("category", flat=True)))
    )
    categories.sort()
    for staff_availability in StaffAvailability.objects.all():
        if staff_availability.category:
            category, created = StaffAvailabilityCategory.objects.get_or_create(
                name=staff_availability.category, display_order=categories.index(staff_availability.category)
            )
            staff_availability.category_link = category
            staff_availability.save()


class Migration(migrations.Migration):

    replaces = [
        ("NEMO", "0020_version_3_0_0"),
        ("NEMO", "0021_version_3_1_0"),
        ("NEMO", "0022_version_3_3_0"),
        ("NEMO", "0023_badgereader"),
        ("NEMO", "0024_contactinformation_user"),
        ("NEMO", "0025_version_3_6_0"),
        ("NEMO", "0026_version_3_7_0"),
        ("NEMO", "0027_version_3_8_0"),
        ("NEMO", "0028_version_3_9_0"),
        ("NEMO", "0030_version_3_9_2"),
        ("NEMO", "0031_version_3_10_0"),
        ("NEMO", "0032_version_3_11_0"),
        ("NEMO", "0033_version_3_12_0"),
        ("NEMO", "0034_version_3_13_0"),
        ("NEMO", "0035_version_3_14_0"),
        ("NEMO", "0036_version_3_15_0"),
        ("NEMO", "0037_version_3_16_0"),
    ]

    dependencies = [
        ("NEMO", "0012_version_2_0_0_squashed"),
    ]

    operations = [
        migrations.AlterModelOptions(
            name="area",
            options={},
        ),
        migrations.AddField(
            model_name="area",
            name="level",
            field=models.PositiveIntegerField(default=0, editable=False),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name="area",
            name="lft",
            field=models.PositiveIntegerField(default=0, editable=False),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name="area",
            name="rght",
            field=models.PositiveIntegerField(default=0, editable=False),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name="area",
            name="tree_id",
            field=models.PositiveIntegerField(db_index=True, default=0, editable=False),
            preserve_default=False,
        ),
        migrations.AddField(
            model_name="area",
            name="category",
            field=models.CharField(
                blank=True,
                db_column="category",
                help_text='Create sub-categories using slashes. For example "Category 1/Sub-category 1".',
                max_length=1000,
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="parent_area",
            field=mptt.fields.TreeForeignKey(
                blank=True,
                help_text="Select a parent area, (building, floor etc.)",
                null=True,
                on_delete=django.db.models.deletion.CASCADE,
                related_name="area_children_set",
                to="NEMO.area",
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="logout_grace_period",
            field=models.PositiveIntegerField(
                blank=True,
                help_text="Number of minutes users have to logout of this area after their reservation expired before being flagged and abuse email is sent.",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="maximum_future_reservation_time",
            field=models.PositiveIntegerField(
                blank=True,
                db_column="maximum_future_reservation_time",
                help_text="The maximum amount of time (in minutes) that a user may reserve from the current time onwards.",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="maximum_reservations_per_day",
            field=models.PositiveIntegerField(
                blank=True,
                db_column="maximum_reservations_per_day",
                help_text="The maximum number of reservations a user may make per day for this area.",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="maximum_usage_block_time",
            field=models.PositiveIntegerField(
                blank=True,
                db_column="maximum_usage_block_time",
                help_text="The maximum amount of time (in minutes) that a user may reserve this area for a single reservation. Leave this field blank to indicate that no maximum usage block time exists for this area.",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="minimum_time_between_reservations",
            field=models.PositiveIntegerField(
                blank=True,
                db_column="minimum_time_between_reservations",
                help_text="The minimum amount of time (in minutes) that the same user must have between any two reservations for this area.",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="minimum_usage_block_time",
            field=models.PositiveIntegerField(
                blank=True,
                db_column="minimum_usage_block_time",
                help_text="The minimum amount of time (in minutes) that a user must reserve this area for a single reservation. Leave this field blank to indicate that no minimum usage block time exists for this area.",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="missed_reservation_threshold",
            field=models.PositiveIntegerField(
                blank=True,
                db_column="missed_reservation_threshold",
                help_text='The amount of time (in minutes) that a area reservation may go unused before it is automatically marked as "missed" and hidden from the calendar. Usage can be from any user, regardless of who the reservation was originally created for. The cancellation process is triggered by a timed job on the web server.',
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="policy_off_between_times",
            field=models.BooleanField(
                db_column="policy_off_between_times",
                default=False,
                help_text="Check this box to disable policy rules every day between the given times",
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="policy_off_end_time",
            field=models.TimeField(
                blank=True,
                db_column="policy_off_end_time",
                help_text="The end time when policy rules should NOT be enforced",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="policy_off_start_time",
            field=models.TimeField(
                blank=True,
                db_column="policy_off_start_time",
                help_text="The start time when policy rules should NOT be enforced",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="policy_off_weekend",
            field=models.BooleanField(
                db_column="policy_off_weekend",
                default=False,
                help_text="Whether or not policy rules should be enforced on weekends",
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="requires_reservation",
            field=models.BooleanField(
                default=False,
                help_text="Check this box to require a reservation for this area before a user can login.",
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="reservation_horizon",
            field=models.PositiveIntegerField(
                blank=True,
                db_column="reservation_horizon",
                default=14,
                help_text="Users may create reservations this many days in advance. Leave this field blank to indicate that no reservation horizon exists for this area.",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="scheduledoutage",
            name="area",
            field=mptt.fields.TreeForeignKey(
                blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="NEMO.area"
            ),
        ),
        migrations.AddField(
            model_name="reservation",
            name="area",
            field=mptt.fields.TreeForeignKey(
                blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="NEMO.area"
            ),
        ),
        migrations.AlterField(
            model_name="scheduledoutage",
            name="tool",
            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="NEMO.tool"),
        ),
        migrations.AlterField(
            model_name="reservation",
            name="tool",
            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="NEMO.tool"),
        ),
        migrations.AlterField(
            model_name="area",
            name="welcome_message",
            field=models.TextField(
                blank=True,
                help_text="The welcome message will be displayed on the tablet login page. You can use HTML and JavaScript.",
                null=True,
            ),
        ),
        migrations.AlterField(
            model_name="user",
            name="physical_access_levels",
            field=models.ManyToManyField(blank=True, to="NEMO.physicalaccesslevel"),
        ),
        migrations.AlterModelOptions(
            name="areaaccessrecord",
            options={},
        ),
        migrations.AlterField(
            model_name="scheduledoutage",
            name="resource",
            field=models.ForeignKey(
                blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to="NEMO.resource"
            ),
        ),
        migrations.AddIndex(
            model_name="area",
            index=models.Index(fields=["name"], name="NEMO_area_name_1e7670_idx"),
        ),
        migrations.AddIndex(
            model_name="areaaccessrecord",
            index=models.Index(fields=["end"], name="NEMO_areaac_end_fae061_idx"),
        ),
        migrations.AlterField(
            model_name="areaaccessrecord",
            name="area",
            field=mptt.fields.TreeForeignKey(on_delete=django.db.models.deletion.CASCADE, to="NEMO.area"),
        ),
        migrations.AlterField(
            model_name="door",
            name="area",
            field=mptt.fields.TreeForeignKey(
                on_delete=django.db.models.deletion.PROTECT, related_name="doors", to="NEMO.area"
            ),
        ),
        migrations.AlterField(
            model_name="physicalaccesslevel",
            name="area",
            field=mptt.fields.TreeForeignKey(on_delete=django.db.models.deletion.CASCADE, to="NEMO.area"),
        ),
        migrations.AlterField(
            model_name="tool",
            name="_requires_area_access",
            field=mptt.fields.TreeForeignKey(
                blank=True,
                db_column="requires_area_access_id",
                help_text="Indicates that this tool is physically located in a billable area and requires an active area access record in order to be operated.",
                null=True,
                on_delete=django.db.models.deletion.PROTECT,
                to="NEMO.area",
            ),
        ),
        migrations.RunPython(rebuild_area_tree, migrations.RunPython.noop),
        migrations.AddField(
            model_name="area",
            name="abuse_email",
            field=NEMO.fields.MultiEmailField(
                blank=True,
                help_text="An email will be sent to this address when users overstay in the area or in children areas (logged in with expired reservation). A comma-separated list can be used.",
                max_length=2000,
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="reservation_email",
            field=NEMO.fields.MultiEmailField(
                blank=True,
                help_text="An email will be sent to this address when users create or cancel reservations in the area or in children areas. A comma-separated list can be used.",
                max_length=2000,
                null=True,
            ),
        ),
        migrations.AlterField(
            model_name="physicalaccesslevel",
            name="schedule",
            field=models.IntegerField(choices=[(0, "Always"), (1, "Weekdays"), (2, "Weekends")]),
        ),
        migrations.AddField(
            model_name="physicalaccesslevel",
            name="weekdays_end_time",
            field=models.TimeField(
                blank=True, default=datetime.time(0, 0), help_text="The weekday access end time", null=True
            ),
        ),
        migrations.AddField(
            model_name="physicalaccesslevel",
            name="weekdays_start_time",
            field=models.TimeField(
                blank=True, default=datetime.time(7, 0), help_text="The weekday access start time", null=True
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="count_service_personnel_in_occupancy",
            field=models.BooleanField(
                default=True, help_text="Indicates that service personnel will count towards maximum capacity."
            ),
        ),
        migrations.AddField(
            model_name="user",
            name="is_service_personnel",
            field=models.BooleanField(
                default=False,
                help_text="Designates this user as service personnel. Service personnel can operate qualified tools without a reservation even when they are shutdown or during an outage and can access authorized areas without a reservation.",
                verbose_name="service personnel",
            ),
        ),
        migrations.AddField(
            model_name="consumable",
            name="reminder_threshold_reached",
            field=models.BooleanField(default=False),
        ),
        migrations.CreateModel(
            name="BadgeReader",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                ("name", models.CharField(max_length=200)),
                (
                    "send_key",
                    models.CharField(
                        help_text="The name of the key which submits the badge number ('F2', 'Shift', 'Meta', 'Enter', 'a' etc.)",
                        max_length=20,
                    ),
                ),
                (
                    "record_key",
                    models.CharField(
                        blank=True,
                        help_text="The name of the key which starts badge number recording. If left blank, badge number recording starts when any input is received.",
                        max_length=20,
                        null=True,
                    ),
                ),
            ],
        ),
        migrations.AddField(
            model_name="contactinformation",
            name="user",
            field=models.OneToOneField(
                blank=True,
                help_text="Select a user to associate with this contact. When set, this contact information will be shown instead of the user information on pages like tool details.",
                null=True,
                on_delete=django.db.models.deletion.CASCADE,
                to=settings.AUTH_USER_MODEL,
            ),
        ),
        migrations.CreateModel(
            name="ToolUsageCounter",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                ("name", models.CharField(help_text="The name of this counter", max_length=200)),
                (
                    "description",
                    models.TextField(
                        blank=True,
                        help_text="The counter description to be displayed next to it on the tool control page",
                        null=True,
                    ),
                ),
                ("value", models.PositiveIntegerField(default=0, help_text="The current value of this counter")),
                (
                    "tool_usage_question",
                    models.CharField(
                        help_text="The name of the tool's post usage question which should be used to increment this counter",
                        max_length=200,
                    ),
                ),
                (
                    "last_reset_value",
                    models.PositiveIntegerField(
                        blank=True, help_text="The last value before the counter was reset", null=True
                    ),
                ),
                (
                    "last_reset",
                    models.DateTimeField(
                        blank=True, help_text="The date and time this counter was last reset", null=True
                    ),
                ),
                ("is_active", models.BooleanField(default=True, help_text="The state of the counter")),
                (
                    "last_reset_by",
                    models.ForeignKey(
                        blank=True,
                        help_text="The user who last reset this counter",
                        null=True,
                        on_delete=django.db.models.deletion.SET_NULL,
                        to=settings.AUTH_USER_MODEL,
                    ),
                ),
                (
                    "tool",
                    models.ForeignKey(
                        help_text="The tool this counter is for.",
                        on_delete=django.db.models.deletion.CASCADE,
                        to="NEMO.tool",
                    ),
                ),
            ],
        ),
        migrations.CreateModel(
            name="PhysicalAccessException",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                (
                    "name",
                    models.CharField(
                        help_text="The name of this exception that will be displayed as the policy problem",
                        max_length=100,
                    ),
                ),
                (
                    "start_time",
                    models.DateTimeField(
                        help_text="The start of the exception, after which users will be denied access."
                    ),
                ),
                (
                    "end_time",
                    models.DateTimeField(
                        help_text="The end of the exception, after which users will be allowed access again"
                    ),
                ),
                ("physical_access_levels", models.ManyToManyField(blank=True, to="NEMO.physicalaccesslevel")),
            ],
            options={
                "ordering": ["-start_time"],
            },
        ),
        migrations.CreateModel(
            name="BuddyRequest",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                (
                    "creation_time",
                    models.DateTimeField(
                        default=django.utils.timezone.now, help_text="The date and time when the request was created."
                    ),
                ),
                ("start", models.DateField(help_text="The start date the user is requesting a buddy.")),
                ("end", models.DateField(help_text="The end date the user is requesting a buddy.")),
                ("description", models.TextField(help_text="The description of the request.")),
                (
                    "expired",
                    models.BooleanField(
                        default=False, help_text="Indicates the request has expired and won't be shown anymore."
                    ),
                ),
                (
                    "deleted",
                    models.BooleanField(
                        default=False, help_text="Indicates the request has been deleted and won't be shown anymore."
                    ),
                ),
                ("area", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="NEMO.area")),
                (
                    "user",
                    models.ForeignKey(
                        help_text="The user who is submitting the request.",
                        on_delete=django.db.models.deletion.CASCADE,
                        to=settings.AUTH_USER_MODEL,
                    ),
                ),
            ],
        ),
        migrations.AddField(
            model_name="area",
            name="buddy_system_allowed",
            field=models.BooleanField(
                default=False, help_text="Check this box if the buddy system is allowed in this area."
            ),
        ),
        migrations.AddField(
            model_name="userpreferences",
            name="display_new_buddy_request_notification",
            field=models.BooleanField(
                default=True,
                help_text="Whether or not to notify the user of new buddy requests (via unread badges)",
                verbose_name="new_buddy_request_notification",
            ),
        ),
        migrations.AddField(
            model_name="userpreferences",
            name="display_new_buddy_request_reply_notification",
            field=models.BooleanField(
                default=True,
                help_text="Whether or not to notify the user of replies on buddy request he commented on (via unread badges)",
                verbose_name="new_buddy_request_reply_notification",
            ),
        ),
        migrations.AddField(
            model_name="userpreferences",
            name="email_new_buddy_request_reply",
            field=models.BooleanField(
                default=True,
                help_text="Whether or not to email the user of replies on buddy request he commented on",
                verbose_name="email_new_buddy_request_reply",
            ),
        ),
        migrations.AlterField(
            model_name="landingpagechoice",
            name="notifications",
            field=models.CharField(
                blank=True,
                choices=[
                    ("news", "News creation and updates - notifies all users"),
                    ("safetyissue", "New safety issues - notifies staff only"),
                    ("buddyrequest", "New buddy request - notifies all users"),
                    (
                        "buddyrequestmessage",
                        "New buddy request reply - notifies request creator and users who have replied",
                    ),
                ],
                help_text="Displays a the number of new notifications for the user. For example, if the user has two unread news notifications then the number '2' would appear for the news icon on the landing page.",
                max_length=25,
                null=True,
            ),
        ),
        migrations.CreateModel(
            name="BuddyRequestMessage",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                ("creation_date", models.DateTimeField(default=django.utils.timezone.now)),
                ("content", models.TextField()),
                ("author", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
                (
                    "buddy_request",
                    models.ForeignKey(
                        help_text="The request that this message relates to.",
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="replies",
                        to="NEMO.buddyrequest",
                    ),
                ),
            ],
            options={
                "ordering": ["creation_date"],
            },
        ),
        migrations.CreateModel(
            name="EmailLog",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                (
                    "category",
                    models.IntegerField(
                        choices=[
                            (0, "General"),
                            (1, "System"),
                            (2, "Direct Contact"),
                            (3, "Broadcast Email"),
                            (4, "Timed Services"),
                            (5, "Feedback"),
                            (6, "Abuse"),
                            (7, "Safety"),
                            (8, "Tasks"),
                        ],
                        default=0,
                    ),
                ),
                ("when", models.DateTimeField(auto_now_add=True)),
                ("sender", models.EmailField(max_length=254)),
                ("to", models.TextField()),
                ("subject", models.CharField(max_length=254)),
                ("content", models.TextField()),
                ("ok", models.BooleanField(default=True)),
                ("attachments", models.TextField(null=True)),
            ],
            options={
                "ordering": ["-when"],
            },
        ),
        migrations.RunPython(news_for_version_forward("3.7.0"), news_for_version_reverse("3.7.0")),
        migrations.RunPython(news_for_version_forward("3.8.0"), news_for_version_reverse("3.8.0")),
        migrations.AlterModelOptions(
            name="toolusagecounter",
            options={"ordering": ["tool__name"]},
        ),
        migrations.AlterField(
            model_name="toolusagecounter",
            name="last_reset_value",
            field=models.FloatField(blank=True, help_text="The last value before the counter was reset", null=True),
        ),
        migrations.AlterField(
            model_name="toolusagecounter",
            name="value",
            field=models.FloatField(default=0, help_text="The current value of this counter"),
        ),
        migrations.CreateModel(
            name="ToolDocuments",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                (
                    "document",
                    models.FileField(
                        blank=True,
                        null=True,
                        upload_to=NEMO.utilities.document_filename_upload,
                        verbose_name="Document",
                    ),
                ),
                ("uploaded_at", models.DateTimeField(auto_now_add=True)),
                (
                    "name",
                    models.CharField(
                        blank=True,
                        help_text="The optional name to display for this document",
                        max_length=200,
                        null=True,
                    ),
                ),
                ("url", models.CharField(blank=True, max_length=200, null=True, verbose_name="URL")),
                ("tool", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="NEMO.tool")),
            ],
            options={
                "verbose_name_plural": "Tool documents",
                "ordering": ["-uploaded_at"],
            },
        ),
        migrations.AddField(
            model_name="user",
            name="managed_projects",
            field=models.ManyToManyField(
                blank=True,
                help_text="Select the projects that this user is a PI for.",
                related_name="manager_set",
                to="NEMO.project",
            ),
        ),
        migrations.RunPython(news_for_version_forward("3.9.0"), news_for_version_reverse("3.9.0")),
        migrations.RenameField(
            model_name="user",
            old_name="badge_number",
            new_name="badge_number_tmp",
        ),
        migrations.AddField(
            model_name="user",
            name="badge_number",
            field=models.CharField(
                blank=True,
                help_text="The badge number associated with this user. This number must correctly correspond to a user in order for the tablet-login system (in the lobby) to work properly.",
                max_length=50,
                null=True,
                unique=True,
            ),
        ),
        migrations.RunPython(copy_badge_number_data),
        migrations.RemoveField(
            model_name="user",
            name="badge_number_tmp",
        ),
        migrations.AddField(
            model_name="project",
            name="only_allow_tools",
            field=models.ManyToManyField(
                blank=True, help_text="Selected tools will be the only ones allowed for this project.", to="NEMO.tool"
            ),
        ),
        migrations.AddField(
            model_name="toolusagecounter",
            name="warning_email",
            field=NEMO.fields.MultiEmailField(
                blank=True,
                help_text="The address to send the warning email to. A comma-separated list can be used.",
                max_length=2000,
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="toolusagecounter",
            name="warning_threshold",
            field=models.FloatField(
                blank=True,
                help_text="When set in combination with the email address, a warning email will be sent when the counter reaches this value.",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="toolusagecounter",
            name="warning_threshold_reached",
            field=models.BooleanField(default=False),
        ),
        migrations.AddField(
            model_name="project",
            name="allow_consumable_withdrawals",
            field=models.BooleanField(
                default=True, help_text="Uncheck this box if consumable withdrawals are forbidden under this project"
            ),
        ),
        migrations.AlterField(
            model_name="alert",
            name="title",
            field=models.CharField(blank=True, max_length=150),
        ),
        migrations.AddField(
            model_name="tool",
            name="_tool_calendar_color",
            field=models.CharField(
                db_column="tool_calendar_color",
                default="#33ad33",
                help_text="Color for tool reservations in calendar overviews",
                max_length=9,
            ),
        ),
        migrations.AddField(
            model_name="area",
            name="area_calendar_color",
            field=models.CharField(
                default="#88B7CD", help_text="Color for tool reservations in calendar overviews", max_length=9
            ),
        ),
        migrations.AddField(
            model_name="tool",
            name="_superusers",
            field=models.ManyToManyField(
                blank=True,
                db_table="NEMO_tool_superusers",
                help_text="Superusers who can train users on this tool.",
                related_name="superuser_for_tools",
                to=settings.AUTH_USER_MODEL,
            ),
        ),
        migrations.RunPython(news_for_version_forward("3.10.0"), news_for_version_reverse("3.10.0")),
        migrations.RunPython(news_for_version_forward("3.11.0"), news_for_version_reverse("3.11.0")),
        migrations.CreateModel(
            name="ReservationQuestions",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                ("name", models.CharField(help_text="The name of this ", max_length=100)),
                (
                    "questions",
                    models.TextField(
                        help_text="Upon making a reservation, the user will be asked these questions. This field will only accept JSON format"
                    ),
                ),
                (
                    "tool_reservations",
                    models.BooleanField(
                        default=True, help_text="Check this box to apply these questions to tool reservations"
                    ),
                ),
                (
                    "only_for_tools",
                    models.ManyToManyField(
                        blank=True,
                        help_text="Select the tools these questions only apply to. Leave blank for all tools",
                        to="NEMO.tool",
                    ),
                ),
                (
                    "area_reservations",
                    models.BooleanField(
                        default=False, help_text="Check this box to apply these questions to area reservations"
                    ),
                ),
                (
                    "only_for_areas",
                    models.ManyToManyField(
                        blank=True,
                        help_text="Select the areas these questions only apply to. Leave blank for all areas",
                        to="NEMO.area",
                    ),
                ),
                (
                    "only_for_projects",
                    models.ManyToManyField(
                        blank=True,
                        help_text="Select the projects these questions only apply to. Leave blank for all projects",
                        to="NEMO.project",
                    ),
                ),
            ],
            options={
                "verbose_name_plural": "Reservation questions",
                "ordering": ["name"],
            },
        ),
        migrations.AddField(
            model_name="reservation",
            name="question_data",
            field=models.TextField(blank=True, null=True),
        ),
        migrations.AddField(
            model_name="news",
            name="pinned",
            field=models.BooleanField(
                default=False, help_text="Check this box to keep this story at the top of the news feed"
            ),
        ),
        migrations.CreateModel(
            name="AccountType",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                ("name", models.CharField(max_length=100, unique=True)),
            ],
            options={
                "ordering": ["name"],
            },
        ),
        migrations.AddField(
            model_name="account",
            name="start_date",
            field=models.DateField(blank=True, null=True),
        ),
        migrations.AddField(
            model_name="project",
            name="start_date",
            field=models.DateField(blank=True, null=True),
        ),
        migrations.AddField(
            model_name="account",
            name="type",
            field=models.ForeignKey(
                blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to="NEMO.accounttype"
            ),
        ),
        migrations.RunPython(news_for_version_forward("3.12.0"), news_for_version_reverse("3.12.0")),
        migrations.RunPython(news_for_version_forward("3.13.0"), news_for_version_reverse("3.13.0")),
        migrations.RunPython(news_for_version_forward("3.14.0"), news_for_version_reverse("3.14.0")),
        migrations.RunPython(rename_buddy_board_customization),
        migrations.CreateModel(
            name="TemporaryPhysicalAccess",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                ("start_time", models.DateTimeField(help_text="The start of the temporary access")),
                ("end_time", models.DateTimeField(help_text="The end of the temporary access")),
                (
                    "physical_access_level",
                    models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="NEMO.physicalaccesslevel"),
                ),
                ("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
            ],
            options={
                "ordering": ["-end_time"],
            },
        ),
        migrations.AddField(
            model_name="user",
            name="is_facility_manager",
            field=models.BooleanField(
                default=False,
                help_text="Designates this user as facility manager. Facility managers receive updates on all reported problems in the facility and can also review access requests.",
                verbose_name="facility manager",
            ),
        ),
        migrations.AlterField(
            model_name="user",
            name="is_staff",
            field=models.BooleanField(
                default=False,
                help_text="Designates whether the user can log into this admin site.",
                verbose_name="staff",
            ),
        ),
        migrations.AlterField(
            model_name="user",
            name="is_superuser",
            field=models.BooleanField(
                default=False,
                help_text="Designates that this user has all permissions without explicitly assigning them.",
                verbose_name="administrator",
            ),
        ),
        migrations.AlterField(
            model_name="user",
            name="is_technician",
            field=models.BooleanField(
                default=False,
                help_text="Specifies how to bill staff time for this user. When checked, customers are billed at technician rates.",
                verbose_name="technician",
            ),
        ),
        migrations.AlterField(
            model_name="landingpagechoice",
            name="notifications",
            field=models.CharField(
                blank=True,
                choices=[
                    ("news", "News creation and updates - notifies all users"),
                    ("safetyissue", "New safety issues - notifies staff only"),
                    ("buddyrequest", "New buddy request - notifies all users"),
                    (
                        "buddyrequestmessage",
                        "New buddy request reply - notifies request creator and users who have replied",
                    ),
                    (
                        "temporaryphysicalaccessrequest",
                        "New access request - notifies other users on request and reviewers",
                    ),
                ],
                help_text="Displays a the number of new notifications for the user. For example, if the user has two unread news notifications then the number '2' would appear for the news icon on the landing page.",
                max_length=100,
                null=True,
            ),
        ),
        migrations.AlterField(
            model_name="emaillog",
            name="category",
            field=models.IntegerField(
                choices=[
                    (0, "General"),
                    (1, "System"),
                    (2, "Direct Contact"),
                    (3, "Broadcast Email"),
                    (4, "Timed Services"),
                    (5, "Feedback"),
                    (6, "Abuse"),
                    (7, "Safety"),
                    (8, "Tasks"),
                    (9, "Access Requests"),
                ],
                default=0,
            ),
        ),
        migrations.AlterField(
            model_name="physicalaccesslevel",
            name="schedule",
            field=models.IntegerField(choices=[(0, "Anytime"), (1, "Weekdays"), (2, "Weekends")]),
        ),
        migrations.AddField(
            model_name="physicalaccesslevel",
            name="allow_user_request",
            field=models.BooleanField(
                default=False,
                help_text='Check this box to allow users to request this access temporarily in "Access requests"',
            ),
        ),
        migrations.CreateModel(
            name="TemporaryPhysicalAccessRequest",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                (
                    "creation_time",
                    models.DateTimeField(
                        auto_now_add=True, help_text="The date and time when the request was created."
                    ),
                ),
                (
                    "last_updated",
                    models.DateTimeField(auto_now=True, help_text="The last time this request was modified."),
                ),
                ("description", models.TextField(blank=True, help_text="The description of the request.", null=True)),
                ("start_time", models.DateTimeField(help_text="The requested time for the access to start.")),
                ("end_time", models.DateTimeField(help_text="The requested time for the access to end.")),
                (
                    "status",
                    models.IntegerField(
                        choices=[(0, "Pending"), (1, "Approved"), (2, "Denied"), (3, "Expired")], default=0
                    ),
                ),
                (
                    "deleted",
                    models.BooleanField(
                        default=False, help_text="Indicates the request has been deleted and won't be shown anymore."
                    ),
                ),
                (
                    "creator",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="access_requests_created",
                        to=settings.AUTH_USER_MODEL,
                    ),
                ),
                (
                    "last_updated_by",
                    models.ForeignKey(
                        blank=True,
                        help_text="The last user who modified this request.",
                        null=True,
                        on_delete=django.db.models.deletion.SET_NULL,
                        related_name="access_requests_updated",
                        to=settings.AUTH_USER_MODEL,
                    ),
                ),
                (
                    "other_users",
                    models.ManyToManyField(
                        blank=True, help_text="Select the other users requesting access.", to=settings.AUTH_USER_MODEL
                    ),
                ),
                (
                    "physical_access_level",
                    models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="NEMO.physicalaccesslevel"),
                ),
                (
                    "reviewer",
                    models.ForeignKey(
                        blank=True,
                        null=True,
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="access_requests_reviewed",
                        to=settings.AUTH_USER_MODEL,
                    ),
                ),
            ],
            options={
                "ordering": ["-creation_time"],
            },
        ),
        migrations.AddField(
            model_name="staffcharge",
            name="note",
            field=models.TextField(blank=True, null=True),
        ),
        migrations.RunPython(news_for_version_forward("3.15.0"), news_for_version_reverse("3.15.0")),
        migrations.CreateModel(
            name="StaffAbsenceType",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                ("name", models.CharField(help_text="The name of this absence type.", max_length=255)),
                ("description", models.CharField(help_text="The description for this absence type.", max_length=255)),
            ],
            options={
                "ordering": ["name"],
            },
        ),
        migrations.CreateModel(
            name="StaffAvailability",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                (
                    "category",
                    models.CharField(
                        blank=True, help_text="The category for this staff member.", max_length=100, null=True
                    ),
                ),
                (
                    "start_time",
                    models.TimeField(blank=True, help_text="The usual start time for this staff member.", null=True),
                ),
                (
                    "end_time",
                    models.TimeField(blank=True, help_text="The usual end time for this staff member.", null=True),
                ),
                (
                    "monday",
                    models.BooleanField(
                        default=True, help_text="Check this box if the staff member usually works on Mondays."
                    ),
                ),
                (
                    "tuesday",
                    models.BooleanField(
                        default=True, help_text="Check this box if the staff member usually works on Tuesdays."
                    ),
                ),
                (
                    "wednesday",
                    models.BooleanField(
                        default=True, help_text="Check this box if the staff member usually works on Wednesdays."
                    ),
                ),
                (
                    "thursday",
                    models.BooleanField(
                        default=True, help_text="Check this box if the staff member usually works on Thursdays."
                    ),
                ),
                (
                    "friday",
                    models.BooleanField(
                        default=True, help_text="Check this box if the staff member usually works on Fridays."
                    ),
                ),
                (
                    "saturday",
                    models.BooleanField(
                        default=False, help_text="Check this box if the staff member usually works on Saturdays."
                    ),
                ),
                (
                    "sunday",
                    models.BooleanField(
                        default=False, help_text="Check this box if the staff member usually works on Sundays."
                    ),
                ),
                (
                    "staff_member",
                    models.ForeignKey(
                        help_text="The staff member to display on the staff status page.",
                        on_delete=django.db.models.deletion.CASCADE,
                        to=settings.AUTH_USER_MODEL,
                    ),
                ),
            ],
            options={
                "verbose_name_plural": "Staff availability",
                "ordering": ["staff_member__first_name"],
            },
        ),
        migrations.CreateModel(
            name="StaffAbsence",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                (
                    "creation_time",
                    models.DateTimeField(
                        auto_now_add=True, help_text="The date and time when the absence was created."
                    ),
                ),
                ("start_date", models.DateField(help_text="The start date of the absence.")),
                ("end_date", models.DateField(help_text="The end date of the absence.")),
                (
                    "full_day",
                    models.BooleanField(
                        default=True, help_text="Uncheck this box when the absence is only for part of the day."
                    ),
                ),
                (
                    "description",
                    models.TextField(
                        blank=True,
                        help_text="The absence description. This will be visible to anyone when the absence is not all day.",
                        null=True,
                    ),
                ),
                (
                    "absence_type",
                    models.ForeignKey(
                        help_text="The absence type. This will only be visible to facility managers.",
                        on_delete=django.db.models.deletion.CASCADE,
                        to="NEMO.staffabsencetype",
                    ),
                ),
                (
                    "staff_member",
                    models.ForeignKey(
                        help_text="The staff member who will be absent.",
                        on_delete=django.db.models.deletion.CASCADE,
                        to="NEMO.staffavailability",
                    ),
                ),
            ],
            options={
                "ordering": ["-creation_time"],
            },
        ),
        migrations.RenameModel(
            old_name="PhysicalAccessException",
            new_name="Closure",
        ),
        migrations.AlterField(
            model_name="closure",
            name="name",
            field=models.CharField(
                help_text="The name of this closure, that will be displayed as the policy problem and alert (if applicable)",
                max_length=255,
            ),
        ),
        migrations.CreateModel(
            name="ClosureTime",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                ("start_time", models.DateTimeField(help_text="The start date and time of the closure")),
                ("end_time", models.DateTimeField(help_text="The end date and time of the closure")),
                ("closure", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="NEMO.closure")),
            ],
            options={
                "ordering": ["-start_time"],
            },
        ),
        migrations.RunPython(migrate_exceptions_to_closures),
        migrations.AlterModelOptions(
            name="closure",
            options={"ordering": ["name"]},
        ),
        migrations.RemoveField(
            model_name="closure",
            name="end_time",
        ),
        migrations.RemoveField(
            model_name="closure",
            name="start_time",
        ),
        migrations.AddField(
            model_name="closure",
            name="alert_days_before",
            field=models.PositiveIntegerField(
                blank=True,
                help_text="Enter the number of days before the closure when an alert should automatically be created. Leave blank for no alert.",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="closure",
            name="alert_template",
            field=models.TextField(
                blank=True,
                help_text="The template to create the alert with. The following variables are provided (when applicable): <b>name</b>, <b>start_time</b>, <b>end_time</b>, <b>areas</b>.",
                null=True,
            ),
        ),
        migrations.AddField(
            model_name="closure",
            name="staff_absent",
            field=models.BooleanField(
                default=True,
                help_text="Check this box and all staff members will be marked absent during this closure in staff status.",
            ),
        ),
        migrations.AlterField(
            model_name="closure",
            name="name",
            field=models.CharField(
                help_text="The name of this closure, that will be displayed as the policy problem and alert (if applicable).",
                max_length=255,
            ),
        ),
        migrations.AlterField(
            model_name="closure",
            name="physical_access_levels",
            field=models.ManyToManyField(
                blank=True, help_text="Select access levels this closure applies to.", to="NEMO.physicalaccesslevel"
            ),
        ),
        migrations.AddField(
            model_name="closure",
            name="notify_managers_last_occurrence",
            field=models.BooleanField(
                default=True,
                help_text="Check this box to notify facility managers on the last occurrence of this closure.",
            ),
        ),
        migrations.RunPython(news_for_version_forward("3.16.0"), news_for_version_reverse("3.16.0")),
        migrations.AddField(
            model_name="configuration",
            name="enabled",
            field=models.BooleanField(
                default=True, help_text="Only active configurations will show up for the selected tool"
            ),
        ),
        migrations.CreateModel(
            name="StaffAvailabilityCategory",
            fields=[
                ("id", models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
                ("name", models.CharField(max_length=200)),
                (
                    "display_order",
                    models.IntegerField(
                        help_text="Staff availability categories are sorted according to display order. The lowest value category is displayed first in the 'Staff status' page."
                    ),
                ),
            ],
            options={
                "verbose_name_plural": "Staff availability categories",
                "ordering": ["display_order", "name"],
            },
        ),
        migrations.AlterModelOptions(
            name="landingpagechoice",
            options={"ordering": ["display_order"]},
        ),
        migrations.RenameField(
            model_name="configuration",
            old_name="display_priority",
            new_name="display_order",
        ),
        migrations.RenameField(
            model_name="landingpagechoice",
            old_name="display_priority",
            new_name="display_order",
        ),
        migrations.AddField(
            model_name="staffavailability",
            name="category_link",
            field=models.ForeignKey(
                blank=True,
                help_text="The category for this staff member.",
                null=True,
                on_delete=django.db.models.deletion.CASCADE,
                to="NEMO.staffavailabilitycategory",
            ),
        ),
        migrations.RunPython(migrate_staff_availability_categories),
        migrations.RemoveField(
            model_name="staffavailability",
            name="category",
        ),
        migrations.RenameField(
            model_name="staffavailability",
            old_name="category_link",
            new_name="category",
        ),
        migrations.AddField(
            model_name="userpreferences",
            name="staff_status_view",
            field=models.CharField(
                choices=[("day", "Day"), ("week", "Week"), ("month", "Month")],
                default="day",
                help_text="Preferred view for staff status page",
                max_length=100,
                verbose_name="staff_status_view",
            ),
        ),
    ]
