<commit_before>import json
import os
import re
import logging
import socket

from django.conf import settings
from django.db import models, transaction
from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage

import envoy
import yaml

from django_extensions.db.models import (
    TimeStampedModel,
    TitleSlugDescriptionModel,
)
import model_utils.models
from model_utils import Choices

from core.fields import DeletingFileField
from core.utils import recursive_update
from cloud.models import SecurityGroup
from volumes.models import Volume

logger = logging.getLogger(__name__)

HOST_INDEX_PATTERN = re.compile('.*-.*-(\d+)')


def get_hostnames_from_hostdefs(hostdefs, username='', namespace=''):
    hostnames = []
    for hostdef in hostdefs:
        for i in xrange(hostdef.count):
            hostnames.append(
                hostdef.hostname_template.format(
                    namespace=namespace,
                    username=username,
                    index=i
                )
            )
    return hostnames


# Map, pillar, and properties files go into storage
def get_map_file_path(obj, filename):
    return "stacks/{0}/{1}/stack.map".format(obj.owner.username, obj.slug)


def get_pillar_file_path(obj, filename):
    return "stacks/{0}/{1}/stack.pillar".format(obj.owner.username, obj.slug)


def get_props_file_path(obj, filename):
    return "stacks/{0}/{1}/stack.props".format(obj.owner.username, obj.slug)


# Top and overstate files go into salt root
def get_top_file_path(obj, filename):
    return "stack_{0}_top.sls".format(obj.id)


def get_overstate_file_path(obj, filename):
    return "stack_{0}_overstate.sls".format(obj.id)


class StackCreationException(Exception):
    def __init__(self, errors, *args, **kwargs):
        self.errors = errors
        super(StackCreationException, self).__init__(*args, **kwargs)


class Level(object):
    DEBUG = 'DEBUG'
    INFO = 'INFO'
    WARN = 'WARNING'
    ERROR = 'ERROR'


class StatusDetailModel(model_utils.models.StatusModel):
    status_detail = models.TextField(blank=True)

    class Meta:
        abstract = True

    def set_status(self, status, detail=''):
        self.status = status
        self.status_detail = detail
        return self.save()


class StackManager(models.Manager):

    @transaction.commit_on_success
    def create_stack(self, owner, blueprint, **data):
        '''
        '''
        title = data.get('title', '')
        description = data.get('description', '')
        public = data.get('public', False)

        if not title:
            raise ValueError("Stack 'title' is a required field.")

        stack = self.model(owner=owner,
                           blueprint=blueprint,
                           title=title,
                           description=description,
                           public=public)
        stack.save()

        # add the namespace
        namespace = data.get('namespace', 'stack{0}'.format(stack.pk))
        stack.namespace = namespace
        stack.save()

        # manage the properties
        properties = blueprint.properties
        recursive_update(properties, data.get('properties', {}))
        props_json = json.dumps(properties, indent=4)
        if not stack.props_file:
            stack.props_file.save(stack.slug + '.props',
                                  ContentFile(props_json))
        else:
            with open(stack.props_file.path, 'w') as f:
                f.write(props_json)

        # create host records on the stack based on the host definitions in
        # the blueprint
        for hostdef in blueprint.host_definitions.all():

            # all components defined in the host definition
            components = hostdef.formula_components.all()
<commit_msg>Handle empty string namespace correctly<commit_after>import json
import os
import re
import logging
import socket

from django.conf import settings
from django.db import models, transaction
from django.core.files.base import ContentFile
from django.core.files.storage import FileSystemStorage

import envoy
import yaml

from django_extensions.db.models import (
    TimeStampedModel,
    TitleSlugDescriptionModel,
)
import model_utils.models
from model_utils import Choices

from core.fields import DeletingFileField
from core.utils import recursive_update
from cloud.models import SecurityGroup
from volumes.models import Volume

logger = logging.getLogger(__name__)

HOST_INDEX_PATTERN = re.compile('.*-.*-(\d+)')


def get_hostnames_from_hostdefs(hostdefs, username='', namespace=''):
    hostnames = []
    for hostdef in hostdefs:
        for i in xrange(hostdef.count):
            hostnames.append(
                hostdef.hostname_template.format(
                    namespace=namespace,
                    username=username,
                    index=i
                )
            )
    return hostnames


# Map, pillar, and properties files go into storage
def get_map_file_path(obj, filename):
    return "stacks/{0}/{1}/stack.map".format(obj.owner.username, obj.slug)


def get_pillar_file_path(obj, filename):
    return "stacks/{0}/{1}/stack.pillar".format(obj.owner.username, obj.slug)


def get_props_file_path(obj, filename):
    return "stacks/{0}/{1}/stack.props".format(obj.owner.username, obj.slug)


# Top and overstate files go into salt root
def get_top_file_path(obj, filename):
    return "stack_{0}_top.sls".format(obj.id)


def get_overstate_file_path(obj, filename):
    return "stack_{0}_overstate.sls".format(obj.id)


class StackCreationException(Exception):
    def __init__(self, errors, *args, **kwargs):
        self.errors = errors
        super(StackCreationException, self).__init__(*args, **kwargs)


class Level(object):
    DEBUG = 'DEBUG'
    INFO = 'INFO'
    WARN = 'WARNING'
    ERROR = 'ERROR'


class StatusDetailModel(model_utils.models.StatusModel):
    status_detail = models.TextField(blank=True)

    class Meta:
        abstract = True

    def set_status(self, status, detail=''):
        self.status = status
        self.status_detail = detail
        return self.save()


class StackManager(models.Manager):

    @transaction.commit_on_success
    def create_stack(self, owner, blueprint, **data):
        '''
        '''
        title = data.get('title', '')
        description = data.get('description', '')
        public = data.get('public', False)

        if not title:
            raise ValueError("Stack 'title' is a required field.")

        stack = self.model(owner=owner,
                           blueprint=blueprint,
                           title=title,
                           description=description,
                           public=public)
        stack.save()

        # add the namespace
        namespace = data.get('namespace', None)
        if not namespace:
            namespace = 'stack{0}'.format(stack.pk)
        stack.namespace = namespace
        stack.save()

        # manage the properties
        properties = blueprint.properties
        recursive_update(properties, data.get('properties', {}))
        props_json = json.dumps(properties, indent=4)
        if not stack.props_file:
            stack.props_file.save(stack.slug + '.props',
                                  ContentFile(props_json))
        else:
            with open(stack.props_file.path, 'w') as f:
                f.write(props_json)

        # create host records on the stack based on the host definitions in
        # the blueprint
        for hostdef in blueprint.host_definitions.all():

            # all components defined in the host definition
            components = hostdef.formula_components.all()

{'fraction_non_alphanumeric': 0.099545159194282, 'fraction_numerical': 0.002599090318388564, 'mean_word_length': 2.574547143520669, 'pattern_counts': {'":': 0, '<': 3, '<?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': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2852399', 'n_tokens_mistral': 2157, 'n_tokens_neox': 2096, 'n_words': 523}