<commit_before>import datetime
import hashlib
import secrets
from io import BytesIO
from zipfile import ZipFile

import typing as typing
from django.db.models import (
    Model,

    BooleanField,
    CharField,
    DateTimeField,
    IntegerField,
    ForeignKey,
    FileField,
    TextField,
    CASCADE, SET_NULL, PROTECT)
from django.contrib.contenttypes.models import ContentType
from polymorphic.models import PolymorphicModel

from accounts.models import Account
from .permission_models import ProjectPermission, ProjectRole, UserProjectRole
from .session_models import *

TOKEN_HASH_FUNCTION = hashlib.sha256
PROJECT_KEY_LENGTH = 32
TRUNCATED_TOKEN_SHOW_CHARACTERS = 8


def generate_project_key() -> str:
    """Generate a random key for a SessionGroup."""
    return secrets.token_hex(PROJECT_KEY_LENGTH)


def generate_project_token(project: 'Project') -> str:
    """Generate a unique token for a Project based on its creator, creation date and a random string."""
    user_id = project.creator.id if project.creator else None
    created = project.created or datetime.datetime.now()
    return TOKEN_HASH_FUNCTION("{}{}{}".format(user_id, created, secrets.token_hex()).encode("utf8")).hexdigest()


class Project(Model):
    """
    A project
    """
    name = TextField(
        null=True,
        blank=True,
        help_text="The name of the project"
    )

    creator = ForeignKey(
        'auth.User',
        null=True,  # Should only be null if the creator is deleted
        on_delete=SET_NULL,
        related_name='projects_created',
        help_text='User who created project'
    )

    created = DateTimeField(
        auto_now_add=True,
        help_text='When this project was created'
    )

    public = BooleanField(
        default=False,
        help_text='Should this project be publically visible?'
    )

    token = TextField(
        null=True,
        unique=True,
        help_text='A token to publicly identify the SessionGroup (in URLs etc)'
    )

    key = TextField(
        null=True,
        blank=True,
        help_text='Key required to create Sessions in this SessionGroup'
    )

    max_sessions = IntegerField(
        null=True,
        help_text='Maximum total number of sessions that can be created in this SessionGroup (null = unlimited)'
    )

    max_concurrent = IntegerField(
        null=True,
        help_text='Maximum number of sessions allowed to run at one time for this SessionGroup (null = unlimited)',
    )

    max_queue = IntegerField(
        null=True,
        help_text='Maximum number of users waiting for a new Session to be created in this Session Group '
                  '(null = unlimited)'
    )

    session_parameters = ForeignKey(
        'SessionParameters',
        related_name='projects',
        null=True,
        on_delete=CASCADE,
        help_text='The SessionParameters that defines resources and other parameters of new sessions for this Project.'
    )

    account = ForeignKey(
        Account,
        on_delete=PROTECT,
        related_name='projects',
        null=False
    )

    def __str__(self):
        return 'Project #{}'.format(self.id)

    @staticmethod
    def create(project_type, creator):
        """
        Create a new editor of the given project_type
        """
        if project_type == 'files':
            return FilesSource.objects.create(creator=creator)
        else:
            raise RuntimeError('Unhandled project type "{}" when attempting to create project'.format(project_type))

    @staticmethod
    def get_or_create(project_type, address, creator):
        """
        Get, or create, a project
        """
        return Project.objects.get_or_create(
            address=address,
            creator=creator
        )
<commit_msg>Reorder and set blank=True on Project fields to make it easier to create in admin<commit_after>import datetime
import hashlib
import secrets
from io import BytesIO
from zipfile import ZipFile

import typing as typing
from django.db.models import (
    Model,

    BooleanField,
    CharField,
    DateTimeField,
    IntegerField,
    ForeignKey,
    FileField,
    TextField,
    CASCADE, SET_NULL, PROTECT)
from django.contrib.contenttypes.models import ContentType
from polymorphic.models import PolymorphicModel

from accounts.models import Account
from .permission_models import ProjectPermission, ProjectRole, UserProjectRole
from .session_models import *

TOKEN_HASH_FUNCTION = hashlib.sha256
PROJECT_KEY_LENGTH = 32
TRUNCATED_TOKEN_SHOW_CHARACTERS = 8


def generate_project_key() -> str:
    """Generate a random key for a SessionGroup."""
    return secrets.token_hex(PROJECT_KEY_LENGTH)


def generate_project_token(project: 'Project') -> str:
    """Generate a unique token for a Project based on its creator, creation date and a random string."""
    user_id = project.creator.id if project.creator else None
    created = project.created or datetime.datetime.now()
    return TOKEN_HASH_FUNCTION("{}{}{}".format(user_id, created, secrets.token_hex()).encode("utf8")).hexdigest()


class Project(Model):
    """
    A project
    """

    account = ForeignKey(
        Account,
        on_delete=PROTECT,
        related_name='projects',
        null=False
    )

    name = TextField(
        null=True,
        blank=True,
        help_text="The name of the project"
    )

    creator = ForeignKey(
        'auth.User',
        null=True,  # Should only be null if the creator is deleted
        on_delete=SET_NULL,
        related_name='projects_created',
        help_text='User who created project'
    )

    created = DateTimeField(
        auto_now_add=True,
        help_text='When this project was created'
    )

    public = BooleanField(
        default=False,
        help_text='Should this project be publically visible?'
    )

    token = TextField(
        null=True,
        blank=True,
        unique=True,
        help_text='A token to publicly identify the SessionGroup (in URLs etc)'
    )

    key = TextField(
        null=True,
        blank=True,
        help_text='Key required to create Sessions in this SessionGroup'
    )

    max_sessions = IntegerField(
        null=True,
        blank=True,
        help_text='Maximum total number of sessions that can be created in this SessionGroup (null = unlimited)'
    )

    max_concurrent = IntegerField(
        null=True,
        blank=True,
        help_text='Maximum number of sessions allowed to run at one time for this SessionGroup (null = unlimited)',
    )

    max_queue = IntegerField(
        null=True,
        blank=True,
        help_text='Maximum number of users waiting for a new Session to be created in this Session Group '
                  '(null = unlimited)'
    )

    session_parameters = ForeignKey(
        'SessionParameters',
        related_name='projects',
        null=True,
        blank=True,
        on_delete=CASCADE,
        help_text='The SessionParameters that defines resources and other parameters of new sessions for this Project.'
    )

    def __str__(self):
        return 'Project #{}'.format(self.id)

    @staticmethod
    def create(project_type, creator):
        """
        Create a new editor of the given project_type
        """
        if project_type == 'files':
            return FilesSource.objects.create(creator=creator)
        else:
            raise RuntimeError('Unhandled project type "{}" when attempting to create project'.format(project_type))

    @staticmethod
    def get_or_create(project_type, address, creator):
        """
        Get, or create, a project
        """
        return Project.objects.get_or_create(
            address=address,
            creator=creator
        )

{'fraction_non_alphanumeric': 0.08060551170914737, 'fraction_numerical': 0.001811359813688705, 'mean_word_length': 2.71456030754445, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, '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': '4482730', 'n_tokens_mistral': 2157, 'n_tokens_neox': 2073, 'n_words': 637}