<commit_before># coding: utf-8
import json

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.urlresolvers import reverse
from django.http import HttpResponsePermanentRedirect, HttpResponse
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext

from .models import Author, Tag, Quote
from .forms import QuoteForm

PER_PAGE = 25

def index(request, page=1, format=None):
    featured_quote = Quote.objects.filter(featured=True).order_by('?')[:1][0]
    quotes = Quote.objects.all() \
                          .exclude(id=featured_quote.id) \
                          .order_by('-publish_date')
    quotes = __validates_pagination(page, Paginator(quotes, PER_PAGE))

    if format == 'json':
        data = {
            'hasNext': quotes.has_next(),
            'quotes': []
        }
        for quote in quotes:
            data['quotes'].append(quote.serialize())
        return HttpResponse(json.dumps(data), mimetype='application/json')
    else:
        title = "defprogramming: quotes about coding"
        description = "Quotes about programming, coding, computer science, " \
                      "debugging, software industry, startups and motivation."
        base_url_pagination = reverse('root')
        return render_to_response('quotes/index.html',
                                  locals(),
                                  context_instance=RequestContext(request))

def detail(request, uuid):
    quote = get_object_or_404(Quote, uuid=uuid)
    return render_to_response('quotes/detail.html',
                              locals(),
                              context_instance=RequestContext(request))

def quote_redirect(request, slug):
    quote = get_object_or_404(Quote, slug=slug)
    return HttpResponsePermanentRedirect(quote.get_absolute_url())

def random(request):
    quote = Quote.objects.order_by('?')[0]
    return render_to_response('quotes/detail.html',
                              locals(),
                              context_instance=RequestContext(request))

def authors(request):
    authors = Author.objects.all().order_by('name')
    title = "Listing all authors | defprogramming"
    description = "List of all the authors with quotes published. Quotes " \
                  "about programming, coding, software industry."
    return render_to_response('quotes/authors.html',
                              locals(),
                              context_instance=RequestContext(request))

def author_detail(request, slug, page=1):
    author = get_object_or_404(Author, slug=slug)
    quotes = author.quote_set.all().order_by('-publish_date')
    quotes = __validates_pagination(page, Paginator(quotes, PER_PAGE))
    authors = Author.objects.all().order_by('name')
    title = "Programming quotes by " + author.name + " | defprogramming"
    description = "Listing all programming quotes by " + author.name + \
                  ". Quotes about programming, coding, software industry."
    base_url_pagination = reverse('author', kwargs={'slug': slug})
    return render_to_response('quotes/author_detail.html',
                              locals(),
                              context_instance=RequestContext(request))

def tags(request):
    tags = Tag.objects.all().order_by('name')
    title = "Listing all tags | defprogramming"
    description = "Tags list. Quotes about programming, coding, software " \
                  "industry."
    return render_to_response('quotes/tags.html',
                              locals(),
                              context_instance=RequestContext(request))

def tag_detail(request, slug, page=1):
    tag = get_object_or_404(Tag, slug=slug)
    quotes = tag.quote_set.all().order_by('-publish_date')
    quotes = __validates_pagination(page, Paginator(quotes, PER_PAGE))
    tags = Tag.objects.all().order_by('name')
    title = "Programming quotes tagged under " + tag.name + " | defprogramming"
    description = "Listing all programming quotes tagged under " + tag.name + \
                  ". Quotes about programming, coding, software industry."
    base_url_pagination = reverse('tag', kwargs={'slug': slug})
    return render_to_response('quotes/tag_detail.html',
                              locals(),
                              context_instance=RequestContext(request))

def submit_quote(request):
    title = "Submit a quote | defprogramming"
    description = "Use this form to submit a quote. Please send only quotes " \
                  "about programming, coding, software industry."
    sent = False

    if request.method == 'POST':
        form = QuoteForm(request.POST)

        if form.is_valid():
            form.save(request.POST)
            sent = True

    else:
        form = QuoteForm()

    return render_to_response('quotes/submit_quote.html',
                              locals(),
                              context_instance=RequestContext(request))

def __validates_pagination(page, paginator):
    page = page or 1
    try:
        return paginator.page(page)
    except PageNotAnInteger:
        return paginator.page(1)
    except EmptyPage:
        return paginator.page(paginator.num_pages)
<commit_msg>Implements initial cache policy<commit_after># coding: utf-8
import json

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.urlresolvers import reverse
from django.http import HttpResponsePermanentRedirect, HttpResponse
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.views.decorators.cache import cache_page

from .models import Author, Tag, Quote
from .forms import QuoteForm

DEFAULT_CACHE_TIME = 60 * 15
PER_PAGE = 25

@cache_page(DEFAULT_CACHE_TIME)
def index(request, page=1, format=None):
    featured_quote = Quote.objects.filter(featured=True).order_by('?')[:1][0]
    quotes = Quote.objects.all() \
                          .exclude(id=featured_quote.id) \
                          .order_by('-publish_date')
    quotes = __validates_pagination(page, Paginator(quotes, PER_PAGE))

    if format == 'json':
        data = {
            'hasNext': quotes.has_next(),
            'quotes': []
        }
        for quote in quotes:
            data['quotes'].append(quote.serialize())
        return HttpResponse(json.dumps(data), mimetype='application/json')
    else:
        title = "defprogramming: quotes about coding"
        description = "Quotes about programming, coding, computer science, " \
                      "debugging, software industry, startups and motivation."
        base_url_pagination = reverse('root')
        return render_to_response('quotes/index.html',
                                  locals(),
                                  context_instance=RequestContext(request))

@cache_page(DEFAULT_CACHE_TIME)
def detail(request, uuid):
    quote = get_object_or_404(Quote, uuid=uuid)
    return render_to_response('quotes/detail.html',
                              locals(),
                              context_instance=RequestContext(request))

@cache_page(60 * 60)
def quote_redirect(request, slug):
    quote = get_object_or_404(Quote, slug=slug)
    return HttpResponsePermanentRedirect(quote.get_absolute_url())

def random(request):
    quote = Quote.objects.order_by('?')[0]
    return render_to_response('quotes/detail.html',
                              locals(),
                              context_instance=RequestContext(request))

@cache_page(DEFAULT_CACHE_TIME)
def authors(request):
    authors = Author.objects.all().order_by('name')
    title = "Listing all authors | defprogramming"
    description = "List of all the authors with quotes published. Quotes " \
                  "about programming, coding, software industry."
    return render_to_response('quotes/authors.html',
                              locals(),
                              context_instance=RequestContext(request))

@cache_page(DEFAULT_CACHE_TIME)
def author_detail(request, slug, page=1):
    author = get_object_or_404(Author, slug=slug)
    quotes = author.quote_set.all().order_by('-publish_date')
    quotes = __validates_pagination(page, Paginator(quotes, PER_PAGE))
    authors = Author.objects.all().order_by('name')
    title = "Programming quotes by " + author.name + " | defprogramming"
    description = "Listing all programming quotes by " + author.name + \
                  ". Quotes about programming, coding, software industry."
    base_url_pagination = reverse('author', kwargs={'slug': slug})
    return render_to_response('quotes/author_detail.html',
                              locals(),
                              context_instance=RequestContext(request))

@cache_page(DEFAULT_CACHE_TIME)
def tags(request):
    tags = Tag.objects.all().order_by('name')
    title = "Listing all tags | defprogramming"
    description = "Tags list. Quotes about programming, coding, software " \
                  "industry."
    return render_to_response('quotes/tags.html',
                              locals(),
                              context_instance=RequestContext(request))

@cache_page(DEFAULT_CACHE_TIME)
def tag_detail(request, slug, page=1):
    tag = get_object_or_404(Tag, slug=slug)
    quotes = tag.quote_set.all().order_by('-publish_date')
    quotes = __validates_pagination(page, Paginator(quotes, PER_PAGE))
    tags = Tag.objects.all().order_by('name')
    title = "Programming quotes tagged under " + tag.name + " | defprogramming"
    description = "Listing all programming quotes tagged under " + tag.name + \
                  ". Quotes about programming, coding, software industry."
    base_url_pagination = reverse('tag', kwargs={'slug': slug})
    return render_to_response('quotes/tag_detail.html',
                              locals(),
                              context_instance=RequestContext(request))

def submit_quote(request):
    title = "Submit a quote | defprogramming"
    description = "Use this form to submit a quote. Please send only quotes " \
                  "about programming, coding, software industry."
    sent = False

    if request.method == 'POST':
        form = QuoteForm(request.POST)

        if form.is_valid():
            form.save(request.POST)
            sent = True

    else:
        form = QuoteForm()

    return render_to_response('quotes/submit_quote.html',
                              locals(),
                              context_instance=RequestContext(request))

def __validates_pagination(page, paginator):
    page = page or 1
    try:
        return paginator.page(page)
    except PageNotAnInteger:
        return paginator.page(1)
    except EmptyPage:
        return paginator.page(paginator.num_pages)
<|endoftext|>


<commit_before>import os
import sys
import argparse
import scm.plams as plams

reader = plams.KFReader("test/CO2-HBH2-IRC.t21")
print(reader.read("General", "termination status"))

def main():
    """
        The main function.
        Reads input, sends it to a parser, then sets up the computation
    """
    parse_args()

def parse_args():
    """
        The argument parser
    """
    parser = argparse.ArgumentParser(description="Cheap PyFrag implementation")
    parser.formatter_class = argparse.RawDescriptionHelpFormatter
    parser.add_argument(
        "-n",
        "--nproc",
        default=24,
        type=int,
        help="Number of cores used for the computation",
    )
    parser.add_argument(
        "-t",
        "--walltime",
        default="24:00:00",
        type=str,
        help="Maximum time allowed for the computation",
    )
    parser.add_argument(
        "-m", "--memory", type=int, help="Amount of memory allowed for the computation"
    )
    parser.add_argument(
        "inputfile", type=str, nargs="+", help="The input file to submit"
    )

    try:
        args = parser.parse_args()
    except argparse.ArgumentError as error:
        print(str(error))  # Print something like "option -a not recognized"
        sys.exit(2)

    runvalues = dict.fromkeys(
        [
            "inputfile",
            "cores",
            "walltime",
            "memory",
            "chk",
            "oldchk",
            "rwf",
            "nproc_in_input",
            "memory_in_input",
        ]
    )
    # Get values from parser
    runvalues["inputfile"] = os.path.basename(args.inputfile[0])
    runvalues["cores"] = args.nproc
    runvalues["walltime"] = args.walltime
    if args.memory:
        runvalues["memory"] = args.memory

    # Initialize empty values
    runvalues["chk"] = set()
    runvalues["oldchk"] = set()
    runvalues["rwf"] = set()
    runvalues["nbo"] = False
    runvalues["nbo_basefilename"] = ""

    return runvalues
<commit_msg>Include stuff from previous implementation<commit_after>import os
import sys
import argparse
import logging
import scm.plams

def main():
    """
        The main function.
        Reads input, sends it to a parser, then sets up the computation
    """
    # Setup logging
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    stream_handler = logging.StreamHandler()
    stream_handler.setLevel(logging.DEBUG)

    formatter = logging.Formatter("%(asctime)s :: %(levelname)s :: %(message)s")
    stream_handler.setFormatter(formatter)

    logger.addHandler(stream_handler)

    # Parse command line arguments
    parse_args()
    scm.plams.read_molecules()

def parse_args():
    """
        The argument parser
    """
    logger = logging.getLogger()
    parser = argparse.ArgumentParser(
        description=help_description(), epilog=help_epilog()
    )
    parser.formatter_class = argparse.RawDescriptionHelpFormatter
    parser.add_argument(
        "-i", "--input_file", type=str, nargs=1, help="The input file to submit"
    )
    parser.add_argument(
        "-o",
        "--output_file",
        type=str,
        nargs=1,
        help="Output file in which to print the NBO charges",
    )
    parser.add_argument(
        "-f",
        "--functional",
        type=str,
        nargs="?",
        default="B3LYP-D3",
        help="Functional used for the computation, as B3LYP-D3 or M062X\n"
             "Hyphen will split into functional/dispersion parts when applicable",
    )
    parser.add_argument(
        "-b",
        "--basis_set",
        type=str,
        nargs="?",
        default="6-31G*",
        help="The basis set to use for all atoms",
    )
    parser.add_argument(
        "-d",
        "--data",
        type=str,
        nargs="*",
        help="Useful data to extract, such as bonds or angles\n"
             "Write as B 1 2 (bond between atoms 1 and 2), A 3 5 4 (Angle 3-5-4)\n"
             "or D 3 8 9 1 (Dihedral 3-8-9-1)",
    )
    parser.add_argument(
        "-r",
        "--fragment",
        type=int,
        nargs="*",
        help="List of atoms in one of the fragments to consider.\n"
             "If absent, fragmentation is not considered.\n"
             "If present, all listed atoms (as numbers in geometry) are used in\n"
             "Frag 1, while others are added to Frag 0.\n",
    )
    parser.add_argument(
        "-n",
        "--nbo",
        type=bool,
        default=False,
        nargs="?",
        help="Request NBO calculations at every point"
    )

    try:
        args = parser.parse_args()
    except argparse.ArgumentError as error:
        print(str(error))  # Print something like "option -a not recognized"
        sys.exit(2)

    run_values = dict.fromkeys(
        [
            "input_file",
            "output_file",
            "functional",
            "basis_set",
            "data",
            "fragment",
            "NBO"
        ]
    )
    # Get values from parser
    run_values["input_file"] = os.path.basename(args.input_file[0])
    run_values["output_file"] = os.path.basename(args.output_file[0])
    run_values["functional"] = args.functional
    run_values["basis_set"] = args.basis_set
    # Parse data to extract
    if args.data:
        bonds = []
        angles = []
        dihedrals = []
        iterator = iter(args.data)
        for i in iterator:
            if i == "B":
                bonds.append([int(next(iterator)) - 1, int(next(iterator)) - 1])
            if i == "A":
                angles.append(
                    [
                        int(next(iterator)) - 1,
                        int(next(iterator)) - 1,
                        int(next(iterator)) - 1,
                    ]
                )
            if i == "D":
                dihedrals.append(
                    [
                        int(next(iterator)) - 1,
                        int(next(iterator)) - 1,
                        int(next(iterator)) - 1,
                        int(next(iterator)) - 1,
                    ]
                )
        run_values["data"] = dict.fromkeys(["bonds", "angles", "dihedrals"])
        run_values["data"]["bonds"] = bonds
        run_values["data"]["angles"] = angles
        run_values["data"]["dihedrals"] = dihedrals
    logger.debug("Data to extract: %s", run_values["data"])

    # Parse fragmentation
    if args.fragment:
        run_values["frag1"] = args.fragment
        natoms = number_of_atoms(run_values["input_file"][0])
        atoms = range(1, natoms + 1)
        run_values["frag0"] = [atom for atom in atoms if atom not in run_values["frag1"]]
    else:
        run_values["frag1"] = []
        run_values["frag0"] = [
            atom for atom in range(1, number_of_atoms(run_values["input_file"][0] + 1))
        ]

    logger.debug("Fragment 0: %s", run_values["frag0"])
    logger.debug("Fragment 1: %s", run_values["frag1"])

    return run_values

def number_of_atoms(file):
    """Return number of atoms from TAPE21 file"""
    reader = ADFJob()

def help_description():
    """Return description of program for help message."""
    return "Help Description // To fill"

def help_epilog():
    """Return additionnal help message."""
    return "Help epilog // To Fill"

if __name__ == "__main__":
    main()
<|endoftext|>


<commit_before>import os, sys, shutil, distutils
from nose.tools import *

import logging
logger = logging.getLogger(__name__)

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from test.helpers.project import project
from qipipe.pipelines import qipipeline as qip
from qipipe.helpers.dicom_helper import iter_dicom
from qipipe.helpers import xnat_helper
from qipipe.staging import airc_collection as airc
from qipipe.helpers.xnat_helper import delete_subjects
from qipipe.staging.staging_helper import get_subjects
from qipipe.helpers.ast_config import read_config

ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..'))
"""The test parent directory."""

REG_CONF = os.path.join(ROOT, 'conf', 'registration.cfg')
"""The test registration configuration."""

RESULTS = os.path.join(ROOT, 'results', 'pipelines', 'qipipeline')
"""The test results directory."""

from nipype import config
cfg = dict(logging=dict(workflow_level='DEBUG', log_directory=RESULTS, log_to_file=True),
    execution=dict(crashdump_dir=RESULTS, create_report=False))
config.update_config(cfg)

class TestQIPipeline:
    """
    QIN Pipeline unit tests.

    Note:: a precondition for running this test is that the following environment
    variables are set:

    - ``QIN_BREAST_INPUT``: the input AIRC test Breast fixture parent directory to test

    - ``QIN_SARCOMA_INPUT``: the input AIRC test Sarcoma fixture parent directory to test
    The test directories must conform to the subject/visit/dicom directory patterns
    defined in :meth:`airc`.

    Note:: the modeling workflow is only executed if the ``fastfit`` executable is
    found.

    The recommended test input is three series for one visit from each collection.
    The pipeline is run serially, and takes app. two hours per visit on this input.
    """

    def setUp(self):
        shutil.rmtree(RESULTS, True)

    def tearDown(self):
        shutil.rmtree(RESULTS, True)

    def test_breast(self):
        fixture = os.getenv('QIN_BREAST_INPUT')
        if fixture:
            self._test_collection('Breast', fixture)
        else:
            logger.info("Skipping the QIN pipeline unit Breast test, since the QIN_BREAST_INPUT environment variable is not set.")

    def test_sarcoma(self):
        fixture = os.getenv('QIN_SARCOMA_INPUT')
        if fixture:
            self._test_collection('Sarcoma', fixture)
        else:
            logger.info("Skipping the QIN pipeline unit Sarcoma test, since the QIN_SARCOMA_INPUT environment variable is not set.")

    def _test_collection(self, collection, fixture):
        """
        Run the pipeline on the given collection and verify the following:

        - scans are created in XNAT

        :param collection: the AIRC collection name
        :param fixture: the test input
        """
        logger.debug("Testing the QIN pipeline on %s..." % fixture)

        # The staging destination and work area.
        dest = os.path.join(RESULTS, 'data')
        work = os.path.join(RESULTS, 'work')

        # The test registration configuration.
        reg_opts = dict(read_config(REG_CONF))

        # The pipeline options.
        opts = dict(registration=reg_opts)

        # Check whether the modeling workflow is executable.
        if not distutils.spawn.find_executable('fastfit'):
            opts['modeling'] = False

        # The test subject => directory dictionary.
        sbj_dir_dict = get_subjects(collection, fixture)
        # The test subjects.
        subjects = sbj_dir_dict.keys()
        # The test source directories.
        sources = sbj_dir_dict.values()

        with xnat_helper.connection() as xnat:
            # Delete any existing test subjects.
            delete_subjects(project(), *subjects)

            # Run the staging and registration workflows, but not the modeling.
            logger.debug("Executing the QIN pipeline...")
            reg_specs = qip.run(collection, *sources, dest=dest, work=work, **opts)

            # Verify the result.
            for sbj, sess, recon in reg_specs:
                reg_obj = xnat.get_reconstruction(project(), sbj, sess, recon)
                assert_true(reg_obj.exists(), "The %s %s reconstruction %s was not created in XNAT" % (sbj, sess, recon))

            # Delete the test subjects.
            delete_subjects(project(), *subjects)

if __name__ == "__main__":
    import nose

    nose.main(defaultTest=__name__)
<commit_msg>Get test data from QIN_DATA.<commit_after>import os, sys, shutil, distutils
from nose.tools import *

import logging
logger = logging.getLogger(__name__)

sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
from test.helpers.project import project
from qipipe.pipelines import qipipeline as qip
from qipipe.helpers.dicom_helper import iter_dicom
from qipipe.helpers import xnat_helper
from qipipe.staging import airc_collection as airc
from qipipe.helpers.xnat_helper import delete_subjects
from qipipe.staging.staging_helper import get_subjects
from qipipe.helpers.ast_config import read_config

ROOT = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', '..'))
"""The test parent directory."""

REG_CONF = os.path.join(ROOT, 'conf', 'registration.cfg')
"""The test registration configuration."""

RESULTS = os.path.join(ROOT, 'results', 'pipelines', 'qipipeline')
"""The test results directory."""

from nipype import config
cfg = dict(logging=dict(workflow_level='DEBUG', log_directory=RESULTS, log_to_file=True),
    execution=dict(crashdump_dir=RESULTS, create_report=False))
config.update_config(cfg)

class TestQIPipeline:
    """
    QIN Pipeline unit tests.

    Note:: a precondition for running this test is that the environment variable
    ``QIN_DATA`` is set to the AIRC ``HUANG_LAB`` mount point, e.g.::

        QIN_DATA=/Volumes/HUANG_LAB

    Note:: the modeling workflow is only executed if the ``fastfit`` executable is
    found.
    """

    def setUp(self):
        shutil.rmtree(RESULTS, True)

    def tearDown(self):
        shutil.rmtree(RESULTS, True)

    def test_breast(self):
        data = os.getenv('QIN_DATA')
        if data:
            fixture = os.path.join(RESULT, 'data', 'breast', 'BreastChemo3')
            os.makedirs(fixture)
            src = os.path.join(data, 'Breast_Chemo_Study', 'BreastChemo3', 'Visit1')
            dest = os.path.join(fixture, 'Visit1')
            os.symlink(src, dest)
            self._test_collection('Breast', fixture)
        else:
            logger.info("Skipping the QIN pipeline unit Breast test, since the QIN_DATA environment variable is not set.")

    def test_sarcoma(self):
        data = os.getenv('QIN_DATA')
        if data:
            fixture = os.path.join(RESULT, 'data', 'sarcoma', 'Subj_1')
            os.makedirs(fixture)
            src = os.path.join(data, 'Sarcoma', 'Subj_1', 'Visit1')
            dest = os.path.join(fixture, 'Visit1')
            os.symlink(src, dest)
            self._test_collection('Sarcoma', fixture)
        else:
            logger.info("Skipping the QIN pipeline unit Sarcoma test, since the QIN_DATA environment variable is not set.")

    def _test_collection(self, collection, fixture):
        """
        Run the pipeline on the given collection and verify that scans are created in XNAT.

        :param collection: the AIRC collection name
        :param fixture: the test input
        """
        logger.debug("Testing the QIN pipeline on %s..." % fixture)

        # The staging destination and work area.
        dest = os.path.join(RESULTS, 'data')
        work = os.path.join(RESULTS, 'work')

        # The test registration configuration.
        reg_opts = dict(read_config(REG_CONF))

        # The pipeline options.
        opts = dict(registration=reg_opts)

        # Check whether the modeling workflow is executable.
        if not distutils.spawn.find_executable('fastfit'):
            opts['modeling'] = False

        # The test subject => directory dictionary.
        sbj_dir_dict = get_subjects(collection, fixture)
        # The test subjects.
        subjects = sbj_dir_dict.keys()
        # The test source directories.
        sources = sbj_dir_dict.values()

        with xnat_helper.connection() as xnat:
            # Delete any existing test subjects.
            delete_subjects(project(), *subjects)

            # Run the staging and registration workflows, but not the modeling.
            logger.debug("Executing the QIN pipeline...")
            reg_specs = qip.run(collection, *sources, dest=dest, work=work, **opts)

            # Verify the result.
            for sbj, sess, recon in reg_specs:
                reg_obj = xnat.get_reconstruction(project(), sbj, sess, recon)
                assert_true(reg_obj.exists(), "The %s %s reconstruction %s was not created in XNAT" % (sbj, sess, recon))

            # Delete the test subjects.
            delete_subjects(project(), *subjects)

if __name__ == "__main__":
    import nose

    nose.main(defaultTest=__name__)
<|endoftext|>


<commit_before># This file is part of the GBI project.
# Copyright (C) 2013 Omniscale GmbH & Co. KG <http://omniscale.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Import features from GBI-Server coverage/area box into CouchDB layers.
"""

from collections import namedtuple
import json
import requests

from geobox.lib.couchdb import UnexpectedResponse, VectorCouchDB

feature = namedtuple('feature', ['id', 'rev', 'layer', 'properties', 'geometry'])

def feature_from_document(document):
    """
    Takes couchdb document and returns a feature if
    the document has a layer and geometry.
    """
    if 'geometry' not in document or 'layer' not in document:
        return None

    if document['_id'].startswith('schema_'):
        return None

    return feature(
        id=document['_id'],
        rev=document['_rev'],
        properties=document.get('properties', {}),
        geometry=document['geometry'],
        layer=document['layer'],
    )

class FeatureInserter(object):
    """
    Insert features into CouchDB at `url`. It inserts each feature
    into a database named after `feature.layer`. Existing features
    with the same `feature.id` are overwritten. Missing databases
    are created.
    """
    def __init__(self, url, prefix=None, auth=None):
        self.url = url
        self.prefix = prefix
        self.session = requests.Session()
        self.inserted_layers = set()
        if auth:
            self.session.auth = auth

    def from_source(self, source):
        for doc in source.load_records():
            f = feature_from_document(doc)
            if f:
                self.insert(f)

        for layer in self.inserted_layers:
            self._check_metadata_doc(layer, source)

    def insert(self, feature):
        couchdb_layer = '%s%s' % (self.prefix, feature.layer)
        couch = VectorCouchDB(self.url, couchdb_layer, couchdb_layer)
        feature_dict = {
            'geometry': feature.geometry,
            'properties': feature.properties,
            'type': 'Feature',
        }
        existing_feature = couch.get(feature.id)
        if existing_feature:
            feature_dict['_rev'] = existing_feature['_rev']

        couch.put(feature.id, feature_dict)

        self.inserted_layers.add(feature.layer)

    def _check_metadata_doc(self, layer, source):
        couchdb_layer = '%s%s' % (self.prefix, layer)
        couch = VectorCouchDB(self.url, couchdb_layer, couchdb_layer )

        source_md = source.load_record('schema_' + layer)
        md_doc = {
            'title': source_md.get('title', layer) if source_md else layer,
            'name': couchdb_layer,
            'layer': source_md.get('layer'),
            'type': 'GeoJSON',
        }
        couch.update_or_create_doc('metadata', md_doc)
<commit_msg>Use layer paremeter for layer property if given source have no schema document<commit_after># This file is part of the GBI project.
# Copyright (C) 2013 Omniscale GmbH & Co. KG <http://omniscale.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
Import features from GBI-Server coverage/area box into CouchDB layers.
"""

from collections import namedtuple
import json
import requests

from geobox.lib.couchdb import UnexpectedResponse, VectorCouchDB

feature = namedtuple('feature', ['id', 'rev', 'layer', 'properties', 'geometry'])

def feature_from_document(document):
    """
    Takes couchdb document and returns a feature if
    the document has a layer and geometry.
    """
    if 'geometry' not in document or 'layer' not in document:
        return None

    if document['_id'].startswith('schema_'):
        return None

    return feature(
        id=document['_id'],
        rev=document['_rev'],
        properties=document.get('properties', {}),
        geometry=document['geometry'],
        layer=document['layer'],
    )

class FeatureInserter(object):
    """
    Insert features into CouchDB at `url`. It inserts each feature
    into a database named after `feature.layer`. Existing features
    with the same `feature.id` are overwritten. Missing databases
    are created.
    """
    def __init__(self, url, prefix=None, auth=None):
        self.url = url
        self.prefix = prefix
        self.session = requests.Session()
        self.inserted_layers = set()
        if auth:
            self.session.auth = auth

    def from_source(self, source):
        for doc in source.load_records():
            f = feature_from_document(doc)
            if f:
                self.insert(f)

        for layer in self.inserted_layers:
            self._check_metadata_doc(layer, source)

    def insert(self, feature):
        couchdb_layer = '%s%s' % (self.prefix, feature.layer)
        couch = VectorCouchDB(self.url, couchdb_layer, couchdb_layer)
        feature_dict = {
            'geometry': feature.geometry,
            'properties': feature.properties,
            'type': 'Feature',
        }
        existing_feature = couch.get(feature.id)
        if existing_feature:
            feature_dict['_rev'] = existing_feature['_rev']

        couch.put(feature.id, feature_dict)

        self.inserted_layers.add(feature.layer)

    def _check_metadata_doc(self, layer, source):
        couchdb_layer = '%s%s' % (self.prefix, layer)
        couch = VectorCouchDB(self.url, couchdb_layer, couchdb_layer )

        source_md = source.load_record('schema_' + layer)
        md_doc = {
            'title': source_md.get('title', layer) if source_md else layer,
            'name': couchdb_layer,
            'layer': source_md.get('layer', layer) if source_md else layer,
            'type': 'GeoJSON',
        }
        couch.update_or_create_doc('metadata', md_doc)
<|endoftext|>


<commit_before>import logging
import sys
import traceback

from django.core.management.base import BaseCommand, CommandError
from django.core.management import call_command
from django.db import transaction

from corehq.dbaccessors.couchapps.all_docs import get_all_docs_with_doc_types, get_doc_count_by_type
from corehq.util.couchdb_management import couch_config
from corehq.util.django_migrations import skip_on_fresh_install

logger = logging.getLogger(__name__)

class PopulateSQLCommand(BaseCommand):
    """
        Base class for migrating couch docs to sql models.

        Adds a SQL object for any couch doc that doesn't yet have one.
        Override all methods that raise NotImplementedError and, optionoally, couch_db_slug.
    """
    AUTO_MIGRATE_ITEMS_LIMIT = 1000

    @classmethod
    def couch_db_slug(cls):
        # Override this if couch model was not stored in the main commcarehq database
        return None

    @classmethod
    def couch_doc_type(cls):
        raise NotImplementedError()

    @classmethod
    def sql_class(self):
        raise NotImplementedError()

    def update_or_create_sql_object(self, doc):
        """
        This should find and update the sql object that corresponds to the given doc,
        or create it if it doesn't yet exist. This method is responsible for saving
        the sql object.
        """
        raise NotImplementedError()

    @classmethod
    def diff_couch_and_sql(cls, couch, sql):
        """
        This should compare each attribute of the given couch document and sql object.
        Return a list of human-reaedable strings describing their differences, or None if the
        two are equivalent. The list may contain `None` or empty strings which will be filtered
        out before display.
        """
        raise NotImplementedError()

    @classmethod
    def get_filtered_diffs(cls, couch, sql):
        diffs = cls.diff_couch_and_sql(couch, sql)
        if isinstance(diffs, list):
            diffs = list(filter(None, diffs))
        return diffs

    @classmethod
    def get_diff_as_string(cls, couch, sql):
        diffs = cls.get_filtered_diffs(couch, sql)
        return "\n".join(diffs) if diffs else None

    @classmethod
    def diff_attr(cls, name, doc, obj, wrap_couch=None, wrap_sql=None, name_prefix=None):
        """
        Helper for diff_couch_and_sql
        """
        couch = doc.get(name, None) if doc else None
        sql = getattr(obj, name, None)
        if wrap_couch:
            couch = wrap_couch(couch) if couch is not None else None
        if wrap_sql:
            sql = wrap_sql(sql) if sql is not None else None
        return cls.diff_value(name, couch, sql, name_prefix)

    @classmethod
    def diff_value(cls, name, couch, sql, name_prefix=None):
        if couch != sql:
            name_prefix = "" if name_prefix is None else f"{name_prefix}."
            return f"{name_prefix}{name}: couch value {couch!r} != sql value {sql!r}"

    @classmethod
    def diff_lists(cls, name, docs, objects, attr_list=None):
        diffs = []
        if len(docs) != len(objects):
            diffs.append(f"{name}: {len(docs)} in couch != {len(objects)} in sql")
        else:
            for couch_field, sql_field in list(zip(docs, objects)):
                if attr_list:
                    for attr in attr_list:
                        diffs.append(cls.diff_attr(attr, couch_field, sql_field, name_prefix=name))
                else:
                    diffs.append(cls.diff_value(name, couch_field, sql_field))
        return diffs

    @classmethod
    def count_items_to_be_migrated(cls):
        couch_count = get_doc_count_by_type(cls.couch_db(), cls.couch_doc_type())
        sql_count = cls.sql_class().objects.count()
        return couch_count - sql_count

    @classmethod
    def commit_adding_migration(cls):
        """
        This should be the merge commit of the pull request that adds the command to the commcare-hq repository.
        If this is provided, the failure message in migrate_from_migration will instruct users to deploy this
        commit before running the command.
        """
        return None

    @classmethod
    @skip_on_fresh_install
    def migrate_from_migration(cls, apps, schema_editor):
        """
            Should only be called from within a django migration.
            Calls sys.exit on failure.
        """
        to_migrate = cls.count_items_to_be_migrated()
        print(f"Found {to_migrate} {cls.couch_doc_type()} documents to migrate.")

        migrated = to_migrate == 0
        if migrated:
            return

        command_name = cls.__module__.split('.')[-1]
        if to_migrate < cls.AUTO_MIGRATE_ITEMS_LIMIT:
            try:
                call_command(command_name)
                remaining = cls.count_items_to_be_migrated()
                if remaining != 0:
                    migrated = False
                    print(f"Automatic migration failed, {remaining} items remain to migrate.")
                else:
                    migrated = True
            except Exception:
                traceback.print_exc()
        else:
            print("Found {} items that need to be migrated.".format(to_migrate))
            print("Too many to migrate automatically.")

        if not migrated:
            print(f"""
                A migration must be performed before this environment can be upgraded to the latest version
                of CommCareHQ. This migration is run using the management command {command_name}.
            """)
            if cls.commit_adding_migration():
                print(f"""
                Run the following commands to run the migration and get up to date:

                    commcare-cloud <env> fab setup_limited_release --set code_branch={cls.commit_adding_migration()}

                    commcare-cloud <env> django-manage --release <release created by previous command> {command_name}

                    commcare-cloud <env> deploy commcare
                """)
            sys.exit(1)

    @classmethod
    def couch_db(cls):
        return couch_config.get_db(cls.couch_db_slug())

    def add_arguments(self, parser):
        parser.add_argument(
            '--verify-only',
            action='store_true',
            dest='verify_only',
            default=False,
            help="""
                Don't migrate anything, instead check if couch and sql data is identical.
            """,
        )
        parser.add_argument(
            '--skip-verify',
            action='store_true',
            dest='skip_verify',
            default=False,
            help="""
                Migrate even if verifcation fails. This is intended for usage only with
                models that don't support verification.
            """,
        )

    def handle(self, **options):
        verify_only = options.get("verify_only", False)
        skip_verify = options.get("skip_verify", False)

        if verify_only and skip_verify:
            raise CommandError("verify_only and skip_verify are mutually exclusive")

        self.doc_count = get_doc_count_by_type(self.couch_db(), self.couch_doc_type())
        self.diff_count = 0
        self.doc_index = 0

        logger.info("Found {} {} docs and {} {} models".format(
            self.doc_count,
            self.couch_doc_type(),
            self.sql_class().objects.count(),
            self.sql_class().__name__,
        ))
        for doc in get_all_docs_with_doc_types(self.couch_db(), [self.couch_doc_type()]):
            self.doc_index += 1
            if not verify_only:
                self._migrate_doc(doc)
            if not skip_verify:
                self._verify_doc(doc, exit=not verify_only)

        logger.info(f"Processed {self.doc_index} documents")
        if not skip_verify:
            logger.info(f"Found {self.diff_count} differences")

    def _verify_doc(self, doc, exit=True):
        try:
            couch_id_name = getattr(self.sql_class(), '_migration_couch_id_name', 'couch_id')
            obj = self.sql_class().objects.get(**{couch_id_name: doc["_id"]})
            diff = self.get_diff_as_string(doc, obj)
            if isinstance(diff, list):
                diffs = filter(None, diff)
                diff = "\n".join(diffs) if diffs else None
            if diff:
                logger.info(f"Doc {getattr(obj, couch_id_name)} has differences:\n{diff}")
                self.diff_count += 1
                if exit:
                    sys.exit(1)
        except self.sql_class().DoesNotExist:
            pass    # ignore, the difference in total object count has already been displayed

    def _migrate_doc(self, doc):
        logger.info("Looking at {} doc #{} of {} with id {}".format(
            self.couch_doc_type(),
            self.doc_index,
            self.doc_count,
            doc["_id"]
        ))
        with transaction.atomic():
            model, created = self.update_or_create_sql_object(doc)
            action = "Creating" if created else "Updated"
            logger.info("{} model for doc with id {}".format(action, doc["_id"]))
<commit_msg>fix bad merge<commit_after>import logging
import sys
import traceback

from django.core.management.base import BaseCommand, CommandError
from django.core.management import call_command
from django.db import transaction

from corehq.dbaccessors.couchapps.all_docs import get_all_docs_with_doc_types, get_doc_count_by_type
from corehq.util.couchdb_management import couch_config
from corehq.util.django_migrations import skip_on_fresh_install

logger = logging.getLogger(__name__)

class PopulateSQLCommand(BaseCommand):
    """
        Base class for migrating couch docs to sql models.

        Adds a SQL object for any couch doc that doesn't yet have one.
        Override all methods that raise NotImplementedError and, optionoally, couch_db_slug.
    """
    AUTO_MIGRATE_ITEMS_LIMIT = 1000

    @classmethod
    def couch_db_slug(cls):
        # Override this if couch model was not stored in the main commcarehq database
        return None

    @classmethod
    def couch_doc_type(cls):
        raise NotImplementedError()

    @classmethod
    def sql_class(self):
        raise NotImplementedError()

    def update_or_create_sql_object(self, doc):
        """
        This should find and update the sql object that corresponds to the given doc,
        or create it if it doesn't yet exist. This method is responsible for saving
        the sql object.
        """
        raise NotImplementedError()

    @classmethod
    def diff_couch_and_sql(cls, couch, sql):
        """
        This should compare each attribute of the given couch document and sql object.
        Return a list of human-reaedable strings describing their differences, or None if the
        two are equivalent. The list may contain `None` or empty strings which will be filtered
        out before display.
        """
        raise NotImplementedError()

    @classmethod
    def get_filtered_diffs(cls, couch, sql):
        diffs = cls.diff_couch_and_sql(couch, sql)
        if isinstance(diffs, list):
            diffs = list(filter(None, diffs))
        return diffs

    @classmethod
    def get_diff_as_string(cls, couch, sql):
        diffs = cls.get_filtered_diffs(couch, sql)
        return "\n".join(diffs) if diffs else None

    @classmethod
    def diff_attr(cls, name, doc, obj, wrap_couch=None, wrap_sql=None, name_prefix=None):
        """
        Helper for diff_couch_and_sql
        """
        couch = doc.get(name, None) if doc else None
        sql = getattr(obj, name, None)
        if wrap_couch:
            couch = wrap_couch(couch) if couch is not None else None
        if wrap_sql:
            sql = wrap_sql(sql) if sql is not None else None
        return cls.diff_value(name, couch, sql, name_prefix)

    @classmethod
    def diff_value(cls, name, couch, sql, name_prefix=None):
        if couch != sql:
            name_prefix = "" if name_prefix is None else f"{name_prefix}."
            return f"{name_prefix}{name}: couch value {couch!r} != sql value {sql!r}"

    @classmethod
    def diff_lists(cls, name, docs, objects, attr_list=None):
        diffs = []
        if len(docs) != len(objects):
            diffs.append(f"{name}: {len(docs)} in couch != {len(objects)} in sql")
        else:
            for couch_field, sql_field in list(zip(docs, objects)):
                if attr_list:
                    for attr in attr_list:
                        diffs.append(cls.diff_attr(attr, couch_field, sql_field, name_prefix=name))
                else:
                    diffs.append(cls.diff_value(name, couch_field, sql_field))
        return diffs

    @classmethod
    def count_items_to_be_migrated(cls):
        couch_count = get_doc_count_by_type(cls.couch_db(), cls.couch_doc_type())
        sql_count = cls.sql_class().objects.count()
        return couch_count - sql_count

    @classmethod
    def commit_adding_migration(cls):
        """
        This should be the merge commit of the pull request that adds the command to the commcare-hq repository.
        If this is provided, the failure message in migrate_from_migration will instruct users to deploy this
        commit before running the command.
        """
        return None

    @classmethod
    @skip_on_fresh_install
    def migrate_from_migration(cls, apps, schema_editor):
        """
            Should only be called from within a django migration.
            Calls sys.exit on failure.
        """
        to_migrate = cls.count_items_to_be_migrated()
        print(f"Found {to_migrate} {cls.couch_doc_type()} documents to migrate.")

        migrated = to_migrate == 0
        if migrated:
            return

        command_name = cls.__module__.split('.')[-1]
        if to_migrate < cls.AUTO_MIGRATE_ITEMS_LIMIT:
            try:
                call_command(command_name)
                remaining = cls.count_items_to_be_migrated()
                if remaining != 0:
                    migrated = False
                    print(f"Automatic migration failed, {remaining} items remain to migrate.")
                else:
                    migrated = True
            except Exception:
                traceback.print_exc()
        else:
            print("Found {} items that need to be migrated.".format(to_migrate))
            print("Too many to migrate automatically.")

        if not migrated:
            print(f"""
                A migration must be performed before this environment can be upgraded to the latest version
                of CommCareHQ. This migration is run using the management command {command_name}.
            """)
            if cls.commit_adding_migration():
                print(f"""
                Run the following commands to run the migration and get up to date:

                    commcare-cloud <env> fab setup_limited_release --set code_branch={cls.commit_adding_migration()}

                    commcare-cloud <env> django-manage --release <release created by previous command> {command_name}

                    commcare-cloud <env> deploy commcare
                """)
            sys.exit(1)

    @classmethod
    def couch_db(cls):
        return couch_config.get_db(cls.couch_db_slug())

    def add_arguments(self, parser):
        parser.add_argument(
            '--verify-only',
            action='store_true',
            dest='verify_only',
            default=False,
            help="""
                Don't migrate anything, instead check if couch and sql data is identical.
            """,
        )
        parser.add_argument(
            '--skip-verify',
            action='store_true',
            dest='skip_verify',
            default=False,
            help="""
                Migrate even if verifcation fails. This is intended for usage only with
                models that don't support verification.
            """,
        )

    def handle(self, **options):
        verify_only = options.get("verify_only", False)
        skip_verify = options.get("skip_verify", False)

        if verify_only and skip_verify:
            raise CommandError("verify_only and skip_verify are mutually exclusive")

        self.doc_count = get_doc_count_by_type(self.couch_db(), self.couch_doc_type())
        self.diff_count = 0
        self.doc_index = 0

        logger.info("Found {} {} docs and {} {} models".format(
            self.doc_count,
            self.couch_doc_type(),
            self.sql_class().objects.count(),
            self.sql_class().__name__,
        ))
        for doc in get_all_docs_with_doc_types(self.couch_db(), [self.couch_doc_type()]):
            self.doc_index += 1
            if not verify_only:
                self._migrate_doc(doc)
            if not skip_verify:
                self._verify_doc(doc, exit=not verify_only)

        logger.info(f"Processed {self.doc_index} documents")
        if not skip_verify:
            logger.info(f"Found {self.diff_count} differences")

    def _verify_doc(self, doc, exit=True):
        try:
            couch_id_name = getattr(self.sql_class(), '_migration_couch_id_name', 'couch_id')
            obj = self.sql_class().objects.get(**{couch_id_name: doc["_id"]})
            diff = self.get_diff_as_string(doc, obj)
            if diff:
                logger.info(f"Doc {getattr(obj, couch_id_name)} has differences:\n{diff}")
                self.diff_count += 1
                if exit:
                    sys.exit(1)
        except self.sql_class().DoesNotExist:
            pass    # ignore, the difference in total object count has already been displayed

    def _migrate_doc(self, doc):
        logger.info("Looking at {} doc #{} of {} with id {}".format(
            self.couch_doc_type(),
            self.doc_index,
            self.doc_count,
            doc["_id"]
        ))
        with transaction.atomic():
            model, created = self.update_or_create_sql_object(doc)
            action = "Creating" if created else "Updated"
            logger.info("{} model for doc with id {}".format(action, doc["_id"]))
<|endoftext|>


<commit_before>import random
import string

script = open('Hamlet.txt', 'r')
text = script.read()
script.close()

monkeyTyped = ''
lengthRecord = 0

while len(monkeyTyped) < len(text) - 1:
    monkeyTyped = monkeyTyped + (random.choice(string.ascii_lowercase + ' '))
    if monkeyTyped[len(monkeyTyped) - 1] == text[len(monkeyTyped) - 1]:
        if len(monkeyTyped) > lengthRecord:
            print monkeyTyped
            lengthRecord = len(monkeyTyped)
    else:
        monkeyTyped = ''
print "Done. I'll get myself a banana."
<commit_msg>More information<commit_after>import random
import string

script = open('Hamlet.txt', 'r')
text = script.read()
script.close()

monkeyTyped = ''
lengthRecord = 0
strokes = 0

while len(monkeyTyped) < len(text) - 1:
    monkeyTyped = monkeyTyped + (random.choice(string.ascii_lowercase + ' '))
    strokes = strokes + 1
    if monkeyTyped[len(monkeyTyped) - 1] == text[len(monkeyTyped) - 1]:
        if len(monkeyTyped) > lengthRecord:
            print monkeyTyped
            lengthRecord = len(monkeyTyped)
    else:
        monkeyTyped = ''

    if strokes % 100000 == 0:
        print text[0:lengthRecord] + '\nafter ' + str(strokes) + ' keystrokes'
print "Done. I'll get myself a banana."
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

from base_resources import BaseResources
import logging
from utils import setup_logger, filter_resources, tag_with_name_with_suffix

class Instances(BaseResources):
    """Instances representation
    """

    def __init__(self, ec2, ec2_client, tag_base_name, **kwargs):
        """Constructor

        Args:
            ec2 (object): Aws Ec2 session
            ec2_client (object): Aws ec2 session
            tag_base_name (string): Tag base name
            **kwargs: Multiple arguments

        Raises:
            TypeError: Description
        """
        BaseResources.__init__(self, ec2, ec2_client, tag_base_name)
        log_level = kwargs.pop("log_level", logging.WARNING)
        boto_log_level = kwargs.pop("boto_log_level", logging.WARNING)

        if kwargs:
            raise TypeError("Unexpected **kwargs: %r" % kwargs)
        self.logger = setup_logger(__name__, log_level, boto_log_level)

    def terminate(self):
        """Terminate instances
        """
        aws_instances_ids = []
        aws_instances = self.ec2.instances.filter(Filters=[
            {
                "Name": "tag:Name",
                "Values": [self.tag_base_name + '-*']
            },
            {
                "Name": "instance-state-name",
                "Values": ['pending', 'running', 'shutting-down', 'stopping', 'stopped']
            },
        ])

        for aws_instance in aws_instances:
            aws_instances_ids.append(aws_instance.id)

        aws_instances_ids_str = str(aws_instances_ids).strip('[]')
        if aws_instances_ids:
            self.logger.info(
                "Terminating instances %s. Please wait",
                aws_instances_ids_str
            )

            self.ec2_client.terminate_instances(
                InstanceIds=aws_instances_ids
            )

            waiter = self.ec2_client.get_waiter('instance_terminated')
            waiter.wait(InstanceIds=aws_instances_ids)

            self.logger.info(
                "Instances %s are now terminated",
                aws_instances_ids_str
            )

    def create(self, instances_groups_config, vpcs_config):
        """Create instances

        Args:
            instances_groups_config (dict): Instances groups config
            vpcs_config (dict): Vpcs config
        """
        instances_config = []
        user_data = "#!/bin/bash\n"
        for instance_group in instances_groups_config:
            for instance_index, instance in enumerate(instance_group['Instances']):
                instance_config = {
                    'ImageId': instance_group['ImageId'],
                    'MinCount': 1,
                    'MaxCount': 1,
                    'InstanceType': instance_group['InstanceType'],
                    'DisableApiTermination': False,
                    'InstanceInitiatedShutdownBehavior': 'terminate',
                    'NetworkInterfaces': [],
                }

                for index, eni in enumerate(instance['NetworkInterfaces']):
                    found_eni = filter_resources(self.ec2.network_interfaces, "tag-value", eni["uid"])
                    if found_eni:
                        instance_config['NetworkInterfaces'].append({
                            'NetworkInterfaceId': found_eni[0].id,
                            'DeviceIndex': index
                        })

                    if index > 0:
                        user_data += "\n\nsudo bash -c \"echo 'auto eth{0}' >> /etc/network/interfaces\"\n" \
                            "sudo bash -c \"echo 'iface eth{0} inet dhcp' >> /etc/network/interfaces\"\n" \
                            "sudo ifup eth{0}\n" \
                            "sudo bash -c \"echo '40{0} eth{0}_rt' >> /etc/iproute2/rt_tables\"\n".format(index)

                    for private_ip_address in found_eni[0].private_ip_addresses:
                        if not private_ip_address['Primary']:
                            user_data += "\n# Add the primary ip address to the network interface\n"
                            user_data += "sudo ip addr add {0}{1} dev eth{2}\n".format(
                                private_ip_address['PrivateIpAddress'], instance['SubnetCidrSuffix'], index
                            )
                        if index > 0:
                            user_data += "\n# Add an ip rule to a routing table\n"
                            user_data += "sudo ip rule add from {0} lookup eth{1}_rt\n".format(
                                private_ip_address['PrivateIpAddress'],
                                index
                            )

                    if index > 0:
                        user_data += "\n# Add a route\n"
                        user_data += "sudo ip route add default via {0} dev " \
                            "eth{1} table eth{1}_rt\n".format(instance['GatewayIP'], index)

                instance_config['UserData'] = user_data

                aws_reservation = self.ec2_client.run_instances(**instance_config)
                aws_instance_config = aws_reservation['Instances'][0]
                aws_instance = self.ec2.Instance(aws_instance_config['InstanceId'])
                tag_with_name_with_suffix(aws_instance, "i", instance_index, self.tag_base_name)
                instance_config['InstanceId'] = aws_instance_config['InstanceId']
                instances_config.append(instance_config)

        return instances_config
<commit_msg>Add a method to get all ip addresses for running proxies<commit_after># -*- coding: utf-8 -*-

from base_resources import BaseResources
import logging
from utils import setup_logger, filter_resources, tag_with_name_with_suffix

class Instances(BaseResources):
    """Instances representation
    """

    def __init__(self, ec2, ec2_client, tag_base_name, **kwargs):
        """Constructor

        Args:
            ec2 (object): Aws Ec2 session
            ec2_client (object): Aws ec2 session
            tag_base_name (string): Tag base name
            **kwargs: Multiple arguments

        Raises:
            TypeError: Description
        """
        BaseResources.__init__(self, ec2, ec2_client, tag_base_name)
        log_level = kwargs.pop("log_level", logging.WARNING)
        boto_log_level = kwargs.pop("boto_log_level", logging.WARNING)

        if kwargs:
            raise TypeError("Unexpected **kwargs: %r" % kwargs)
        self.logger = setup_logger(__name__, log_level, boto_log_level)

    def terminate(self):
        """Terminate instances
        """
        aws_instances_ids = []
        aws_instances = self.ec2.instances.filter(Filters=[
            {
                "Name": "tag:Name",
                "Values": [self.tag_base_name + '-*']
            },
            {
                "Name": "instance-state-name",
                "Values": ['pending', 'running', 'shutting-down', 'stopping', 'stopped']
            },
        ])

        for aws_instance in aws_instances:
            aws_instances_ids.append(aws_instance.id)

        aws_instances_ids_str = str(aws_instances_ids).strip('[]')
        if aws_instances_ids:
            self.logger.info(
                "Terminating instances %s. Please wait",
                aws_instances_ids_str
            )

            self.ec2_client.terminate_instances(
                InstanceIds=aws_instances_ids
            )

            waiter = self.ec2_client.get_waiter('instance_terminated')
            waiter.wait(InstanceIds=aws_instances_ids)

            self.logger.info(
                "Instances %s are now terminated",
                aws_instances_ids_str
            )

    def get_running_proxies_ips(self, silent=False):
        """Get the public and private ips of all running proxy instances

        Args:
            silent (bool, optional): Silent

        Returns:
            Dict: Dictionnary of tuple public/private ips
        """
        filter = [
            {
                "Name": "tag:Name",
                "Values": [self.tag_base_name + '-*']
            },
            {
                "Name": "instance-state-name",
                "Values": ['running']
            },
        ]

        if not silent:
            print "Waiting for all proxies to be running..."

        waiter = self.ec2_client.get_waiter('instance_running')
        waiter.wait(Filters=filter)

        instances = self.ec2.instances.filter(Filters=filter)
        ips = []
        for instance in list(instances):
            for eni in instance.network_interfaces_attribute:
                for private_ip_addresses in eni['PrivateIpAddresses']:
                    ips.append(
                        (private_ip_addresses['Association']['PublicIp'], private_ip_addresses['PrivateIpAddress'])
                    )

        return ips

    def create(self, instances_groups_config, vpcs_config):
        """Create instances

        Args:
            instances_groups_config (dict): Instances groups config
            vpcs_config (dict): Vpcs config
        """
        instances_config = []
        user_data = "#!/bin/bash\n"
        for instance_group in instances_groups_config:
            for instance_index, instance in enumerate(instance_group['Instances']):
                instance_config = {
                    'ImageId': instance_group['ImageId'],
                    'MinCount': 1,
                    'MaxCount': 1,
                    'InstanceType': instance_group['InstanceType'],
                    'DisableApiTermination': False,
                    'InstanceInitiatedShutdownBehavior': 'terminate',
                    'NetworkInterfaces': [],
                }

                for index, eni in enumerate(instance['NetworkInterfaces']):
                    found_eni = filter_resources(self.ec2.network_interfaces, "tag-value", eni["uid"])
                    if found_eni:
                        instance_config['NetworkInterfaces'].append({
                            'NetworkInterfaceId': found_eni[0].id,
                            'DeviceIndex': index
                        })

                    if index > 0:
                        user_data += "\n\nsudo bash -c \"echo 'auto eth{0}' >> /etc/network/interfaces\"\n" \
                            "sudo bash -c \"echo 'iface eth{0} inet dhcp' >> /etc/network/interfaces\"\n" \
                            "sudo ifup eth{0}\n" \
                            "sudo bash -c \"echo '40{0} eth{0}_rt' >> /etc/iproute2/rt_tables\"\n".format(index)

                    for private_ip_address in found_eni[0].private_ip_addresses:
                        if not private_ip_address['Primary']:
                            user_data += "\n# Add the primary ip address to the network interface\n"
                            user_data += "sudo ip addr add {0}{1} dev eth{2}\n".format(
                                private_ip_address['PrivateIpAddress'], instance['SubnetCidrSuffix'], index
                            )
                        if index > 0:
                            user_data += "\n# Add an ip rule to a routing table\n"
                            user_data += "sudo ip rule add from {0} lookup eth{1}_rt\n".format(
                                private_ip_address['PrivateIpAddress'],
                                index
                            )

                    if index > 0:
                        user_data += "\n# Add a route\n"
                        user_data += "sudo ip route add default via {0} dev " \
                            "eth{1} table eth{1}_rt\n".format(instance['GatewayIP'], index)

                instance_config['UserData'] = user_data

                aws_reservation = self.ec2_client.run_instances(**instance_config)
                aws_instance_config = aws_reservation['Instances'][0]
                aws_instance = self.ec2.Instance(aws_instance_config['InstanceId'])
                tag_with_name_with_suffix(aws_instance, "i", instance_index, self.tag_base_name)
                instance_config['InstanceId'] = aws_instance_config['InstanceId']
                instances_config.append(instance_config)

        return instances_config
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from datetime import datetime
from decimal import Decimal

from django.db import models
from django.db.models import Q
from django.db.models.signals import post_save

from django.contrib.auth.models import User

from markitup.fields import MarkupField

from symposion.proposals.models import ProposalBase
from symposion.schedule.models import Presentation

class ProposalScoreExpression(object):

    def as_sql(self, qn, connection=None):
        sql = "((3 * plus_one + plus_zero) - (minus_zero + 3 * minus_one))"
        return sql, []

    def prepare_database_save(self, unused):
        return self

class Votes(object):
    PLUS_ONE = "+1"
    PLUS_ZERO = "+0"
    MINUS_ZERO = u"−0"
    MINUS_ONE = u"−1"

    CHOICES = [
        (PLUS_ONE, u"+1 — Good proposal and I will argue for it to be accepted."),
        (PLUS_ZERO, u"+0 — OK proposal, but I will not argue for it to be accepted."),
        (MINUS_ZERO, u"−0 — Weak proposal, but I will not argue strongly against acceptance."),
        (MINUS_ONE, u"−1 — Serious issues and I will argue to reject this proposal."),
    ]
VOTES = Votes()

class ReviewAssignment(models.Model):
    AUTO_ASSIGNED_INITIAL = 0
    OPT_IN = 1
    AUTO_ASSIGNED_LATER = 2

    ORIGIN_CHOICES = [
        (AUTO_ASSIGNED_INITIAL, "auto-assigned, initial"),
        (OPT_IN, "opted-in"),
        (AUTO_ASSIGNED_LATER, "auto-assigned, later"),
    ]

    proposal = models.ForeignKey("proposals.ProposalBase")
    user = models.ForeignKey(User)

    origin = models.IntegerField(choices=ORIGIN_CHOICES)

    assigned_at = models.DateTimeField(default=datetime.now)
    opted_out = models.BooleanField()

    @classmethod
    def create_assignments(cls, proposal, origin=AUTO_ASSIGNED_INITIAL):
        speakers = [proposal.speaker] + list(proposal.additional_speakers.all())
        reviewers = User.objects.exclude(
            pk__in=[
                speaker.user_id
                for speaker in speakers
                if speaker.user_id is not None
            ]
        ).filter(
            groups__name="reviewers",
        ).filter(
            Q(reviewassignment__opted_out=False) | Q(reviewassignment=None)
        ).annotate(
            num_assignments=models.Count("reviewassignment")
        ).order_by(
            "num_assignments",
        )
        for reviewer in reviewers[:3]:
            cls._default_manager.create(
                proposal=proposal,
                user=reviewer,
                origin=origin,
            )

class ProposalMessage(models.Model):
    proposal = models.ForeignKey("proposals.ProposalBase", related_name="messages")
    user = models.ForeignKey(User)

    message = MarkupField()
    submitted_at = models.DateTimeField(default=datetime.now, editable=False)

    class Meta:
        ordering = ["submitted_at"]

class Review(models.Model):
    VOTES = VOTES

    proposal = models.ForeignKey("proposals.ProposalBase", related_name="reviews")
    user = models.ForeignKey(User)

    # No way to encode "-0" vs. "+0" into an IntegerField, and I don't feel
    # like some complicated encoding system.
    vote = models.CharField(max_length=2, blank=True, choices=VOTES.CHOICES)
    comment = MarkupField()
    submitted_at = models.DateTimeField(default=datetime.now, editable=False)

    def save(self, **kwargs):
        if self.vote:
            vote, created = LatestVote.objects.get_or_create(
                proposal = self.proposal,
                user = self.user,
                defaults = dict(
                    vote = self.vote,
                    submitted_at = self.submitted_at,
                )
            )
            if not created:
                LatestVote.objects.filter(pk=vote.pk).update(vote=self.vote)
                self.proposal.result.update_vote(self.vote, previous=vote.vote)
            else:
                self.proposal.result.update_vote(self.vote)
        super(Review, self).save(**kwargs)

    def delete(self):
        model = self.__class__
        user_reviews = model._default_manager.filter(
            proposal=self.proposal,
            user=self.user,
        )
        # find previous vote before self
        try:
            previous = user_reviews.filter(submitted_at__lt=self.submitted_at).order_by("-submitted_at")[0]
        except IndexError:
            # did not find a previous which means this must be the only one.
            # treat it as a last, but delete the latest vote.
            self.proposal.result.update_vote(self.vote, removal=True)
            lv = LatestVote.objects.filter(proposal=self.proposal, user=self.user)
            lv.delete()
        else:
            # handle that we've found a previous vote
            # check if self is the last vote
            if self == user_reviews.order_by("-submitted_at")[0]:
                # self is the latest which means we need to treat as last.
                # revert the latest vote to previous vote.
                self.proposal.result.update_vote(self.vote, previous=previous.vote, removal=True)
                lv = LatestVote.objects.filter(proposal=self.proposal, user=self.user)
                lv.update(
                    vote=previous.vote,
                    submitted_at=previous.submitted_at,
                )
            else:
                # self is not the latest so we just need to decrement the
                # comment count
                self.proposal.result.comment_count = models.F("comment_count") - 1
                self.proposal.result.save()
        # in all cases we need to delete the review; let's do it!
        super(Review, self).delete()

    def css_class(self):
        return {
            self.VOTES.PLUS_ONE: "plus-one",
            self.VOTES.PLUS_ZERO: "plus-zero",
            self.VOTES.MINUS_ZERO: "minus-zero",
            self.VOTES.MINUS_ONE: "minus-one",
        }[self.vote]

    @property
    def section(self):
        return self.proposal.kind.section.slug

class LatestVote(models.Model):
    VOTES = VOTES

    proposal = models.ForeignKey("proposals.ProposalBase", related_name="votes")
    user = models.ForeignKey(User)

    # No way to encode "-0" vs. "+0" into an IntegerField, and I don't feel
    # like some complicated encoding system.
    vote = models.CharField(max_length=2, choices=VOTES.CHOICES)
    submitted_at = models.DateTimeField(default=datetime.now, editable=False)

    class Meta:
        unique_together = [("proposal", "user")]

    def css_class(self):
        return {
            self.VOTES.PLUS_ONE: "plus-one",
            self.VOTES.PLUS_ZERO: "plus-zero",
            self.VOTES.MINUS_ZERO: "minus-zero",
            self.VOTES.MINUS_ONE: "minus-one",
        }[self.vote]

class ProposalResult(models.Model):
    proposal = models.OneToOneField("proposals.ProposalBase", related_name="result")
    score = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal("0.00"))
    comment_count = models.PositiveIntegerField(default=1)
    vote_count = models.PositiveIntegerField(default=1)
    plus_one = models.PositiveIntegerField(default=0)
    plus_zero = models.PositiveIntegerField(default=0)
    minus_zero = models.PositiveIntegerField(default=0)
    minus_one = models.PositiveIntegerField(default=0)
    accepted = models.NullBooleanField(choices=[
        (True, "accepted"),
        (False, "rejected"),
        (None, "undecided"),
    ], default=None)
    status = models.CharField(max_length=20, choices=[
        ("accepted", "accepted"),
        ("rejected", "rejected"),
        ("undecided", "undecided"),
        ("standby", "standby"),
    ], default="undecided")

    @classmethod
    def full_calculate(cls):
        for proposal in ProposalBase.objects.all():
            result, created = cls._default_manager.get_or_create(proposal=proposal)
            result.comment_count = Review.objects.filter(proposal=proposal).count()
            result.vote_count = LatestVote.objects.filter(proposal=proposal).count()
            result.plus_one = LatestVote.objects.filter(
                proposal = proposal,
                vote = VOTES.PLUS_ONE
            ).count()
            result.plus_zero = LatestVote.objects.filter(
                proposal = proposal,
                vote = VOTES.PLUS_ZERO
            ).count()
            result.minus_zero = LatestVote.objects.filter(
                proposal = proposal,
                vote = VOTES.MINUS_ZERO
            ).count()
            result.minus_one = LatestVote.objects.filter(
                proposal = proposal,
                vote = VOTES.MINUS_ONE
            ).count()
            result.save()
            cls._default_manager.filter(pk=result.pk).update(score=ProposalScoreExpression())

    def update_vote(self, vote, previous=None, removal=False):
        mapping = {
            VOTES.PLUS_ONE: "plus_one",
            VOTES.PLUS_ZERO: "plus_zero",
            VOTES.MINUS_ZERO: "minus_zero",
            VOTES.MINUS_ONE: "minus_one",
        }
        if previous:
            if previous == vote:
                return
            if removal:
                setattr(self, mapping[previous], models.F(mapping[previous]) + 1)
            else:
                setattr(self, mapping[previous], models.F(mapping[previous]) - 1)
        else:
            if removal:
                self.vote_count = models.F("vote_count") - 1
            else:
                self.vote_count = models.F("vote_count") + 1
        if removal:
            setattr(self, mapping[vote], models.F(mapping[vote]) - 1)
            self.comment_count = models.F("comment_count") - 1
        else:
            setattr(self, mapping[vote], models.F(mapping[vote]) + 1)
            self.comment_count = models.F("comment_count") + 1
        self.save()
        model = self.__class__
        model._default_manager.filter(pk=self.pk).update(score=ProposalScoreExpression())

class Comment(models.Model):
    proposal = models.ForeignKey("proposals.ProposalBase", related_name="comments")
    commenter = models.ForeignKey(User)
    text = MarkupField()

    # Or perhaps more accurately, can the user see this comment.
    public = models.BooleanField(choices=[
        (True, "public"),
        (False, "private"),
    ])
    commented_at = models.DateTimeField(default=datetime.now)

class NotificationTemplate(models.Model):

    label = models.CharField(max_length=100)
    from_address = models.EmailField()
    subject = models.CharField(max_length=100)
    body = models.TextField()

class ResultNotification(models.Model):

    proposal = models.ForeignKey("proposals.ProposalBase", related_name="notifications")
    template = models.ForeignKey(NotificationTemplate, null=True, blank=True, on_delete=models.SET_NULL)
    timestamp = models.DateTimeField(default=datetime.now)
    to_address = models.EmailField()
    from_address = models.EmailField()
    subject = models.CharField(max_length=100)
    body = models.TextField()

    @property
    def email_args(self):
        return (self.subject, self.body, self.from_address, [self.to_address])

def promote_proposal(proposal):
    print "promote", proposal
    if hasattr(proposal, "presentation") and proposal.presentation:
        # already promoted
        presentation = proposal.presentation
    else:
        presentation = Presentation(
            title = proposal.title,
            description = proposal.description,
            abstract = proposal.abstract,
            speaker = proposal.speaker,
            section = proposal.section,
            _proposal = proposal,
        )
        presentation.save()
        for speaker in proposal.additional_speakers.all():
            presentation.additional_speakers.add(speaker)
            presentation.save()

    return presentation

def unpromote_proposal(proposal):
    print "unpromote"
    if hasattr(proposal, "presentation") and proposal.presentation:
        proposal.presentation.delete()

def accepted_proposal(sender, instance=None, **kwargs):
    if instance is None:
        return
    if instance.status == "accepted":
        promote_proposal(instance.proposal)
    else:
        unpromote_proposal(instance.proposal)
post_save.connect(accepted_proposal, sender=ProposalResult)
<commit_msg>removed debugging prints and updated Presentation field name<commit_after># -*- coding: utf-8 -*-
from datetime import datetime
from decimal import Decimal

from django.db import models
from django.db.models import Q
from django.db.models.signals import post_save

from django.contrib.auth.models import User

from markitup.fields import MarkupField

from symposion.proposals.models import ProposalBase
from symposion.schedule.models import Presentation

class ProposalScoreExpression(object):

    def as_sql(self, qn, connection=None):
        sql = "((3 * plus_one + plus_zero) - (minus_zero + 3 * minus_one))"
        return sql, []

    def prepare_database_save(self, unused):
        return self

class Votes(object):
    PLUS_ONE = "+1"
    PLUS_ZERO = "+0"
    MINUS_ZERO = u"−0"
    MINUS_ONE = u"−1"

    CHOICES = [
        (PLUS_ONE, u"+1 — Good proposal and I will argue for it to be accepted."),
        (PLUS_ZERO, u"+0 — OK proposal, but I will not argue for it to be accepted."),
        (MINUS_ZERO, u"−0 — Weak proposal, but I will not argue strongly against acceptance."),
        (MINUS_ONE, u"−1 — Serious issues and I will argue to reject this proposal."),
    ]
VOTES = Votes()

class ReviewAssignment(models.Model):
    AUTO_ASSIGNED_INITIAL = 0
    OPT_IN = 1
    AUTO_ASSIGNED_LATER = 2

    ORIGIN_CHOICES = [
        (AUTO_ASSIGNED_INITIAL, "auto-assigned, initial"),
        (OPT_IN, "opted-in"),
        (AUTO_ASSIGNED_LATER, "auto-assigned, later"),
    ]

    proposal = models.ForeignKey("proposals.ProposalBase")
    user = models.ForeignKey(User)

    origin = models.IntegerField(choices=ORIGIN_CHOICES)

    assigned_at = models.DateTimeField(default=datetime.now)
    opted_out = models.BooleanField()

    @classmethod
    def create_assignments(cls, proposal, origin=AUTO_ASSIGNED_INITIAL):
        speakers = [proposal.speaker] + list(proposal.additional_speakers.all())
        reviewers = User.objects.exclude(
            pk__in=[
                speaker.user_id
                for speaker in speakers
                if speaker.user_id is not None
            ]
        ).filter(
            groups__name="reviewers",
        ).filter(
            Q(reviewassignment__opted_out=False) | Q(reviewassignment=None)
        ).annotate(
            num_assignments=models.Count("reviewassignment")
        ).order_by(
            "num_assignments",
        )
        for reviewer in reviewers[:3]:
            cls._default_manager.create(
                proposal=proposal,
                user=reviewer,
                origin=origin,
            )

class ProposalMessage(models.Model):
    proposal = models.ForeignKey("proposals.ProposalBase", related_name="messages")
    user = models.ForeignKey(User)

    message = MarkupField()
    submitted_at = models.DateTimeField(default=datetime.now, editable=False)

    class Meta:
        ordering = ["submitted_at"]

class Review(models.Model):
    VOTES = VOTES

    proposal = models.ForeignKey("proposals.ProposalBase", related_name="reviews")
    user = models.ForeignKey(User)

    # No way to encode "-0" vs. "+0" into an IntegerField, and I don't feel
    # like some complicated encoding system.
    vote = models.CharField(max_length=2, blank=True, choices=VOTES.CHOICES)
    comment = MarkupField()
    submitted_at = models.DateTimeField(default=datetime.now, editable=False)

    def save(self, **kwargs):
        if self.vote:
            vote, created = LatestVote.objects.get_or_create(
                proposal = self.proposal,
                user = self.user,
                defaults = dict(
                    vote = self.vote,
                    submitted_at = self.submitted_at,
                )
            )
            if not created:
                LatestVote.objects.filter(pk=vote.pk).update(vote=self.vote)
                self.proposal.result.update_vote(self.vote, previous=vote.vote)
            else:
                self.proposal.result.update_vote(self.vote)
        super(Review, self).save(**kwargs)

    def delete(self):
        model = self.__class__
        user_reviews = model._default_manager.filter(
            proposal=self.proposal,
            user=self.user,
        )
        # find previous vote before self
        try:
            previous = user_reviews.filter(submitted_at__lt=self.submitted_at).order_by("-submitted_at")[0]
        except IndexError:
            # did not find a previous which means this must be the only one.
            # treat it as a last, but delete the latest vote.
            self.proposal.result.update_vote(self.vote, removal=True)
            lv = LatestVote.objects.filter(proposal=self.proposal, user=self.user)
            lv.delete()
        else:
            # handle that we've found a previous vote
            # check if self is the last vote
            if self == user_reviews.order_by("-submitted_at")[0]:
                # self is the latest which means we need to treat as last.
                # revert the latest vote to previous vote.
                self.proposal.result.update_vote(self.vote, previous=previous.vote, removal=True)
                lv = LatestVote.objects.filter(proposal=self.proposal, user=self.user)
                lv.update(
                    vote=previous.vote,
                    submitted_at=previous.submitted_at,
                )
            else:
                # self is not the latest so we just need to decrement the
                # comment count
                self.proposal.result.comment_count = models.F("comment_count") - 1
                self.proposal.result.save()
        # in all cases we need to delete the review; let's do it!
        super(Review, self).delete()

    def css_class(self):
        return {
            self.VOTES.PLUS_ONE: "plus-one",
            self.VOTES.PLUS_ZERO: "plus-zero",
            self.VOTES.MINUS_ZERO: "minus-zero",
            self.VOTES.MINUS_ONE: "minus-one",
        }[self.vote]

    @property
    def section(self):
        return self.proposal.kind.section.slug

class LatestVote(models.Model):
    VOTES = VOTES

    proposal = models.ForeignKey("proposals.ProposalBase", related_name="votes")
    user = models.ForeignKey(User)

    # No way to encode "-0" vs. "+0" into an IntegerField, and I don't feel
    # like some complicated encoding system.
    vote = models.CharField(max_length=2, choices=VOTES.CHOICES)
    submitted_at = models.DateTimeField(default=datetime.now, editable=False)

    class Meta:
        unique_together = [("proposal", "user")]

    def css_class(self):
        return {
            self.VOTES.PLUS_ONE: "plus-one",
            self.VOTES.PLUS_ZERO: "plus-zero",
            self.VOTES.MINUS_ZERO: "minus-zero",
            self.VOTES.MINUS_ONE: "minus-one",
        }[self.vote]

class ProposalResult(models.Model):
    proposal = models.OneToOneField("proposals.ProposalBase", related_name="result")
    score = models.DecimalField(max_digits=5, decimal_places=2, default=Decimal("0.00"))
    comment_count = models.PositiveIntegerField(default=1)
    vote_count = models.PositiveIntegerField(default=1)
    plus_one = models.PositiveIntegerField(default=0)
    plus_zero = models.PositiveIntegerField(default=0)
    minus_zero = models.PositiveIntegerField(default=0)
    minus_one = models.PositiveIntegerField(default=0)
    accepted = models.NullBooleanField(choices=[
        (True, "accepted"),
        (False, "rejected"),
        (None, "undecided"),
    ], default=None)
    status = models.CharField(max_length=20, choices=[
        ("accepted", "accepted"),
        ("rejected", "rejected"),
        ("undecided", "undecided"),
        ("standby", "standby"),
    ], default="undecided")

    @classmethod
    def full_calculate(cls):
        for proposal in ProposalBase.objects.all():
            result, created = cls._default_manager.get_or_create(proposal=proposal)
            result.comment_count = Review.objects.filter(proposal=proposal).count()
            result.vote_count = LatestVote.objects.filter(proposal=proposal).count()
            result.plus_one = LatestVote.objects.filter(
                proposal = proposal,
                vote = VOTES.PLUS_ONE
            ).count()
            result.plus_zero = LatestVote.objects.filter(
                proposal = proposal,
                vote = VOTES.PLUS_ZERO
            ).count()
            result.minus_zero = LatestVote.objects.filter(
                proposal = proposal,
                vote = VOTES.MINUS_ZERO
            ).count()
            result.minus_one = LatestVote.objects.filter(
                proposal = proposal,
                vote = VOTES.MINUS_ONE
            ).count()
            result.save()
            cls._default_manager.filter(pk=result.pk).update(score=ProposalScoreExpression())

    def update_vote(self, vote, previous=None, removal=False):
        mapping = {
            VOTES.PLUS_ONE: "plus_one",
            VOTES.PLUS_ZERO: "plus_zero",
            VOTES.MINUS_ZERO: "minus_zero",
            VOTES.MINUS_ONE: "minus_one",
        }
        if previous:
            if previous == vote:
                return
            if removal:
                setattr(self, mapping[previous], models.F(mapping[previous]) + 1)
            else:
                setattr(self, mapping[previous], models.F(mapping[previous]) - 1)
        else:
            if removal:
                self.vote_count = models.F("vote_count") - 1
            else:
                self.vote_count = models.F("vote_count") + 1
        if removal:
            setattr(self, mapping[vote], models.F(mapping[vote]) - 1)
            self.comment_count = models.F("comment_count") - 1
        else:
            setattr(self, mapping[vote], models.F(mapping[vote]) + 1)
            self.comment_count = models.F("comment_count") + 1
        self.save()
        model = self.__class__
        model._default_manager.filter(pk=self.pk).update(score=ProposalScoreExpression())

class Comment(models.Model):
    proposal = models.ForeignKey("proposals.ProposalBase", related_name="comments")
    commenter = models.ForeignKey(User)
    text = MarkupField()

    # Or perhaps more accurately, can the user see this comment.
    public = models.BooleanField(choices=[
        (True, "public"),
        (False, "private"),
    ])
    commented_at = models.DateTimeField(default=datetime.now)

class NotificationTemplate(models.Model):

    label = models.CharField(max_length=100)
    from_address = models.EmailField()
    subject = models.CharField(max_length=100)
    body = models.TextField()

class ResultNotification(models.Model):

    proposal = models.ForeignKey("proposals.ProposalBase", related_name="notifications")
    template = models.ForeignKey(NotificationTemplate, null=True, blank=True, on_delete=models.SET_NULL)
    timestamp = models.DateTimeField(default=datetime.now)
    to_address = models.EmailField()
    from_address = models.EmailField()
    subject = models.CharField(max_length=100)
    body = models.TextField()

    @property
    def email_args(self):
        return (self.subject, self.body, self.from_address, [self.to_address])

def promote_proposal(proposal):
    if hasattr(proposal, "presentation") and proposal.presentation:
        # already promoted
        presentation = proposal.presentation
    else:
        presentation = Presentation(
            title = proposal.title,
            description = proposal.description,
            abstract = proposal.abstract,
            speaker = proposal.speaker,
            section = proposal.section,
            proposal_base = proposal,
        )
        presentation.save()
        for speaker in proposal.additional_speakers.all():
            presentation.additional_speakers.add(speaker)
            presentation.save()

    return presentation

def unpromote_proposal(proposal):
    if hasattr(proposal, "presentation") and proposal.presentation:
        proposal.presentation.delete()

def accepted_proposal(sender, instance=None, **kwargs):
    if instance is None:
        return
    if instance.status == "accepted":
        promote_proposal(instance.proposal)
    else:
        unpromote_proposal(instance.proposal)
post_save.connect(accepted_proposal, sender=ProposalResult)
<|endoftext|>