<commit_before>'''
### What this script does
This script takes data from a Google spreadsheet, converts it to a JSON array
where each object has keys corresponding to the spreadsheet column names, then
then stores that JSON locally and/or automatically commits it to a GitHub repo.

There are four methods chained together to perform these task(s).

* fetch_from_spreadsheet(): uses Oauth2 credentials to authenticate with
    Google and copy data from a spreadsheet into a Python dict.

* transform_data(): passes each item in the Python dict through a filter,
    providing an opportunity to validate, transform, remove fields, etc.

* make_json(): converts the Python dict into a JSON array, and provides
    an opportunity to store a local copy of the JSON (useful for testing).

* commit_json(): authenticates with GitHub and commits a JSON file
    to the identified repository.

Running `update_schedule()` will execute these four methods in succession.
It can also be run from the command line: `python update_mozfest_schedule.py`

### Authentication
For authentication to work, you must generate Google and GitHub credentials.
These should be stored as environment variables, and *not* committed to the
repository.

* GitHub: Follow these instructions to create a person API token:
    https://github.com/blog/1509-personal-api-tokens

    You should generate the token from an account that has write access
    to the repository where you want to store the JSON. It should be stored
    in an environment variable called `GITHUB_TOKEN`.

* Google: Follow these instructions to create *a service account*:
    https://developers.google.com/console/help/new/?hl=en_US#credentials-access-security-and-identity

    Creating a service account will generate a special client email address
    and a new private key. The client email address should be stored in
    an environment variable called `GOOGLE_API_CLIENT_EMAIL`. The private key
    should be stored in an environment variable called `GOOGLE_API_PRIVATE_KEY`.

### Customization
You should change the values in `GITHUB_CONFIG` according to your project.

* REPO_OWNER: a string representing the GitHub username of the account
    that owns the repository you want to commit to.

* REPO_NAME: a string representing the name of the repository to commit to.

* TARGET_FILE: a string representing the name of the file you want to create
     or update in the GitHub repository. This can include path information,
     e.g. 'sessions.json', or '_data/sessions.json'

* TARGET_BRANCH': a string representing the branch of the repository you want
    to commit to, e.g. 'gh-pages'

You should also change these values according to your project.

* GOOGLE_SPREADSHEET_KEY: a string representing the unique ID of the Google
    spreadsheet storing your data. This can be stored as an environment
    variable called GOOGLE_SPREADSHEET_KEY for extra security, or can simply
    be stored as a string in this script.

* MAKE_LOCAL_JSON: should likely be set to `False` in production, but can
    be set to `True` for  testing. If set to `True`, the `make_json()` method
    will create a local file containing the JSON for you to inspect.

* COMMIT_JSON_TO_GITHUB: should be set to `True` in production. If set
    to `True`, the `commit_json()` method will create or update a JSON file
    in the GitHub repository you specify. Can be set to `False` for testing,
    which will authenticate with GitHub but not create or update any files.

### Usage notes
Data is pulled from the spreadsheet identified in `GOOGLE_SPREADSHEET_KEY`.
The script assumes that data is in the *first* worksheet in the spreadsheet:

    datasheet = spreadsheet.get_worksheet(0)

If data is *not* in the first worksheet, `spreadsheet.get_worksheet(0)`
should be changed to identify the worksheet by title:

    datasheet = spreadsheet.worksheet("SHEET NAME")

'''
import argparse, os, sys, traceback
import github3
import gspread
import io
import json
import os
import requests
from datetime import datetime
from oauth2client.client import SignedJwtAssertionCredentials

GITHUB_CONFIG = {
    'TOKEN': os.environ['GITHUB_TOKEN'],
    'REPO_OWNER': 'mozilla',
    'REPO_NAME': 'mozfest-schedule-app',
    'TARGET_FILE': 'sessions.json',
    'TARGET_BRANCH': 'gh-pages',
}

GOOGLE_API_CONFIG = {
    'CLIENT_EMAIL': os.environ['GOOGLE_API_CLIENT_EMAIL'],
    'PRIVATE_KEY': os.environ['GOOGLE_API_PRIVATE_KEY'].decode('unicode_escape'),
    'SCOPE': ['https://spreadsheets.google.com/feeds']
}

# can be stored as an environment variable if worried
# about security or simply stored as string here
GOOGLE_SPREADSHEET_KEY = os.environ['GOOGLE_SPREADSHEET_KEY'] or ''

FETCH_MULTIPLE_WORKSHEETS = True
WORKSHEETS_TO_SKIP = ['Template', '(backup) original imported data']

MAKE_LOCAL_JSON = False
COMMIT_JSON_TO_GITHUB = True

def authenticate_with_google():
    '''
    Connect to Google Spreadsheet with gspread library.
    '''
    credentials = SignedJwtAssertionCredentials(
        GOOGLE_API_CONFIG['CLIENT_EMAIL'], GOOGLE_API_CONFIG['PRIVATE_KEY'], GOOGLE_API_CONFIG['SCOPE']
    )
    google_api_conn = gspread.authorize(credentials)

    return google_api_conn

def open_google_spreadsheet():
    '''
    Authenticate and return spreadsheet by `GOOGLE_SPREADSHEET_KEY`.
    '''
    google_api_conn = authenticate_with_google()
    spreadsheet = google_api_conn.open_by_key(GOOGLE_SPREADSHEET_KEY)

    return spreadsheet

def fetch_from_worksheet():
    '''
    Return data from first worksheet in Google spreadsheet.
    '''
    spreadsheet = open_google_spreadsheet()
    worksheet = spreadsheet.get_worksheet(0)
    data = worksheet.get_all_records(empty2zero=False)

    return data

def fetch_from_all_worksheets(worksheets_to_skip=[]):
    '''
    Return data from all worksheets in Google spreadsheet, optionally
    skipping sheets identified by title in `WORKSHEETS_TO_SKIP`.
    '''
    spreadsheet = open_google_spreadsheet()
    data = []
    worksheet_list = [
        sheet for sheet in spreadsheet.worksheets() if sheet.title not in WORKSHEETS_TO_SKIP
    ]

    for worksheet in worksheet_list:
        worksheet.title
        data.extend(worksheet.get_all_records(empty2zero=False))

    return data

def transform_data(data):
    '''
    Transforms data and filters individual schedule items for fields we want
    to publish. Currently, this:

    * ensures that all variables going into the JSON are strings
    * removes `proposalSpreadsheetRowNumber` to make JSON smaller
    * transforms column name `name` into JSON key `title`
    * transforms column name `githubIssueNumber` into JSON key `id`
    * removes any rows that don't have a numeric `id`
    * creates a concatenated `facilitators` key
    * removes invalid pathway labels that were used for GitHub workflow
    * creates a `scheduleblock` key based on data in `time` column
    * infers a `day` and `start` key based on data in `time` column
    '''
    def _transform_response_item(item, skip=False):
        # make sure vars are strings
        _transformed_item = {k: unicode(v) for k, v in item.iteritems() if k}

        # don't need `proposalSpreadsheetRowNumber` for schedule app
        if 'proposalSpreadsheetRowNumber' in _transformed_item:
            del _transformed_item['proposalSpreadsheetRowNumber']

        # transform `name` column name into `title` key
        # and skip rows that represent pathways, or have no name
        if 'name' in _transformed_item:
            _transformed_item['title'] = _transformed_item.pop('name', '')
            if not _transformed_item['title']:
                skip = True
            if _transformed_item['title'].lower().startswith('[path'):
                skip = True

        # transform `githubIssueNumber` column name into `id` key
        # (and skip rows without a valid id)
        if 'githubIssueNumber' in _transformed_item:
            _transformed_item['id'] = _transformed_item.pop('githubIssueNumber', '')

            # remove rows with `id` that is blank or provides instructions
            try:
                int(_transformed_item['id'])
            except:
                skip = True

        # create concatenated `facilitators` key for schedule list display
        # and `facilitator_array` key for session detail display
        name_list = []
        name_detail_list = []
        for key in _transformed_item.keys():
            if key.startswith('facilitator_'):
                name_list.append(_transformed_item[key].split(",")[0])
                name_detail_list.append(_transformed_item.pop(key))
        _transformed_item['facilitators'] = ', '.join(filter(None, name_list))
        _transformed_item['facilitator_array'] = filter(None, name_detail_list)

        # remove invalid pathway labels that were used for GitHub workflow
        pathway_skip_keywords = ['accepted','consideration','stipend','sample']
        pathway_list = _transformed_item['pathways'].split(',')
        pathway_list = [
            name for name in pathway_list if not set(pathway_skip_keywords).intersection(set(name.lower().split()))
        ]
        _transformed_item['pathways'] = ','.join(pathway_list)

        # create `scheduleblock` key based on `time`
        time_data = _transformed_item.pop('time', '').split('(')
        # "slugified" version of scheduleblock
        scheduleblock = time_data[0].strip()
        scheduleblock = scheduleblock.lower().replace(' ','-')
        _transformed_item['scheduleblock'] = scheduleblock
        # infer session day
        if 'saturday' in _transformed_item['scheduleblock']:
            _transformed_item['day'] = 'Saturday'
        if 'sunday' in _transformed_item['scheduleblock']:
            _transformed_item['day'] = 'Sunday'
        if 'weekend' in _transformed_item['scheduleblock']:
            # TODO
            #_transformed_item['day'] = 'Saturday'
            pass
        # infer start time
        if len(time_data) > 1:
            start_time = time_data[1].strip('()').split(' ')[0]
            try:
                # attempt to coerce to 12-hour format
                d = datetime.strptime(start_time, "%H:%M")
                start_time = d.strftime("%I:%M %p")
            except:
                pass
            _transformed_item['start'] = start_time

        # if we've triggered the skip flag anywhere, drop this record
        if skip:
            _transformed_item = None

        return _transformed_item

    transformed_data = filter(None, [_transform_response_item(item) for item in data])

    return transformed_data

def make_json(data, store_locally=False, filename=GITHUB_CONFIG['TARGET_FILE']):
    '''
    Turns data into nice JSON, and optionally stores to a local file.
    '''
    json_out = json.dumps(data, sort_keys=True, indent=4, ensure_ascii=False)

    if store_locally:
        with io.open(filename, 'w', encoding='utf8') as outfile:
            outfile.write(unicode(json_out))

    return json_out.encode('utf-8')

def commit_json(data, target_config=GITHUB_CONFIG, commit=COMMIT_JSON_TO_GITHUB):
    '''
    Uses token to log into GitHub as `ryanpitts`, then gets the appropriate
    repo based on owner/name defined in GITHUB_CONFIG.

    Creates sessions data file if it does not exist in the repo, otherwise
    updates existing data file.

    If `COMMIT_JSON_TO_GITHUB` is False, this will operate in "dry run" mode,
    authenticating against GitHub but not changing any files.
    '''

    # authenticate with GitHub
    gh = github3.login(token=target_config['TOKEN'])

    # get the right repo
    repo = gh.repository(target_config['REPO_OWNER'], target_config['REPO_NAME'])

    # check to see whether data file exists
    contents = repo.contents(
        path=target_config['TARGET_FILE'],
        ref=target_config['TARGET_BRANCH']
    )

    if commit:
        if not contents:
            # create file that doesn't exist
            repo.create_file(
                path=target_config['TARGET_FILE'],
                message='adding session data',
                content=data,
                branch=target_config['TARGET_BRANCH']
            )
        else:
            # update existing file
            repo.update_file(
                path=target_config['TARGET_FILE'],
                message='updating session data',
                content=data,
                sha=contents.sha,
                branch=GITHUB_CONFIG['TARGET_BRANCH']
        )

def update_schedule():
    if FETCH_MULTIPLE_WORKSHEETS:
        data = fetch_from_all_worksheets(worksheets_to_skip=WORKSHEETS_TO_SKIP)
    else:
        data = fetch_from_worksheet()
    #print 'Fetched the data ...'

    data = transform_data(data)
    #print 'Prepped the data ...'

    session_json = make_json(data, store_locally=MAKE_LOCAL_JSON)
    #print 'Made the local json!'

    commit_json(session_json)
    #print 'SENT THE DATA TO GITHUB!'

if __name__ == "__main__":
    try:
        update_schedule()
    except Exception, e:
        sys.stderr.write('\n')
        traceback.print_exc(file=sys.stderr)
        sys.stderr.write('\n')
        sys.exit(1)
<commit_msg>allow commits to multiple repo branches<commit_after>'''
### What this script does
This script takes data from a Google spreadsheet, converts it to a JSON array
where each object has keys corresponding to the spreadsheet column names, then
then stores that JSON locally and/or automatically commits it to a GitHub repo.

There are four methods chained together to perform these task(s).

* fetch_from_spreadsheet(): uses Oauth2 credentials to authenticate with
    Google and copy data from a spreadsheet into a Python dict.

* transform_data(): passes each item in the Python dict through a filter,
    providing an opportunity to validate, transform, remove fields, etc.

* make_json(): converts the Python dict into a JSON array, and provides
    an opportunity to store a local copy of the JSON (useful for testing).

* commit_json(): authenticates with GitHub and commits a JSON file
    to the identified repository.

Running `update_schedule()` will execute these four methods in succession.
It can also be run from the command line: `python update_mozfest_schedule.py`

### Authentication
For authentication to work, you must generate Google and GitHub credentials.
These should be stored as environment variables, and *not* committed to the
repository.

* GitHub: Follow these instructions to create a person API token:
    https://github.com/blog/1509-personal-api-tokens

    You should generate the token from an account that has write access
    to the repository where you want to store the JSON. It should be stored
    in an environment variable called `GITHUB_TOKEN`.

* Google: Follow these instructions to create *a service account*:
    https://developers.google.com/console/help/new/?hl=en_US#credentials-access-security-and-identity

    Creating a service account will generate a special client email address
    and a new private key. The client email address should be stored in
    an environment variable called `GOOGLE_API_CLIENT_EMAIL`. The private key
    should be stored in an environment variable called `GOOGLE_API_PRIVATE_KEY`.

### Customization
You should change the values in `GITHUB_CONFIG` according to your project.

* REPO_OWNER: a string representing the GitHub username of the account
    that owns the repository you want to commit to.

* REPO_NAME: a string representing the name of the repository to commit to.

* TARGET_FILE: a string representing the name of the file you want to create
     or update in the GitHub repository. This can include path information,
     e.g. 'sessions.json', or '_data/sessions.json'

* TARGET_BRANCH': a string representing the branch of the repository you want
    to commit to, e.g. 'gh-pages'

You should also change these values according to your project.

* GOOGLE_SPREADSHEET_KEY: a string representing the unique ID of the Google
    spreadsheet storing your data. This can be stored as an environment
    variable called GOOGLE_SPREADSHEET_KEY for extra security, or can simply
    be stored as a string in this script.

* MAKE_LOCAL_JSON: should likely be set to `False` in production, but can
    be set to `True` for  testing. If set to `True`, the `make_json()` method
    will create a local file containing the JSON for you to inspect.

* COMMIT_JSON_TO_GITHUB: should be set to `True` in production. If set
    to `True`, the `commit_json()` method will create or update a JSON file
    in the GitHub repository you specify. Can be set to `False` for testing,
    which will authenticate with GitHub but not create or update any files.

### Usage notes
Data is pulled from the spreadsheet identified in `GOOGLE_SPREADSHEET_KEY`.
The script assumes that data is in the *first* worksheet in the spreadsheet:

    datasheet = spreadsheet.get_worksheet(0)

If data is *not* in the first worksheet, `spreadsheet.get_worksheet(0)`
should be changed to identify the worksheet by title:

    datasheet = spreadsheet.worksheet("SHEET NAME")

'''
import argparse, os, sys, traceback
import github3
import gspread
import io
import json
import os
import requests
from datetime import datetime
from oauth2client.client import SignedJwtAssertionCredentials

GITHUB_CONFIG = {
    'TOKEN': os.environ['GITHUB_TOKEN'],
    'REPO_OWNER': 'mozilla',
    'REPO_NAME': 'mozfest-schedule-app',
    'TARGET_FILE': 'sessions.json',
    'TARGET_BRANCHES': ['gh-pages','master'],
}

GOOGLE_API_CONFIG = {
    'CLIENT_EMAIL': os.environ['GOOGLE_API_CLIENT_EMAIL'],
    'PRIVATE_KEY': os.environ['GOOGLE_API_PRIVATE_KEY'].decode('unicode_escape'),
    'SCOPE': ['https://spreadsheets.google.com/feeds']
}

# can be stored as an environment variable if worried
# about security or simply stored as string here
GOOGLE_SPREADSHEET_KEY = os.environ['GOOGLE_SPREADSHEET_KEY'] or ''

FETCH_MULTIPLE_WORKSHEETS = True
WORKSHEETS_TO_SKIP = ['Template', '(backup) original imported data']

MAKE_LOCAL_JSON = False
COMMIT_JSON_TO_GITHUB = True

def authenticate_with_google():
    '''
    Connect to Google Spreadsheet with gspread library.
    '''
    credentials = SignedJwtAssertionCredentials(
        GOOGLE_API_CONFIG['CLIENT_EMAIL'], GOOGLE_API_CONFIG['PRIVATE_KEY'], GOOGLE_API_CONFIG['SCOPE']
    )
    google_api_conn = gspread.authorize(credentials)

    return google_api_conn

def open_google_spreadsheet():
    '''
    Authenticate and return spreadsheet by `GOOGLE_SPREADSHEET_KEY`.
    '''
    google_api_conn = authenticate_with_google()
    spreadsheet = google_api_conn.open_by_key(GOOGLE_SPREADSHEET_KEY)

    return spreadsheet

def fetch_from_worksheet():
    '''
    Return data from first worksheet in Google spreadsheet.
    '''
    spreadsheet = open_google_spreadsheet()
    worksheet = spreadsheet.get_worksheet(0)
    data = worksheet.get_all_records(empty2zero=False)

    return data

def fetch_from_all_worksheets(worksheets_to_skip=[]):
    '''
    Return data from all worksheets in Google spreadsheet, optionally
    skipping sheets identified by title in `WORKSHEETS_TO_SKIP`.
    '''
    spreadsheet = open_google_spreadsheet()
    data = []
    worksheet_list = [
        sheet for sheet in spreadsheet.worksheets() if sheet.title not in WORKSHEETS_TO_SKIP
    ]

    for worksheet in worksheet_list:
        worksheet.title
        data.extend(worksheet.get_all_records(empty2zero=False))

    return data

def transform_data(data):
    '''
    Transforms data and filters individual schedule items for fields we want
    to publish. Currently, this:

    * ensures that all variables going into the JSON are strings
    * removes `proposalSpreadsheetRowNumber` to make JSON smaller
    * transforms column name `name` into JSON key `title`
    * transforms column name `githubIssueNumber` into JSON key `id`
    * removes any rows that don't have a numeric `id`
    * creates a concatenated `facilitators` key
    * removes invalid pathway labels that were used for GitHub workflow
    * creates a `scheduleblock` key based on data in `time` column
    * infers a `day` and `start` key based on data in `time` column
    '''
    def _transform_response_item(item, skip=False):
        # make sure vars are strings
        _transformed_item = {k: unicode(v) for k, v in item.iteritems() if k}

        # don't need `proposalSpreadsheetRowNumber` for schedule app
        if 'proposalSpreadsheetRowNumber' in _transformed_item:
            del _transformed_item['proposalSpreadsheetRowNumber']

        # transform `name` column name into `title` key
        # and skip rows that represent pathways, or have no name
        if 'name' in _transformed_item:
            _transformed_item['title'] = _transformed_item.pop('name', '')
            if not _transformed_item['title']:
                skip = True
            if _transformed_item['title'].lower().startswith('[path'):
                skip = True

        # transform `githubIssueNumber` column name into `id` key
        # (and skip rows without a valid id)
        if 'githubIssueNumber' in _transformed_item:
            _transformed_item['id'] = _transformed_item.pop('githubIssueNumber', '')

            # remove rows with `id` that is blank or provides instructions
            try:
                int(_transformed_item['id'])
            except:
                skip = True

        # create concatenated `facilitators` key for schedule list display
        # and `facilitator_array` key for session detail display
        name_list = []
        name_detail_list = []
        for key in _transformed_item.keys():
            if key.startswith('facilitator_'):
                name_list.append(_transformed_item[key].split(",")[0])
                name_detail_list.append(_transformed_item.pop(key))
        _transformed_item['facilitators'] = ', '.join(filter(None, name_list))
        _transformed_item['facilitator_array'] = filter(None, name_detail_list)

        # remove invalid pathway labels that were used for GitHub workflow
        pathway_skip_keywords = ['accepted','consideration','stipend','sample']
        pathway_list = _transformed_item['pathways'].split(',')
        pathway_list = [
            name for name in pathway_list if not set(pathway_skip_keywords).intersection(set(name.lower().split()))
        ]
        _transformed_item['pathways'] = ','.join(pathway_list)

        # create `scheduleblock` key based on `time`
        time_data = _transformed_item.pop('time', '').split('(')
        # "slugified" version of scheduleblock
        scheduleblock = time_data[0].strip()
        scheduleblock = scheduleblock.lower().replace(' ','-')
        _transformed_item['scheduleblock'] = scheduleblock
        # infer session day
        if 'saturday' in _transformed_item['scheduleblock']:
            _transformed_item['day'] = 'Saturday'
        if 'sunday' in _transformed_item['scheduleblock']:
            _transformed_item['day'] = 'Sunday'
        if 'weekend' in _transformed_item['scheduleblock']:
            # TODO
            #_transformed_item['day'] = 'Saturday'
            pass
        # infer start time
        if len(time_data) > 1:
            start_time = time_data[1].strip('()').split(' ')[0]
            try:
                # attempt to coerce to 12-hour format
                d = datetime.strptime(start_time, "%H:%M")
                start_time = d.strftime("%I:%M %p")
            except:
                pass
            _transformed_item['start'] = start_time

        # if we've triggered the skip flag anywhere, drop this record
        if skip:
            _transformed_item = None

        return _transformed_item

    transformed_data = filter(None, [_transform_response_item(item) for item in data])

    return transformed_data

def make_json(data, store_locally=False, filename=GITHUB_CONFIG['TARGET_FILE']):
    '''
    Turns data into nice JSON, and optionally stores to a local file.
    '''
    json_out = json.dumps(data, sort_keys=True, indent=4, ensure_ascii=False)

    if store_locally:
        with io.open(filename, 'w', encoding='utf8') as outfile:
            outfile.write(unicode(json_out))

    return json_out.encode('utf-8')

def commit_json(data, target_config=GITHUB_CONFIG, commit=COMMIT_JSON_TO_GITHUB):
    '''
    Uses token to log into GitHub as `ryanpitts`, then gets the appropriate
    repo based on owner/name defined in GITHUB_CONFIG.

    Creates sessions data file if it does not exist in the repo, otherwise
    updates existing data file.

    If `COMMIT_JSON_TO_GITHUB` is False, this will operate in "dry run" mode,
    authenticating against GitHub but not changing any files.
    '''

    # authenticate with GitHub
    gh = github3.login(token=target_config['TOKEN'])

    # get the right repo
    repo = gh.repository(target_config['REPO_OWNER'], target_config['REPO_NAME'])

    for branch in target_config['TARGET_BRANCHES']:
        # check to see whether data file exists
        contents = repo.contents(
            path=target_config['TARGET_FILE'],
            ref=branch
        )

        if commit:
            if not contents:
                # create file that doesn't exist
                repo.create_file(
                    path=target_config['TARGET_FILE'],
                    message='adding session data',
                    content=data,
                    branch=branch
                )
            else:
                # update existing file
                repo.update_file(
                    path=target_config['TARGET_FILE'],
                    message='updating session data',
                    content=data,
                    sha=contents.sha,
                    branch=branch
            )

def update_schedule():
    if FETCH_MULTIPLE_WORKSHEETS:
        data = fetch_from_all_worksheets(worksheets_to_skip=WORKSHEETS_TO_SKIP)
    else:
        data = fetch_from_worksheet()
    #print 'Fetched the data ...'

    data = transform_data(data)
    #print 'Prepped the data ...'

    session_json = make_json(data, store_locally=MAKE_LOCAL_JSON)
    #print 'Made the local json!'

    commit_json(session_json)
    #print 'SENT THE DATA TO GITHUB!'

if __name__ == "__main__":
    try:
        update_schedule()
    except Exception, e:
        sys.stderr.write('\n')
        traceback.print_exc(file=sys.stderr)
        sys.stderr.write('\n')
        sys.exit(1)
<|endoftext|>


<commit_before>from celery import task
from brew.models import MashLog, Batch
from brew.helpers import get_variable, set_variable
from time import sleep
from random import random
from datetime import datetime
from django.conf import settings
import pytz

MASHSTEP_STATE_APPROACH = 'approach'
MASHSTEP_STATE_STAY = 'stay'
MASHSTEP_STATE_FINISHED = 'finished'
ARDUINO_TEMPERATURE_PIN = 2
ARDUINO_HEAT_PIN = 13
ARDUINO_COOL_PIN = 11
MAXIMUM_DEVIATION = 0.3  # Maximum allowed deviation in temperature before heat/cooling is triggered
DELAY_BETWEEN_MEASUREMENTS = 2  # Seconds between each measurement
DELAY_BETWEEN_LOGS = 15  # Seconds between each measurement
DELAY_BETWEEN_ACTION_SWITCH = 30  # To prevent fast switching between heat, passive and/or cooling, add build in delay

@task()
def init_mashing(batch):

    # (Re)set defaults for batch
    set_batch_defaults(batch)
    set_variable('active_mashingprocess_batch_id', str(batch.id))

    # Initialize Arduino with Nanpy
    if not settings.ARDUINO_SIMULATION:
        from nanpy import DallasTemperature, Arduino # Nanpy Initializes Arduino connection, thus using conditional import
        sensor = DallasTemperature(ARDUINO_TEMPERATURE_PIN)
        addr = sensor.getAddress(ARDUINO_TEMPERATURE_PIN)
        Arduino.pinMode(ARDUINO_HEAT_PIN, Arduino.OUTPUT)
        Arduino.pinMode(ARDUINO_COOL_PIN, Arduino.OUTPUT)
    else:
        # Set initial dummy temperature
        batch.temperature = 20  # Testing purpose only
        batch.save()
        batch = Batch.objects.get(id=batch.id)

    # Run Mashing proces
    while get_variable('active_mashingprocess_batch_id', 'None') == str(batch.id):
        # Measure data
        measured_data = {}
        if settings.ARDUINO_SIMULATION:
            measured_data['temp'] = get_dummy_temperature(batch)
        else:
            sensor.requestTemperatures()
            measured_data['temp'] = sensor.getTempC(addr)

        # Define actions depending on measured data
        batch = process_measured_data(batch.id, measured_data)

        # Send updates to arduino
        if not settings.ARDUINO_SIMULATION:
            send_updates_to_arduino(batch)

        # Send to logging department
        handle_logging(batch)

        # Delay
        sleep(DELAY_BETWEEN_MEASUREMENTS)

    return 'Mashing proces ended'

# Update batch data according to measured data
def process_measured_data(batch_id, measured_data):
    # Ensure up to date batch
    batch = Batch.objects.get(id=batch_id)
    batch.temperature = float(measured_data['temp'])
    temp = float(measured_data['temp'])

    # current mashing action is heat/cool until next step is reached
    if batch.active_mashingstep_state == MASHSTEP_STATE_APPROACH:
        switch_to_stay = False

        # Cast to float
        target_temperature = float(batch.active_mashingstep.temperature)

        # Define direction if undefined and start appropriate action
        if batch.active_mashingstep_approach_direction == 'tbd':
            if temp < target_temperature:
               batch.active_mashingstep_approach_direction = 'heat'
               batch.heat = True
            else:
                batch.active_mashingstep_approach_direction = 'cool'
                batch.cool = True

        # Healing logic
        if batch.active_mashingstep_approach_direction == 'heat' and temp >= target_temperature:
            # Mashing step temperature reached
            switch_to_stay = True

        # Cooling logic
        if batch.active_mashingstep_approach_direction == 'cool'and temp <= target_temperature:
            # Mashing step temperature reached
            switch_to_stay = True

        # Mashing step temperature reached logic
        if switch_to_stay:
            # Switch to stay at temperature
            batch.active_mashingstep_state = MASHSTEP_STATE_STAY
            batch.heat = False
            batch.cool = False
            # Reset direction
            batch.active_mashingstep_approach_direction = 'tbd'

    elif batch.active_mashingstep_state == MASHSTEP_STATE_STAY:

        # Check if total time to spend in active mashing step is reached
        seconds_to_stay = int(batch.active_mashingstep.minutes) * 60
        now = datetime.now(pytz.utc)
        difference = now - batch.active_mashingstep_state_start
        if difference.total_seconds() >= seconds_to_stay:
            # Total time to spend in active mashing step reached
            # If active mashing step is latest, set status to finished
            active_mashingstep_index = batch.mashing_scheme.mashingstep_set.filter(id__lt=batch.active_mashingstep.id).count()
            try:
                # Activate next mashing step if available
                batch.active_mashingstep = batch.mashing_scheme.mashingstep_set.all()[active_mashingstep_index + 1]
                batch.active_mashingstep_state = MASHSTEP_STATE_APPROACH
            except:
                # Set state to finished
                batch.active_mashingstep_state = MASHSTEP_STATE_FINISHED

        else:
            # Total time spend not reached, check if actions are required to ensure current temperature
            target_temperature = float(batch.active_mashingstep.temperature)

            # TODO: Set actions if temperature goes out of bounds
            # if temp < (temp_to_stay - MAXIMUM_DEVIATION) and
    batch.save()
    return batch

def handle_logging(batch):
    chart_icon = None
    create_log = False

    try:
        last_log = MashLog.objects.filter(batch=batch).latest('id')
        now = datetime.now(pytz.utc)
        difference = now - last_log.created

        #  If DELAY_BETWEEN_LOGS is reached, log!
        if difference.total_seconds() >= DELAY_BETWEEN_LOGS:
            create_log = True

        # If state changed since previous log, log!
        if last_log.active_mashing_step_state != batch.active_mashingstep_state:
            create_log = True

            # Set chart icon
            if batch.active_mashingstep_state == MASHSTEP_STATE_STAY:
                active_mashingstep_index = batch.mashing_scheme.mashingstep_set.filter(id__lt=batch.active_mashingstep.id).count()
                chart_icon = 'start' + str(active_mashingstep_index + 1)
            elif batch.active_mashingstep_state == MASHSTEP_STATE_APPROACH:
                active_mashingstep_index = batch.mashing_scheme.mashingstep_set.filter(id__lt=batch.active_mashingstep.id).count()
                chart_icon = 'stop' + str(active_mashingstep_index)
            elif batch.active_mashingstep_state == MASHSTEP_STATE_FINISHED:
                chart_icon = 'finished'

    except MashLog.DoesNotExist:
        #  No logs found, log first one
        create_log = True

    if create_log:
        # Optionally, Log everything
        MashLog.objects.create(
            batch=batch,
            temperature=batch.temperature,
            active_mashing_step=batch.active_mashingstep,
            active_mashing_step_state=batch.active_mashingstep_state,
            heat=batch.heat,
            chart_icon=chart_icon
        )

# (Re)Set initial status for batch
def set_batch_defaults(batch):
    batch.active_mashingstep = batch.mashing_scheme.mashingstep_set.all().order_by('position')[0]
    batch.active_mashingstep_state = MASHSTEP_STATE_APPROACH
    batch.active_mashingstep_state_start = datetime.now(pytz.utc)
    batch.active_mashingstep_approach_direction = 'tbd'
    batch.heat = False
    batch.cool = False
    batch.save()

def send_updates_to_arduino(batch):
    if batch.heat:
        Arduino.digitalWrite(ARDUINO_HEAT_PIN, Arduino.HIGH)
    else:
        Arduino.digitalWrite(ARDUINO_HEAT_PIN, Arduino.LOW)

    if batch.cool:
        Arduino.digitalWrite(ARDUINO_COOL_PIN, Arduino.HIGH)
    else:
        Arduino.digitalWrite(ARDUINO_COOL_PIN, Arduino.LOW)

# Return dummy temp for testing based on heat/cool actions triggered
def get_dummy_temperature(batch):
    if batch.heat:
        # When heating, simulate steady temperature raising
        temp = "%.2f" % (batch.temperature + (random() / 10))
    else:
        # Nothing happening, simulate slow temperature lowering
        temp = "%.2f" % (batch.temperature - (random() / 150))
    return temp<commit_msg>Always load nanpy<commit_after>from celery import task
from brew.models import MashLog, Batch
from brew.helpers import get_variable, set_variable
from time import sleep
from random import random
from datetime import datetime
from django.conf import settings
from nanpy import DallasTemperature, Arduino
import pytz

MASHSTEP_STATE_APPROACH = 'approach'
MASHSTEP_STATE_STAY = 'stay'
MASHSTEP_STATE_FINISHED = 'finished'
ARDUINO_TEMPERATURE_PIN = 2
ARDUINO_HEAT_PIN = 11
ARDUINO_COOL_PIN = 12
ARDUINO_STATUS_PIN = 13
MAXIMUM_DEVIATION = 0.3  # Maximum allowed deviation in temperature before heat/cooling is triggered
DELAY_BETWEEN_MEASUREMENTS = 2  # Seconds between each measurement
DELAY_BETWEEN_LOGS = 15  # Seconds between each measurement
DELAY_BETWEEN_ACTION_SWITCH = 30  # To prevent fast switching between heat, passive and/or cooling, add build in delay

@task()
def init_mashing(batch):

    # (Re)set defaults for batch
    set_batch_defaults(batch)
    set_variable('active_mashingprocess_batch_id', str(batch.id))

    # Initialize Arduino with Nanpy
    if not settings.ARDUINO_SIMULATION:
        sensor = DallasTemperature(ARDUINO_TEMPERATURE_PIN)
        addr = sensor.getAddress(ARDUINO_TEMPERATURE_PIN)
        Arduino.pinMode(ARDUINO_HEAT_PIN, Arduino.OUTPUT)
        Arduino.pinMode(ARDUINO_STATUS_PIN, Arduino.OUTPUT)
        Arduino.pinMode(ARDUINO_COOL_PIN, Arduino.OUTPUT)
        Arduino.digitalWrite(ARDUINO_STATUS_PIN, Arduino.HIGH)
    else:
        # Set initial dummy temperature
        batch.temperature = 20  # Testing purpose only
        batch.save()
        batch = Batch.objects.get(id=batch.id)

    # Run Mashing proces
    while get_variable('active_mashingprocess_batch_id', 'None') == str(batch.id):
        # Measure data
        measured_data = {}
        if settings.ARDUINO_SIMULATION:
            measured_data['temp'] = get_dummy_temperature(batch)
        else:
            sensor.requestTemperatures()
            measured_data['temp'] = sensor.getTempC(addr)

        # Define actions depending on measured data
        batch = process_measured_data(batch.id, measured_data)

        # Send updates to arduino
        if not settings.ARDUINO_SIMULATION:
            send_updates_to_arduino(batch)

        # Send to logging department
        handle_logging(batch)

        # Delay
        sleep(DELAY_BETWEEN_MEASUREMENTS)

    Arduino.digitalWrite(ARDUINO_STATUS_PIN, Arduino.LOW)

    return 'Mashing proces ended'

# Update batch data according to measured data
def process_measured_data(batch_id, measured_data):
    # Ensure up to date batch
    batch = Batch.objects.get(id=batch_id)
    batch.temperature = float(measured_data['temp'])
    temp = float(measured_data['temp'])

    # current mashing action is heat/cool until next step is reached
    if batch.active_mashingstep_state == MASHSTEP_STATE_APPROACH:
        switch_to_stay = False

        # Cast to float
        target_temperature = float(batch.active_mashingstep.temperature)

        # Define direction if undefined and start appropriate action
        if batch.active_mashingstep_approach_direction == 'tbd':
            if temp < target_temperature:
               batch.active_mashingstep_approach_direction = 'heat'
               batch.heat = True
            else:
                batch.active_mashingstep_approach_direction = 'cool'
                batch.cool = True

        # Healing logic
        if batch.active_mashingstep_approach_direction == 'heat' and temp >= target_temperature:
            # Mashing step temperature reached
            switch_to_stay = True

        # Cooling logic
        if batch.active_mashingstep_approach_direction == 'cool'and temp <= target_temperature:
            # Mashing step temperature reached
            switch_to_stay = True

        # Mashing step temperature reached logic
        if switch_to_stay:
            # Switch to stay at temperature
            batch.active_mashingstep_state = MASHSTEP_STATE_STAY
            batch.heat = False
            batch.cool = False
            # Reset direction
            batch.active_mashingstep_approach_direction = 'tbd'

    elif batch.active_mashingstep_state == MASHSTEP_STATE_STAY:

        # Check if total time to spend in active mashing step is reached
        seconds_to_stay = int(batch.active_mashingstep.minutes) * 60
        now = datetime.now(pytz.utc)
        difference = now - batch.active_mashingstep_state_start
        if difference.total_seconds() >= seconds_to_stay:
            # Total time to spend in active mashing step reached
            # If active mashing step is latest, set status to finished
            active_mashingstep_index = batch.mashing_scheme.mashingstep_set.filter(id__lt=batch.active_mashingstep.id).count()
            try:
                # Activate next mashing step if available
                batch.active_mashingstep = batch.mashing_scheme.mashingstep_set.all()[active_mashingstep_index + 1]
                batch.active_mashingstep_state = MASHSTEP_STATE_APPROACH
            except:
                # Set state to finished
                batch.active_mashingstep_state = MASHSTEP_STATE_FINISHED

        else:
            # Total time spend not reached, check if actions are required to ensure current temperature
            target_temperature = float(batch.active_mashingstep.temperature)

            # TODO: Set actions if temperature goes out of bounds
            # if temp < (temp_to_stay - MAXIMUM_DEVIATION) and
    batch.save()
    return batch

def handle_logging(batch):
    chart_icon = None
    create_log = False

    try:
        last_log = MashLog.objects.filter(batch=batch).latest('id')
        now = datetime.now(pytz.utc)
        difference = now - last_log.created

        #  If DELAY_BETWEEN_LOGS is reached, log!
        if difference.total_seconds() >= DELAY_BETWEEN_LOGS:
            create_log = True

        # If state changed since previous log, log!
        if last_log.active_mashing_step_state != batch.active_mashingstep_state:
            create_log = True

            # Set chart icon
            if batch.active_mashingstep_state == MASHSTEP_STATE_STAY:
                active_mashingstep_index = batch.mashing_scheme.mashingstep_set.filter(id__lt=batch.active_mashingstep.id).count()
                chart_icon = 'start' + str(active_mashingstep_index + 1)
            elif batch.active_mashingstep_state == MASHSTEP_STATE_APPROACH:
                active_mashingstep_index = batch.mashing_scheme.mashingstep_set.filter(id__lt=batch.active_mashingstep.id).count()
                chart_icon = 'stop' + str(active_mashingstep_index)
            elif batch.active_mashingstep_state == MASHSTEP_STATE_FINISHED:
                chart_icon = 'finished'

    except MashLog.DoesNotExist:
        #  No logs found, log first one
        create_log = True

    if create_log:
        # Optionally, Log everything
        MashLog.objects.create(
            batch=batch,
            temperature=batch.temperature,
            active_mashing_step=batch.active_mashingstep,
            active_mashing_step_state=batch.active_mashingstep_state,
            heat=batch.heat,
            chart_icon=chart_icon
        )

# (Re)Set initial status for batch
def set_batch_defaults(batch):
    batch.active_mashingstep = batch.mashing_scheme.mashingstep_set.all().order_by('position')[0]
    batch.active_mashingstep_state = MASHSTEP_STATE_APPROACH
    batch.active_mashingstep_state_start = datetime.now(pytz.utc)
    batch.active_mashingstep_approach_direction = 'tbd'
    batch.heat = False
    batch.cool = False
    batch.save()

def send_updates_to_arduino(batch):
    if batch.heat:
        Arduino.digitalWrite(ARDUINO_HEAT_PIN, Arduino.HIGH)
    else:
        Arduino.digitalWrite(ARDUINO_HEAT_PIN, Arduino.LOW)

    if batch.cool:
        Arduino.digitalWrite(ARDUINO_COOL_PIN, Arduino.HIGH)
    else:
        Arduino.digitalWrite(ARDUINO_COOL_PIN, Arduino.LOW)

# Return dummy temp for testing based on heat/cool actions triggered
def get_dummy_temperature(batch):
    if batch.heat:
        # When heating, simulate steady temperature raising
        temp = "%.2f" % (batch.temperature + (random() / 10))
    else:
        # Nothing happening, simulate slow temperature lowering
        temp = "%.2f" % (batch.temperature - (random() / 150))
    return temp<|endoftext|>


<commit_before>"""
Django settings for project.
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os

from djzbar.settings import INFORMIX_EARL_TEST as INFORMIX_EARL
from djzbar.settings import JX_EARL_PROD
from djzbar.settings import JX_EARL_TEST
from djzbar.settings import INFORMIX_ODBC

# Debug
DEBUG = False
INFORMIX_DEBUG = 'debug'
ADMINS = (
    ('', ''),
)
MANAGERS = ADMINS

SECRET_KEY = ''
ALLOWED_HOSTS = []

LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'America/Chicago'
SITE_ID = 1
USE_I18N = False
USE_L10N = False
USE_TZ = False
DEFAULT_CHARSET = 'utf-8'
FILE_CHARSET = 'utf-8'
SERVER_URL = ''
API_URL = '{}/{}'.format(SERVER_URL, 'api')
LIVEWHALE_API_URL = 'https://{}'.format(SERVER_URL)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ROOT_DIR = os.path.dirname(__file__)
ADMIN_MEDIA_PREFIX = '/static/admin/'
STATIC_URL = '/static/djskeletor/'
ROOT_URL = '/djskeletor/'
MEDIA_ROOT = '{}/assets/'.format(ROOT_DIR)
STATIC_ROOT = '{}/static/'.format(ROOT_DIR)
MEDIA_URL = '{}assets/'.format(STATIC_URL)
UPLOADS_DIR = '{}files/'.format(MEDIA_ROOT)
UPLOADS_URL = '{}files/'.format(MEDIA_URL)
ROOT_URLCONF = 'djskeletor.core.urls'
WSGI_APPLICATION = 'djskeletor.wsgi.application'
STATICFILES_DIRS = ()
STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
DATABASES = {
    'default': {
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'NAME': 'django_djskeletor',
        'ENGINE': 'django.db.backends.mysql',
        'USER': '',
        'PASSWORD': ''
    },
}
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.humanize',
    'django.contrib.messages',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.staticfiles',
    'djskeletor.core',
    'djskeletor.myapp',
    # needed for template tags
    'djtools',
]
MIDDLEWARE_CLASSES = [
    'django.middleware.security.SecurityMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    # the following should be uncommented unless you are
    # embedding your apps in iframes
    #'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# template stuff
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
            '/data2/django_templates/djkorra/',
            '/data2/django_templates/djbootmin/',
            '/data2/django_templates/djcher/',
            '/data2/django_templates/django-djskins/',
            '/data2/livewhale/includes/',
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'debug':DEBUG,
            'context_processors': [
                'djtools.context_processors.sitevars',
                'django.contrib.auth.context_processors.auth',
                'django.template.context_processors.debug',
                'django.template.context_processors.media',
                'django.template.context_processors.static',
                'django.template.context_processors.request',
                'django.contrib.messages.context_processors.messages',
            ],
            #'loaders': [
            #    # insert your TEMPLATE_LOADERS here
            #]
        },
    },
]
# caching
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
        #'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        #'LOCATION': '127.0.0.1:11211',
        #'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        #'LOCATION': '/var/tmp/django_djskeletor_cache',
        #'TIMEOUT': 60*20,
        #'KEY_PREFIX': 'DJSKELETOR_',
        #'OPTIONS': {
        #    'MAX_ENTRIES': 80000,
        #}
    }
}
CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True
# LDAP Constants
LDAP_SERVER = ''
LDAP_SERVER_PWM = ''
LDAP_PORT = ''
LDAP_PORT_PWM = ''
LDAP_PROTOCOL = ''
LDAP_PROTOCOL_PWM = ''
LDAP_BASE = ''
LDAP_USER = ''
LDAP_PASS = ''
LDAP_EMAIL_DOMAIN = ''
LDAP_OBJECT_CLASS = ''
LDAP_OBJECT_CLASS_LIST = []
LDAP_GROUPS = {}
LDAP_RETURN = []
LDAP_RETURN_PWM = []
LDAP_ID_ATTR = ''
LDAP_CHALLENGE_ATTR = ''
LDAP_AUTH_USER_PK = False
# auth backends
AUTHENTICATION_BACKENDS = (
    'djauth.ldapBackend.LDAPBackend',
    'django.contrib.auth.backends.ModelBackend',
)
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]
LOGIN_URL = '{}accounts/login/'.format(ROOT_URL)
LOGOUT_URL = '{}accounts/logout/'.format(ROOT_URL)
LOGIN_REDIRECT_URL = ROOT_URL
USE_X_FORWARDED_HOST = True
#SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
SESSION_COOKIE_DOMAIN='.carthage.edu'
SESSION_COOKIE_NAME ='django_djskeletor_cookie'
SESSION_COOKIE_AGE = 86400
# SMTP settings
EMAIL_HOST = ''
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_FAIL_SILENTLY = False
DEFAULT_FROM_EMAIL = ''
SERVER_EMAIL = ''
SERVER_MAIL=''
# logging
LOG_FILEPATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'logs/')
DEBUG_LOG_FILENAME = LOG_FILEPATH + 'debug.log'
INFO_LOG_FILENAME = LOG_FILEPATH + 'info.log'
ERROR_LOG_FILENAME = LOG_FILEPATH + 'error.log'
CUSTOM_LOG_FILENAME = LOG_FILEPATH + 'custom.log'
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format' : '[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s',
            'datefmt' : '%Y/%b/%d %H:%M:%S'
        },
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s',
            'datefmt' : '%Y/%b/%d %H:%M:%S'
        },
        'custom': {
            'format': '%(asctime)s: %(levelname)s: %(message)s',
            'datefmt' : '%m/%d/%Y %I:%M:%S %p'
        }
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        },
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'custom_logfile': {
            'level':'ERROR',
            'filters': ['require_debug_true'], # do not run error logger in production
            'class': 'logging.FileHandler',
            'filename': CUSTOM_LOG_FILENAME,
            'formatter': 'standard',
        },
        'info_logfile': {
            'level':'INFO',
            'class':'logging.handlers.RotatingFileHandler',
            'backupCount': 10,
            'maxBytes': 50000,
            'filters': ['require_debug_false'], # run logger in production
            'filename': INFO_LOG_FILENAME,
            'formatter': 'standard',
        },
        'debug_logfile': {
            'level': 'DEBUG',
            'filters': ['require_debug_true'], # do not run debug logger in production
            'class': 'logging.FileHandler',
            'filename': DEBUG_LOG_FILENAME,
            'formatter': 'verbose'
        },
        'error_logfile': {
            'level': 'ERROR',
            'filters': ['require_debug_true'], # do not run error logger in production
            'class': 'logging.FileHandler',
            'filename': ERROR_LOG_FILENAME,
            'formatter': 'verbose'
        },
        'console':{
            'level':'INFO',
            'class':'logging.StreamHandler',
            'formatter': 'standard'
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'include_html': True,
            'class': 'django.utils.log.AdminEmailHandler'
        }
    },
    'loggers': {
        'provisioning_logger': {
            'handlers': ['error_logfile'],
            'level': 'ERROR'
         },
        'error_logger': {
            'handlers': ['error_logfile'],
            'level': 'ERROR'
         },
        'info_logger': {
            'handlers': ['info_logfile'],
            'level': 'INFO'
        },
        'debug_logger': {
            'handlers':['debug_logfile'],
            'propagate': True,
            'level':'DEBUG',
        },
        'django': {
            'handlers':['console'],
            'propagate': True,
            'level':'WARN',
        },
        'django.db.backends': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False,
        },
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
    }
}
<commit_msg>log format updates<commit_after>"""
Django settings for project.
"""

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
import os

from djzbar.settings import INFORMIX_EARL_TEST as INFORMIX_EARL
from djzbar.settings import JX_EARL_PROD
from djzbar.settings import JX_EARL_TEST
from djzbar.settings import INFORMIX_ODBC

# Debug
DEBUG = False
INFORMIX_DEBUG = 'debug'
ADMINS = (
    ('', ''),
)
MANAGERS = ADMINS

SECRET_KEY = ''
ALLOWED_HOSTS = []

LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'America/Chicago'
SITE_ID = 1
USE_I18N = False
USE_L10N = False
USE_TZ = False
DEFAULT_CHARSET = 'utf-8'
FILE_CHARSET = 'utf-8'
SERVER_URL = ''
API_URL = '{}/{}'.format(SERVER_URL, 'api')
LIVEWHALE_API_URL = 'https://{}'.format(SERVER_URL)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ROOT_DIR = os.path.dirname(__file__)
ADMIN_MEDIA_PREFIX = '/static/admin/'
STATIC_URL = '/static/djskeletor/'
ROOT_URL = '/djskeletor/'
MEDIA_ROOT = '{}/assets/'.format(ROOT_DIR)
STATIC_ROOT = '{}/static/'.format(ROOT_DIR)
MEDIA_URL = '{}assets/'.format(STATIC_URL)
UPLOADS_DIR = '{}files/'.format(MEDIA_ROOT)
UPLOADS_URL = '{}files/'.format(MEDIA_URL)
ROOT_URLCONF = 'djskeletor.core.urls'
WSGI_APPLICATION = 'djskeletor.wsgi.application'
STATICFILES_DIRS = ()
STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
#    'django.contrib.staticfiles.finders.DefaultStorageFinder',
)
DATABASES = {
    'default': {
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'NAME': 'django_djskeletor',
        'ENGINE': 'django.db.backends.mysql',
        'USER': '',
        'PASSWORD': ''
    },
}
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.humanize',
    'django.contrib.messages',
    'django.contrib.sessions',
    'django.contrib.sites',
    'django.contrib.staticfiles',
    'djskeletor.core',
    'djskeletor.myapp',
    # needed for template tags
    'djtools',
]
MIDDLEWARE_CLASSES = [
    'django.middleware.security.SecurityMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    # the following should be uncommented unless you are
    # embedding your apps in iframes
    #'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# template stuff
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
            '/data2/django_templates/djkorra/',
            '/data2/django_templates/djbootmin/',
            '/data2/django_templates/djcher/',
            '/data2/django_templates/django-djskins/',
            '/data2/livewhale/includes/',
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'debug':DEBUG,
            'context_processors': [
                'djtools.context_processors.sitevars',
                'django.contrib.auth.context_processors.auth',
                'django.template.context_processors.debug',
                'django.template.context_processors.media',
                'django.template.context_processors.static',
                'django.template.context_processors.request',
                'django.contrib.messages.context_processors.messages',
            ],
            #'loaders': [
            #    # insert your TEMPLATE_LOADERS here
            #]
        },
    },
]
# caching
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
        #'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        #'LOCATION': '127.0.0.1:11211',
        #'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        #'LOCATION': '/var/tmp/django_djskeletor_cache',
        #'TIMEOUT': 60*20,
        #'KEY_PREFIX': 'DJSKELETOR_',
        #'OPTIONS': {
        #    'MAX_ENTRIES': 80000,
        #}
    }
}
CACHE_MIDDLEWARE_ANONYMOUS_ONLY = True
# LDAP Constants
LDAP_SERVER = ''
LDAP_SERVER_PWM = ''
LDAP_PORT = ''
LDAP_PORT_PWM = ''
LDAP_PROTOCOL = ''
LDAP_PROTOCOL_PWM = ''
LDAP_BASE = ''
LDAP_USER = ''
LDAP_PASS = ''
LDAP_EMAIL_DOMAIN = ''
LDAP_OBJECT_CLASS = ''
LDAP_OBJECT_CLASS_LIST = []
LDAP_GROUPS = {}
LDAP_RETURN = []
LDAP_RETURN_PWM = []
LDAP_ID_ATTR = ''
LDAP_CHALLENGE_ATTR = ''
LDAP_AUTH_USER_PK = False
# auth backends
AUTHENTICATION_BACKENDS = (
    'djauth.ldapBackend.LDAPBackend',
    'django.contrib.auth.backends.ModelBackend',
)
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]
LOGIN_URL = '{}accounts/login/'.format(ROOT_URL)
LOGOUT_URL = '{}accounts/logout/'.format(ROOT_URL)
LOGIN_REDIRECT_URL = ROOT_URL
USE_X_FORWARDED_HOST = True
#SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
SESSION_COOKIE_DOMAIN='.carthage.edu'
SESSION_COOKIE_NAME ='django_djskeletor_cookie'
SESSION_COOKIE_AGE = 86400
# SMTP settings
EMAIL_HOST = ''
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_FAIL_SILENTLY = False
DEFAULT_FROM_EMAIL = ''
SERVER_EMAIL = ''
SERVER_MAIL=''
# logging
LOG_FILEPATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'logs/')
DEBUG_LOG_FILENAME = LOG_FILEPATH + 'debug.log'
INFO_LOG_FILENAME = LOG_FILEPATH + 'info.log'
ERROR_LOG_FILENAME = LOG_FILEPATH + 'error.log'
CUSTOM_LOG_FILENAME = LOG_FILEPATH + 'custom.log'
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format' : '[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s',
            'datefmt' : '%Y/%b/%d %H:%M:%S'
        },
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s',
            'datefmt' : '%Y/%b/%d %H:%M:%S'
        },
        'custom': {
            'format': '%(asctime)s: %(levelname)s: %(message)s',
            'datefmt' : '%m/%d/%Y %I:%M:%S %p'
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse'
        },
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'custom_logfile': {
            'level':'ERROR',
            'filters': ['require_debug_true'], # do not run error logger in production
            'class': 'logging.FileHandler',
            'filename': CUSTOM_LOG_FILENAME,
            'formatter': 'custom',
        },
        'info_logfile': {
            'level':'INFO',
            'class':'logging.handlers.RotatingFileHandler',
            'backupCount': 10,
            'maxBytes': 50000,
            'filters': ['require_debug_false'], # run logger in production
            'filename': INFO_LOG_FILENAME,
            'formatter': 'simple',
        },
        'debug_logfile': {
            'level': 'DEBUG',
            'filters': ['require_debug_true'], # do not run debug logger in production
            'class': 'logging.FileHandler',
            'filename': DEBUG_LOG_FILENAME,
            'formatter': 'verbose'
        },
        'error_logfile': {
            'level': 'ERROR',
            'filters': ['require_debug_true'], # do not run error logger in production
            'class': 'logging.FileHandler',
            'filename': ERROR_LOG_FILENAME,
            'formatter': 'verbose'
        },
        'console':{
            'level':'INFO',
            'class':'logging.StreamHandler',
            'formatter': 'standard'
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'include_html': True,
            'class': 'django.utils.log.AdminEmailHandler'
        }
    },
    'loggers': {
        'provisioning_logger': {
            'handlers': ['error_logfile'],
            'level': 'ERROR'
         },
        'error_logger': {
            'handlers': ['error_logfile'],
            'level': 'ERROR'
         },
        'info_logger': {
            'handlers': ['info_logfile'],
            'level': 'INFO'
        },
        'debug_logger': {
            'handlers':['debug_logfile'],
            'propagate': True,
            'level':'DEBUG',
        },
        'django': {
            'handlers':['console'],
            'propagate': True,
            'level':'WARN',
        },
        'django.db.backends': {
            'handlers': ['console'],
            'level': 'DEBUG',
            'propagate': False,
        },
        'django.request': {
            'handlers': ['mail_admins'],
            'level': 'ERROR',
            'propagate': True,
        },
    }
}
<|endoftext|>


<commit_before>
from game_state import *
from player import *
from rules import *

import pdb

class Game():

    def __init__(self, rules=None, player1=None, player2=None):
        self.rules = rules
        if rules != None:
            self.current_state = GameState(self)

        self.player = [None, player1, player2]
        if player1 != None:
            player1.attach_to_game(self)
        if player2 != None:
            player2.attach_to_game(self)
        self.move_history = []
        self.autosave_filename = None

    # TODO: get_size for consistency
    def size(self):
        return self.rules.size

    def get_current_state(self):
        return self.current_state

    def get_board(self):
        return self.current_state.board

    def off_board(self, pos):
        return self.get_board().off_board(pos)

    def get_player(self, player_number):
        return self.player[player_number]

    def get_player_name(self, player_number):
        return self.player[player_number].get_name()

    def get_current_player(self):
        return self.current_state.to_move_player()

    def to_move_colour(self):
        return self.current_state.to_move_colour()

    def prompt_for_action(self, gui):
        if self.finished():
            return "Game was won by: %s" % self.winner_name()
        return self.get_current_player().prompt_for_action(self, gui)

    def get_action(self, gui):
        return self.get_current_player().get_action(self, gui)

    # Not sure if these should even delegate
    def get_move_number(self):
        return self.current_state.get_move_number()

    def set_move_number(self, turn):
        self.current_state.set_move_number(turn)

    def get_captured(self, player_number):
        return self.current_state.get_captured(player_number)

    def set_captured(self, player_number, pieces):
        return self.current_state.set_captured(player_number, pieces)

    def make_move(self, move):
        # Record this, then save to a file if required
        self.move_history = self.move_history[:self.get_move_number()-1]
        self.current_state.make_move(move)
        self.move_history.append(move)
        if self.autosave_filename != None:
            self.save_history()

    def finished(self):
        return self.current_state.get_won_by() > 0

    def winner(self):
        return self.current_state.get_won_by()

    def winner_name(self):
        return self.player[self.current_state.get_won_by()-1]

    def get_move(self, move_number):
        return self.move_history[move_number-1]

    def go_forwards_one(self):
        self.go_to_move(self.get_move_number() + 1)

    def go_backwards_one(self):
        self.go_to_move(self.get_move_number() - 1)

    def go_to_move(self, move_number):
        current_move = self.get_move_number()
        if move_number < current_move:
            # Have to go back to the start, and replay all the moves,
            # otherwise the GUI and AI would need to support undo. TODO?
            gs = self.current_state
            gs.reset(self)

            for i in range(move_number-1):
                gs.make_move(self.move_history[i])
        else:
            for i in range(current_move-1, move_number-1):
                if i > len(self.move_history) - 1:
                    return
                gs = self.current_state
                gs.make_move(self.move_history[i])

    def history_to_str(self):
        game_str = self.game_header()
        for i in range(len(self.move_history)):
            move = self.move_history[i]
            # TODO: Could be slow for very long games (n**2)
            game_str = game_str + "%s. %s\n" % (i+1, move)
        return game_str

    def save_history(self):
        game_str = self.history_to_str()
        filename = self.autosave_filename
        game_file = open(filename, "w")
        # TODO: Append to the file
        game_file.write(game_str)
        game_file.close()

    def configure_from_str(self, s):
        player_line, size_line, rules_line, the_rest = s.split('\n', 3)

        # TODO: This is ugly - these values will be replaced shortly
        if self.player[BLACK] == None:
            self.player = [None, Player("Black"), Player("White")]
        if self.rules == None:
            self.rules = Rules(5,"standard")

        players = player_line.split(" versus ", 1)
        self.player[1].name = players[0]
        self.player[2].name = players[1]

        side_length, ignored = size_line.split('x', 1)
        self.rules.size = int(side_length)

        rules_type, ignored = rules_line.split(" rules", 1)
        self.rules.type_str = rules_type

        return the_rest

    def game_header(self):
        player_line = "%s versus %s" % \
                (self.get_player_name(1), self.get_player_name(2))
        size_line = "%sx%s"% (self.size(), self.size())
        rules_line = "%s rules\n" % self.rules.type_str
        return "\n".join([player_line, size_line, rules_line])

    def load_game(self, game_str):
        remainder = self.configure_from_str(game_str)
        self.load_moves(remainder)

    def load_moves(self, game_str):
        # e.g. "1. (4,4)\n2. (3,3)\n"
        #try:
            gs = game_str.strip()

            for line in gs.split('\n'):
                fields = line.split(' ', 1)
                move_number = int(fields[0][:-1])
                move_pos_tuple = fields[1]
                move_pos_str = move_pos_tuple[1:-1]
                coords = move_pos_str.split(',')
                move = int(coords[0]), int(coords[1])
                self.make_move(move)
        #except:
        #    raise IncompatibleFileException("Could not read line: %s" % line)

<commit_msg>Boundary case<commit_after>
from game_state import *
from player import *
from rules import *

import pdb

class Game():

    def __init__(self, rules=None, player1=None, player2=None):
        self.rules = rules
        if rules != None:
            self.current_state = GameState(self)

        self.player = [None, player1, player2]
        if player1 != None:
            player1.attach_to_game(self)
        if player2 != None:
            player2.attach_to_game(self)
        self.move_history = []
        self.autosave_filename = None

    # TODO: get_size for consistency
    def size(self):
        return self.rules.size

    def get_current_state(self):
        return self.current_state

    def get_board(self):
        return self.current_state.board

    def off_board(self, pos):
        return self.get_board().off_board(pos)

    def get_player(self, player_number):
        return self.player[player_number]

    def get_player_name(self, player_number):
        return self.player[player_number].get_name()

    def get_current_player(self):
        return self.current_state.to_move_player()

    def to_move_colour(self):
        return self.current_state.to_move_colour()

    def prompt_for_action(self, gui):
        if self.finished():
            return "Game was won by: %s" % self.winner_name()
        return self.get_current_player().prompt_for_action(self, gui)

    def get_action(self, gui):
        return self.get_current_player().get_action(self, gui)

    # Not sure if these should even delegate
    def get_move_number(self):
        return self.current_state.get_move_number()

    def set_move_number(self, turn):
        self.current_state.set_move_number(turn)

    def get_captured(self, player_number):
        return self.current_state.get_captured(player_number)

    def set_captured(self, player_number, pieces):
        return self.current_state.set_captured(player_number, pieces)

    def make_move(self, move):
        # Record this, then save to a file if required
        if len(self.move_history) > 0:
            self.move_history = self.move_history[:self.get_move_number()-1]
        self.current_state.make_move(move)
        self.move_history.append(move)
        if self.autosave_filename != None:
            self.save_history()

    def finished(self):
        return self.current_state.get_won_by() > 0

    def winner(self):
        return self.current_state.get_won_by()

    def winner_name(self):
        return self.player[self.current_state.get_won_by()-1]

    def get_move(self, move_number):
        return self.move_history[move_number-1]

    def go_forwards_one(self):
        self.go_to_move(self.get_move_number() + 1)

    def go_backwards_one(self):
        self.go_to_move(self.get_move_number() - 1)

    def go_to_move(self, move_number):
        current_move = self.get_move_number()
        if move_number < current_move:
            # Have to go back to the start, and replay all the moves,
            # otherwise the GUI and AI would need to support undo. TODO?
            gs = self.current_state
            gs.reset(self)

            for i in range(move_number-1):
                gs.make_move(self.move_history[i])
        else:
            for i in range(current_move-1, move_number-1):
                if i > len(self.move_history) - 1:
                    return
                gs = self.current_state
                gs.make_move(self.move_history[i])

    def history_to_str(self):
        game_str = self.game_header()
        for i in range(len(self.move_history)):
            move = self.move_history[i]
            # TODO: Could be slow for very long games (n**2)
            game_str = game_str + "%s. %s\n" % (i+1, move)
        return game_str

    def save_history(self):
        game_str = self.history_to_str()
        filename = self.autosave_filename
        game_file = open(filename, "w")
        # TODO: Append to the file
        game_file.write(game_str)
        game_file.close()

    def configure_from_str(self, s):
        player_line, size_line, rules_line, the_rest = s.split('\n', 3)

        # TODO: This is ugly - these values will be replaced shortly
        if self.player[BLACK] == None:
            self.player = [None, Player("Black"), Player("White")]
        if self.rules == None:
            self.rules = Rules(5,"standard")

        players = player_line.split(" versus ", 1)
        self.player[1].name = players[0]
        self.player[2].name = players[1]

        side_length, ignored = size_line.split('x', 1)
        self.rules.size = int(side_length)

        rules_type, ignored = rules_line.split(" rules", 1)
        self.rules.type_str = rules_type

        return the_rest

    def game_header(self):
        player_line = "%s versus %s" % \
                (self.get_player_name(1), self.get_player_name(2))
        size_line = "%sx%s"% (self.size(), self.size())
        rules_line = "%s rules\n" % self.rules.type_str
        return "\n".join([player_line, size_line, rules_line])

    def load_game(self, game_str):
        remainder = self.configure_from_str(game_str)
        self.load_moves(remainder)

    def load_moves(self, game_str):
        # e.g. "1. (4,4)\n2. (3,3)\n"
        #try:
            gs = game_str.strip()

            for line in gs.split('\n'):
                fields = line.split(' ', 1)
                move_number = int(fields[0][:-1])
                move_pos_tuple = fields[1]
                move_pos_str = move_pos_tuple[1:-1]
                coords = move_pos_str.split(',')
                move = int(coords[0]), int(coords[1])
                self.make_move(move)
        #except:
        #    raise IncompatibleFileException("Could not read line: %s" % line)

<|endoftext|>


<commit_before>#!/usr/bin/env python

from grid import Grid
from tetraminos import *

class Game(object):

    def __init__(self):

        self.grid = Grid()
        self.active_tet = Tetramino()
        self.west  = None
        self.east  = None
        self.south = None

    def check_for_collisions(self):

        grid_status = []
        for row_num, row in enumerate(self.grid.board):
            for col, cell in enumerate(row):
                if cell:
                    grid_status.append((row_num, col))

        tet_status = []

        for row_num, row in enumerate(self.active_tet.shape):
            for col, cell in enumerate(row):
                if cell:
                    r = row_num + self.active_tet.south
                    c = col + self.active_tet.west
                    tet_status.append((r, c))

        if set(grid_status).intersection(set(tet_status)):
            return True

    def move_tet_west(self):

        western_edges = [cell[0] for cell in self.active_tet.shape]

        if self.active_tet.west == 0 and any(western_edges):

            pass

        elif self.active_tet.west == 0 and not any(western_edges):

            for index, row in enumerate(self.active_tet.shape):
                self.active_tet.shape[index].pop(0)

            self.active_tet.west = self.active_tet.west - 1
            self.active_tet.east = self.active_tet.east - 1

        elif len(self.active_tet.shape[0]) != self.active_tet.size:

            for index, row in enumerate(self.active_tet.shape):
                self.active_tet.shape[index].append(None)

            self.active_tet.west = self.active_tet.west - 1
            self.active_tet.east = self.active_tet.east - 1

            collision = self.check_for_collisions()

            if collision:

                for index, row in enumerate(self.active_tet.shape):
                    self.active_tet.shape[index].append(None)
                self.active_tet.west = self.active_tet.west + 1
                self.active_tet.east = self.active_tet.east + 1
        else:
            self.active_tet.west = self.active_tet.west - 1
            self.active_tet.east = self.active_tet.east - 1

            collision = self.check_for_collisions()
            if collision:
                self.active_tet.west = self.active_tet.west + 1
                self.active_tet.east = self.active_tet.east + 1

    def move_tet_east(self):

        eastern_edges = [cell[-1] for cell in self.active_tet.shape]

        if self.active_tet.east >= 10 and any(eastern_edges):

            pass

        elif self.active_tet.east >= 10 and not any(eastern_edges):

            for index, row in enumerate(self.active_tet.shape):
                self.active_tet.shape[index].pop(2)

            self.active_tet.west = self.active_tet.west + 1
            self.active_tet.east = self.active_tet.east + 1

        elif len(self.active_tet.shape[0]) != self.active_tet.size:

            for index, row in enumerate(self.active_tet.shape):
                self.active_tet.shape[index].insert(0, None)

            self.active_tet.west = self.active_tet.west + 1
            self.active_tet.east = self.active_tet.east + 1

        else:
            self.active_tet.west = self.active_tet.west + 1
            self.active_tet.east = self.active_tet.east + 1

            collision = self.check_for_collisions()
            if collision:
                self.active_tet.west = self.active_tet.west - 1
                self.active_tet.east = self.active_tet.east - 1

    def move_tet_south(self):

        southern_border = self.active_tet.shape[-1]

        if self.active_tet.south + len(self.active_tet.shape) > 21 and any(southern_border):

            pass

        elif self.active_tet.south + len(self.active_tet.shape) == 21 and not any(southern_border):

            self.active_tet.shape.pop(-1)
            self.active_tet.south = self.active_tet.south + 1

            collision = self.check_for_collisions()

            if collision:
                self.active_tet.south = self.active_tet.south - 1
                self.active_tet.shape.append([None]*self.active_tet.size)
        else:
            self.active_tet.south = self.active_tet.south + 1
            collision = self.check_for_collisions()
            if collision:
                self.active_tet.south = self.active_tet.south - 1

    def hard_drop(self):

        #self.get_coordinates()

        distance_to_floor = 25 - self.active_tet.south - len(self.active_tet.shape)

        count = 0
        while count != distance_to_floor:
            count += 1
            self.move_tet_south()

    def get_coordinates(self):

        self.west = self.active_tet.west
        self.east = self.active_tet.east
        self.south = self.active_tet.south

    def set_board(self, case_change):

        self.get_coordinates()

        if self.active_tet.shape:

            if self.west < 0:
                self.active_tet.west = 0
                self.active_tet.east = self.active_tet.west + len(self.active_tet.shape[0])
                self.get_coordinates()

            for index, row in enumerate(self.active_tet.shape):

                for num, cell in enumerate(row):

                    if cell and case_change == 'lower':
                        self.active_tet.shape[index][num] = cell.lower()
                    elif cell:
                        self.active_tet.shape[index][num] = cell.upper()
                    else:
                        pass

                grid_row = self.grid.board[index+self.south][self.west:self.east]
                for i, c in enumerate(row):
                    if c:
                        grid_row[i] = c

                self.grid.board[index+self.south][self.west:self.east] = grid_row

    def place_tets(self):

        self.set_board('lower')

    def set_active_tet(self, block):

        if self.active_tet:
            self.place_tets()

        i_tet = I_tet()
        o_tet = O_tet()
        z_tet = Z_tet()
        s_tet = S_tet()
        j_tet = J_tet()
        l_tet = L_tet()
        t_tet = T_tet()

        shapes = {'I': i_tet,
                  'O': o_tet,
                  'Z': z_tet,
                  'S': s_tet,
                  'J': j_tet,
                  'L': l_tet,
                  'T': t_tet}

        return shapes[block]<commit_msg>Clean up move_tet_west function to reduce redunancy<commit_after>#!/usr/bin/env python

from grid import Grid
from tetraminos import *

class Game(object):

    def __init__(self):

        self.grid = Grid()
        self.active_tet = Tetramino()
        self.west  = None
        self.east  = None
        self.south = None

    def check_for_collisions(self):

        grid_status = []
        for row_num, row in enumerate(self.grid.board):
            for col, cell in enumerate(row):
                if cell:
                    grid_status.append((row_num, col))

        tet_status = []
        for row_num, row in enumerate(self.active_tet.shape):
            for col, cell in enumerate(row):
                if cell:
                    r = row_num + self.active_tet.south
                    c = col + self.active_tet.west
                    tet_status.append((r, c))

        if set(grid_status).intersection(set(tet_status)):
            return True

    def move_tet_west(self):

        western_edges = [cell[0] for cell in self.active_tet.shape]

        if self.active_tet.west == 0 and any(western_edges):

            pass

        elif self.active_tet.west == 0 and not any(western_edges):

            for index, row in enumerate(self.active_tet.shape):
                self.active_tet.shape[index].pop(0)

            self.active_tet.west = self.active_tet.west - 1
            self.active_tet.east = self.active_tet.east - 1

        else:

            if len(self.active_tet.shape[0]) != self.active_tet.size:

                for index, row in enumerate(self.active_tet.shape):
                    self.active_tet.shape[index].append(None)

            self.active_tet.west = self.active_tet.west - 1
            self.active_tet.east = self.active_tet.east - 1

            collision = self.check_for_collisions()

            if collision:

                self.move_tet_east()

    def move_tet_east(self):

        eastern_edges = [cell[-1] for cell in self.active_tet.shape]

        if self.active_tet.east >= 10 and any(eastern_edges):

            pass

        elif self.active_tet.east >= 10 and not any(eastern_edges):

            for index, row in enumerate(self.active_tet.shape):
                self.active_tet.shape[index].pop(2)

            self.active_tet.west = self.active_tet.west + 1
            self.active_tet.east = self.active_tet.east + 1

        elif len(self.active_tet.shape[0]) != self.active_tet.size:

            for index, row in enumerate(self.active_tet.shape):
                self.active_tet.shape[index].insert(0, None)

            self.active_tet.west = self.active_tet.west + 1
            self.active_tet.east = self.active_tet.east + 1

        else:
            self.active_tet.west = self.active_tet.west + 1
            self.active_tet.east = self.active_tet.east + 1

            collision = self.check_for_collisions()

            if collision:
                self.move_tet_west()

    def move_tet_south(self):

        southern_border = self.active_tet.shape[-1]

        if self.active_tet.south + len(self.active_tet.shape) > 21 and any(southern_border):

            pass

        elif self.active_tet.south + len(self.active_tet.shape) == 21 and not any(southern_border):

            self.active_tet.shape.pop(-1)
            self.active_tet.south = self.active_tet.south + 1

            collision = self.check_for_collisions()

            if collision:
                self.active_tet.south = self.active_tet.south - 1
                self.active_tet.shape.append([None]*self.active_tet.size)
        else:
            self.active_tet.south = self.active_tet.south + 1
            collision = self.check_for_collisions()
            if collision:
                self.active_tet.south = self.active_tet.south - 1

    def hard_drop(self):

        distance_to_floor = 25 - self.active_tet.south - len(self.active_tet.shape)

        count = 0
        while count != distance_to_floor:
            count += 1
            self.move_tet_south()

    def get_coordinates(self):

        self.west = self.active_tet.west
        self.east = self.active_tet.east
        self.south = self.active_tet.south

    def set_board(self, case_change):

        self.get_coordinates()

        if self.active_tet.shape:

            if self.west < 0:
                self.active_tet.west = 0
                self.active_tet.east = self.active_tet.west + len(self.active_tet.shape[0])
                self.get_coordinates()

            for index, row in enumerate(self.active_tet.shape):

                for num, cell in enumerate(row):

                    if cell and case_change == 'lower':
                        self.active_tet.shape[index][num] = cell.lower()
                    elif cell:
                        self.active_tet.shape[index][num] = cell.upper()
                    else:
                        pass

                grid_row = self.grid.board[index+self.south][self.west:self.east]
                for i, c in enumerate(row):
                    if c:
                        grid_row[i] = c

                self.grid.board[index+self.south][self.west:self.east] = grid_row

    def place_tets(self):

        self.set_board('lower')

    def set_active_tet(self, block):

        if self.active_tet:
            self.place_tets()

        i_tet = I_tet()
        o_tet = O_tet()
        z_tet = Z_tet()
        s_tet = S_tet()
        j_tet = J_tet()
        l_tet = L_tet()
        t_tet = T_tet()

        shapes = {'I': i_tet,
                  'O': o_tet,
                  'Z': z_tet,
                  'S': s_tet,
                  'J': j_tet,
                  'L': l_tet,
                  'T': t_tet}

        return shapes[block]<|endoftext|>


<commit_before>import numpy as np
import pandas as pd

def sample_rows(total, sampling_data, with_replace=True, accounting_data=None, foreign_key_column=None, max_iterations=50):
	"""
	comments to go here...

    todo: make the accounting stuff either a delegate or a computed column??
	"""

	# simplest case, just return n random rows from the sampling frame
	if accounting_data is None:
		return sampling_data.loc[np.random.choice(sampling_data.index.values, total, replace=with_replace)].copy()

	# determine avg number of accounting records per sample (e.g. persons per household, jobs per business)
	# todo: throw an error if ther sampling row count is larger than the accounting row count
	per_sample = len(accounting_data.index) / (1.0 * len(sampling_data.index))

	# do the intial sample
	num_samples = int(math.ceil(math.fabs(total) / per_sample))
    if with_replace:
    	sample_idx = sampling_data.index.values
        sample_ids = np.random.choice(sample_idx, num_samples)
    else:
    	sample_idx = np.random.permutation(sampling_data.index.values)
        sample_ids = sample_idx[0:num_samples]
        samplePos = num_samples

    sample_rows = sampling_data.loc[sample_ids].copy()
    curr_total = len(sample_rows.merge(accounting_data, left_index=True, right_on=foreign_key_column).index)

	# iteratively refine the sample until we match the accounting total
	for i in range(0, max_iterations)

		# keep going if we haven't hit the control
        remaining = total - curr_total
        if remaining == 0: break
        num_samples = int(math.ceil(math.fabs(remaining) / per_sample))

        if remaining > 0:
            # we're short, keep sampling
            if with_replace:
            	curr_ids = np.random.choice(sample_idx, num_samples)
            else:
            	curr_ids = sample_idx[samplePos:samplePos + num_samples]
            	samplePos += num_samples
        else:
            # we've overshot, remove from existing samples (FIFO)
            # if replacement is false do these need to go back into the indexes?
            curr_rows = sample_rows[:numSamples]
            sample_rows = sampleRows[numSamples:]
            currTotal -= len(currRows.merge(child_data, left_index=True, right_on=key_field).index)

    return sample_rows.copy()<commit_msg>tweak and debug sample_rows method<commit_after>import math
import numpy as np
import pandas as pd

def sample_rows(total, data, replace=True, accounting_column=None, max_iterations=50):
    """
    Samples and returns rows from a data frame while matching a desired control total. The total may represent a simple
    row count or may attempt to match a sum/quantity from an accounting column.

    Parameters
    ----------
    total : int
        The control total the sampled rows will attempt to match.
    data: pandas.DataFrame
        Table to sample from.
    replace: bool, optional, default True
        Indicates if sampling with or without replacement.
    accounting_column: string, optional
        Name of column with accounting totals/quanties to apply towards the control. If not provided
        then row counts will be used for accounting.
    max_iterations: int, optional, default 50
        When using an accounting attribute, the maximum number of sampling iterations
        that will be applied.

    Returns
    -------
    sample_rows : pandas.DataFrame
        Table containing the sample.
    num_iterations : int
        Number of iterations it took to hit the control (temporary for testing), only
        used when accounting_column is provided
    """

    # simplest case, just return n random rows
    if accounting_column is None:
        if replace == False and total > len(data.index.values):
            raise ValueError('Control total exceeds the available samples')
        return data.loc[np.random.choice(data.index.values, total, replace=replace)].copy()

    # make sure this is even feasible
    if replace == False and total > data[accounting_column].sum():
        raise ValueError('Control total exceeds the available samples')

    # determine avg number of accounting items per sample (e.g. persons per household, jobs per business)
    per_sample = data[accounting_column].sum() / (1.0 * len(data.index.values))

    # do the intial sample
    num_samples = int(math.ceil(total / per_sample))
    if replace:
        sample_idx = data.index.values
        sample_ids = np.random.choice(sample_idx, num_samples)
    else:
        sample_idx = np.random.permutation(data.index.values)
        sample_ids = sample_idx[0:num_samples]
        sample_pos = num_samples

    sample_rows = data.loc[sample_ids].copy()
    curr_total = sample_rows[accounting_column].sum()

    # iteratively refine the sample until we match the accounting total
    for i in range(0, max_iterations):

        # keep going if we haven't hit the control
        remaining = total - curr_total
        if remaining == 0: break
        num_samples = int(math.ceil(math.fabs(remaining) / per_sample))

        if remaining > 0:
            # we're short, keep sampling
            if replace:
                curr_ids = np.random.choice(sample_idx, num_samples)
            else:
                curr_ids = sample_idx[sample_pos:sample_pos + num_samples]
                sample_pos += num_samples

            curr_rows = data.loc[curr_ids].copy()
            sample_rows = pd.concat([sample_rows, curr_rows])
            curr_total += curr_rows[accounting_column].sum()
        else:
            # we've overshot, remove from existing samples (FIFO)
            curr_rows = sample_rows[:num_samples]
            sample_rows = sample_rows[num_samples:]
            curr_total -= curr_rows[accounting_column].sum()
            if not replace: np.append(sample_idx, curr_rows.index.values)

    return sample_rows.copy(), i + 1<|endoftext|>


<commit_before># This file is part of the pyMOR project (http://www.pymor.org).
# Copyright Holders: Rene Milk, Stephan Rave, Felix Schindler
# License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

from __future__ import absolute_import, division, print_function
import time
from datetime import datetime

from pymor.core import cache
from pymortests.base import TestInterface, runmodule

SLEEP_SECONDS = 0.2

class IamMemoryCached(cache.CacheableInterface):

    @cache.cached
    def me_takey_long_time(self, arg):
        time.sleep(SLEEP_SECONDS)
        return arg

class IamDiskCached(cache.CacheableInterface):

    def __init__(self):
        self.cache_region = 'disk'

    @cache.cached
    def me_takey_long_time(self, arg):
        time.sleep(SLEEP_SECONDS)
        return arg

class IamLimitedCached(cache.CacheableInterface):

    def __init__(self, region='disk'):
        self.cache_region = region

    @cache.cached
    def me_takey_no_time(self, arg):
        return int(arg)

class IWillBeCopied(cache.CacheableInterface):

    def __init__(self):
        super(IWillBeCopied, self).__init__()

    @cache.cached
    def my_id(self, x):
        return id(self)

class TestCache(TestInterface):

    def test_runtime(self):
        for Class in [IamMemoryCached, IamDiskCached]:
            r = Class()
            for val in ['koko', 'koko', 'other']:
                int0 = datetime.now()
                r.me_takey_long_time(val)
                int1 = datetime.now()
                self.logger.info(int1 - int0)

    # def test_limit(self):
    #     for c in [IamLimitedCached('memory'),
    #               IamLimitedCached('disk')]:
    #         for i in range(25):
    #             c._cache_region.backend.print_limit()
    #             c.me_takey_no_time(i)
    #             c._cache_region.backend.print_limit()

    # This test will now fail since x and y will have the same sid.
    # def test_copy(self):
    #     from copy import copy
    #     x = IWillBeCopied()
    #     x_id = x.my_id(1)
    #     y = copy(x)
    #     y_id = y.my_id(1)
    #     self.assertNotEqual(x_id, y_id)

    def test_region_api(self):
        for backend_cls in [cache.DogpileMemoryCacheRegion, cache.DogpileDiskCacheRegion]:
            backend = backend_cls()
            if backend_cls is not cache.DogpileDiskCacheRegion:
                assert not backend.get('mykey')[0]
            backend.set('mykey', 1)
            assert backend.get('mykey') == (True, 1)

if __name__ == "__main__":
    runmodule(filename=__file__)
<commit_msg>[tests] fix cache tests<commit_after># This file is part of the pyMOR project (http://www.pymor.org).
# Copyright Holders: Rene Milk, Stephan Rave, Felix Schindler
# License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

from __future__ import absolute_import, division, print_function
import time
import os
from uuid import uuid4
from datetime import datetime
from tempfile import gettempdir

from pymor.core import cache
from pymortests.base import TestInterface, runmodule

SLEEP_SECONDS = 0.2

class IamMemoryCached(cache.CacheableInterface):

    @cache.cached
    def me_takey_long_time(self, arg):
        time.sleep(SLEEP_SECONDS)
        return arg

class IamDiskCached(cache.CacheableInterface):

    def __init__(self):
        self.cache_region = 'disk'

    @cache.cached
    def me_takey_long_time(self, arg):
        time.sleep(SLEEP_SECONDS)
        return arg

class IamLimitedCached(cache.CacheableInterface):

    def __init__(self, region='disk'):
        self.cache_region = region

    @cache.cached
    def me_takey_no_time(self, arg):
        return int(arg)

class IWillBeCopied(cache.CacheableInterface):

    def __init__(self):
        super(IWillBeCopied, self).__init__()

    @cache.cached
    def my_id(self, x):
        return id(self)

class TestCache(TestInterface):

    def test_runtime(self):
        for Class in [IamMemoryCached, IamDiskCached]:
            r = Class()
            for val in ['koko', 'koko', 'other']:
                int0 = datetime.now()
                r.me_takey_long_time(val)
                int1 = datetime.now()
                self.logger.info(int1 - int0)

    # def test_limit(self):
    #     for c in [IamLimitedCached('memory'),
    #               IamLimitedCached('disk')]:
    #         for i in range(25):
    #             c._cache_region.backend.print_limit()
    #             c.me_takey_no_time(i)
    #             c._cache_region.backend.print_limit()

    # This test will now fail since x and y will have the same sid.
    # def test_copy(self):
    #     from copy import copy
    #     x = IWillBeCopied()
    #     x_id = x.my_id(1)
    #     y = copy(x)
    #     y_id = y.my_id(1)
    #     self.assertNotEqual(x_id, y_id)

    def test_region_api(self):
        tempdir = gettempdir()
        backends = [cache.MemoryRegion(100), cache.SQLiteRegion(path=os.path.join(tempdir, str(uuid4())),
                                                                max_size=1024 ** 2)]
        for backend in backends:
            assert not backend.get('mykey')[0]
            backend.set('mykey', 1)
            assert backend.get('mykey') == (True, 1)

if __name__ == "__main__":
    runmodule(filename=__file__)
<|endoftext|>