<commit_before># Copyright 2014-2015 Boxkite Inc.

# This file is part of the DataCats package and is released under
# the terms of the GNU Affero General Public License version 3.0.
# See LICENSE.txt or http://www.fsf.org/licensing/licenses/agpl-3.0.html

from os.path import abspath, split as path_split, expanduser, isdir, exists
from os import makedirs, getcwd, remove
import subprocess
import shutil
import json
from string import uppercase, lowercase, digits
from random import SystemRandom
from sha import sha
from struct import unpack
from ConfigParser import (SafeConfigParser, Error as ConfigParserError,
    NoOptionError)

from datacats.validate import valid_name
from datacats.docker import (web_command, run_container, remove_container,
    inspect_container, is_boot2docker, data_only_container, docker_host,
    PortAllocatedError, container_logs)
from datacats.template import ckan_extension_template
from datacats.scripts import SCRIPTS_DIR, SHELL

class ProjectError(Exception):
    def __init__(self, message, format_args=()):
        self.message = message
        self.format_args = format_args
        super(ProjectError, self).__init__(message, format_args)

    def __str__(self):
        return self.message.format(*self.format_args)


class Project(object):
    """
    DataCats project settings object

    Create with Project.new(path) or Project.load(path)
    """
    def __init__(self, name, target, datadir, ckan_version=None, port=None):
        self.name = name
        self.target = target
        self.datadir = datadir
        self.ckan_version = ckan_version
        self.port = int(port if port else self._choose_port())

    def save(self):
        """
        Save project settings into project directory
        """
        cp = SafeConfigParser()

        cp.add_section('datacats')
        cp.set('datacats', 'name', self.name)
        cp.set('datacats', 'ckan_version', self.ckan_version)
        cp.set('datacats', 'port', str(self.port))

        cp.add_section('passwords')
        for n in sorted(self.passwords):
            cp.set('passwords', n.lower(), self.passwords[n])
        with open(self.target + '/.datacats-project', 'w') as config:
            cp.write(config)

        self._update_saved_project_dir()

    def _update_saved_project_dir(self):
        """
        Store the last place we've seen this project so the user
        can use "datacats -p ..." to specify a project by name
        """
        with open(self.datadir + '/project-dir', 'w') as pdir:
            pdir.write(self.target)

    @classmethod
    def new(cls, path, ckan_version, port=None):
        """
        Return a Project object with settings for a new project.
        No directories or containers are created by this call.

        :params path: location for new project directory, may be relative
        :params ckan_version: release of CKAN to install
        :params port: preferred port for local instance

        Raises ProjectError if directories or project with same
        name already exits.
        """
        workdir, name = path_split(abspath(expanduser(path)))

        if not valid_name(name):
            raise ProjectError('Please choose a project name starting with a '
                'letter and including only lowercase letters and digits')
        if not isdir(workdir):
            raise ProjectError('Parent directory for project does not exist')

        datadir = expanduser('~/.datacats/' + name)
        target = workdir + '/' + name

        if isdir(datadir):
            raise ProjectError('Project data directory {0} already exists',
                (datadir,))
        if isdir(target):
            raise ProjectError('Project directory already exists')

        project = cls(name, target, datadir, ckan_version, port)
        project._generate_passwords()
        return project

    @classmethod
    def load(cls, project_name=None, data_only=False):
        """
        Return a Project object based on an existing project.

        :param project_name: exising project name or None to look in
            current or parent directories for project
        :param data_only: set to True to only load from data dir, not
            the project directory. used for purging project data.

        Raises ProjectError if project can't be found or if there is an
        error parsing the project information.
        """
        if project_name is None:
            wd = abspath(getcwd())
            while not exists(wd + '/.datacats-project'):
                oldwd = wd
                wd, ignore = path_split(wd)
                if wd == oldwd:
                    raise ProjectError(
                        'Project not found in current directory')
        else:
            datadir = expanduser('~/.datacats/' + project_name)
            if not isdir(datadir):
                raise ProjectError('No project found with that name')
            with open(datadir + '/project-dir') as pd:
                wd = pd.read()
            if not data_only and not exists(wd + '/.datacats-project'):
                raise ProjectError(
                    'Project data found but project directory is missing.'
                    ' Try again without "-p" from the new project directory'
                    ' location or remove this project data with'
                    ' "datacats purge"')

        if data_only and project_name:
            return cls(project_name, None, datadir)

        cp = SafeConfigParser()
        try:
            cp.read([wd + '/.datacats-project'])
        except ConfigParserError:
            raise ProjectError('Error reading project information')

        name = cp.get('datacats', 'name')
        datadir = expanduser('~/.datacats/' + name)
        ckan_version = cp.get('datacats', 'ckan_version')
        try:
            port = cp.getint('datacats', 'port')
        except NoOptionError:
            port = None
        passwords = {}
        for n in cp.options('passwords'):
            passwords[n.upper()] = cp.get('passwords', n)

        project = cls(name, wd, datadir, ckan_version, port)
        project.passwords = passwords

        if project_name is None:
            project._update_saved_project_dir()<commit_msg>allow using path to specify project<commit_after># Copyright 2014-2015 Boxkite Inc.

# This file is part of the DataCats package and is released under
# the terms of the GNU Affero General Public License version 3.0.
# See LICENSE.txt or http://www.fsf.org/licensing/licenses/agpl-3.0.html

from os.path import abspath, split as path_split, expanduser, isdir, exists
from os import makedirs, getcwd, remove
import subprocess
import shutil
import json
from string import uppercase, lowercase, digits
from random import SystemRandom
from sha import sha
from struct import unpack
from ConfigParser import (SafeConfigParser, Error as ConfigParserError,
    NoOptionError)

from datacats.validate import valid_name
from datacats.docker import (web_command, run_container, remove_container,
    inspect_container, is_boot2docker, data_only_container, docker_host,
    PortAllocatedError, container_logs)
from datacats.template import ckan_extension_template
from datacats.scripts import SCRIPTS_DIR, SHELL

class ProjectError(Exception):
    def __init__(self, message, format_args=()):
        self.message = message
        self.format_args = format_args
        super(ProjectError, self).__init__(message, format_args)

    def __str__(self):
        return self.message.format(*self.format_args)


class Project(object):
    """
    DataCats project settings object

    Create with Project.new(path) or Project.load(path)
    """
    def __init__(self, name, target, datadir, ckan_version=None, port=None):
        self.name = name
        self.target = target
        self.datadir = datadir
        self.ckan_version = ckan_version
        self.port = int(port if port else self._choose_port())

    def save(self):
        """
        Save project settings into project directory
        """
        cp = SafeConfigParser()

        cp.add_section('datacats')
        cp.set('datacats', 'name', self.name)
        cp.set('datacats', 'ckan_version', self.ckan_version)
        cp.set('datacats', 'port', str(self.port))

        cp.add_section('passwords')
        for n in sorted(self.passwords):
            cp.set('passwords', n.lower(), self.passwords[n])
        with open(self.target + '/.datacats-project', 'w') as config:
            cp.write(config)

        self._update_saved_project_dir()

    def _update_saved_project_dir(self):
        """
        Store the last place we've seen this project so the user
        can use "datacats -p ..." to specify a project by name
        """
        with open(self.datadir + '/project-dir', 'w') as pdir:
            pdir.write(self.target)

    @classmethod
    def new(cls, path, ckan_version, port=None):
        """
        Return a Project object with settings for a new project.
        No directories or containers are created by this call.

        :params path: location for new project directory, may be relative
        :params ckan_version: release of CKAN to install
        :params port: preferred port for local instance

        Raises ProjectError if directories or project with same
        name already exits.
        """
        workdir, name = path_split(abspath(expanduser(path)))

        if not valid_name(name):
            raise ProjectError('Please choose a project name starting with a '
                'letter and including only lowercase letters and digits')
        if not isdir(workdir):
            raise ProjectError('Parent directory for project does not exist')

        datadir = expanduser('~/.datacats/' + name)
        target = workdir + '/' + name

        if isdir(datadir):
            raise ProjectError('Project data directory {0} already exists',
                (datadir,))
        if isdir(target):
            raise ProjectError('Project directory already exists')

        project = cls(name, target, datadir, ckan_version, port)
        project._generate_passwords()
        return project

    @classmethod
    def load(cls, project_name=None, data_only=False):
        """
        Return a Project object based on an existing project.

        :param project_name: exising project name, path or None to look in
            current or parent directories for project
        :param data_only: set to True to only load from data dir, not
            the project directory. used for purging project data.

        Raises ProjectError if project can't be found or if there is an
        error parsing the project information.
        """
        if project_name is None:
            project_name = '.'

        if valid_name(project_name) and isdir(
                expanduser('~/.datacats/' + project_name)):
            datadir = expanduser('~/.datacats/' + project_name)
            with open(datadir + '/project-dir') as pd:
                wd = pd.read()
            if not data_only and not exists(wd + '/.datacats-project'):
                raise ProjectError(
                    'Project data found but project directory is missing.'
                    ' Try again without "-p" from the new project directory'
                    ' location or remove this project data with'
                    ' "datacats purge"')
        else:
            wd = abspath(project_name)
            if not isdir(wd):
                raise ProjectError('No project found with that name')

            while not exists(wd + '/.datacats-project'):
                oldwd = wd
                wd, ignore = path_split(wd)
                if wd == oldwd:
                    raise ProjectError(
                        'Project not found in current directory')

        if data_only and project_name:
            return cls(project_name, None, datadir)

        cp = SafeConfigParser()
        try:
            cp.read([wd + '/.datacats-project'])
        except ConfigParserError:
            raise ProjectError('Error reading project information')

        name = cp.get('datacats', 'name')
        datadir = expanduser('~/.datacats/' + name)
        ckan_version = cp.get('datacats', 'ckan_version')
        try:
            port = cp.getint('datacats', 'port')
        except NoOptionError:
            port = None
        passwords = {}
        for n in cp.options('passwords'):
            passwords[n.upper()] = cp.get('passwords', n)

        project = cls(name, wd, datadir, ckan_version, port)
        project.passwords = passwords

        if project_name is None:
            project._update_saved_project_dir()
{'fraction_non_alphanumeric': 0.08717300245001186, 'fraction_numerical': 0.0022129139334545168, 'mean_word_length': 2.339667458432304, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '3783651', 'n_tokens_mistral': 3502, 'n_tokens_neox': 3372, 'n_words': 1118}