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

import boto3
import clamav
import copy
import json
import metrics
import urllib
from common import *
from datetime import datetime

ENV = os.getenv("ENV", "")

def event_object(event):
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.unquote_plus(event['Records'][0]['s3']['object']['key'].encode('utf8'))
    if (not bucket) or (not key):
        print("Unable to retrieve object from event.\n%s" % event)
        raise Exception("Unable to retrieve object from event.")
    return s3.Object(bucket, key)

def download_s3_object(s3_object, local_prefix):
    local_path = "%s/%s/%s" % (local_prefix, s3_object.bucket_name, s3_object.key)
    create_dir(os.path.dirname(local_path))
    s3_object.download_file(local_path)
    return local_path

def set_av_metadata(s3_object, result):
    content_type = s3_object.content_type
    metadata = s3_object.metadata
    metadata[AV_STATUS_METADATA] = result
    metadata[AV_TIMESTAMP_METADATA] = datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC")
    s3_object.copy(
        {
            'Bucket': s3_object.bucket_name,
            'Key': s3_object.key
        },
        ExtraArgs={
            "ContentType": content_type,
            "Metadata": metadata,
            "MetadataDirective": "REPLACE"
        }
    )

def set_av_tags(s3_object, result):
    curr_tags = s3_client.get_object_tagging(Bucket=s3_object.bucket_name, Key=s3_object.key)["TagSet"]
    new_tags = copy.copy(curr_tags)
    for tag in curr_tags:
        if tag["Key"] == AV_STATUS_METADATA:
            new_tags.remove(tag)
            break
    new_tags.append({"Key": AV_STATUS_METADATA, "Value": result})
    new_tags.append({"Key": AV_TIMESTAMP_METADATA, "Value": datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC")})
    s3_client.put_object_tagging(
        Bucket=s3_object.bucket_name,
        Key=s3_object.key,
        Tagging={"TagSet": new_tags}
    )

def sns_scan_results(s3_object, result):
    if AV_STATUS_SNS_ARN is None:
        return
    message = {
        "bucket": s3_object.bucket_name,
        "key": s3_object.key,
        AV_STATUS_METADATA: result,
        AV_TIMESTAMP_METADATA: datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC")
    }
    sns_client = boto3.client("sns")
    sns_client.publish(
        TargetArn=AV_STATUS_SNS_ARN,
        Message=json.dumps({'default': json.dumps(message)}),
        MessageStructure="json"
    )

def lambda_handler(event, context):
    start_time = datetime.utcnow()
    print("Script starting at %s\n" %
          (start_time.strftime("%Y/%m/%d %H:%M:%S UTC")))
    s3_object = event_object(event)
    file_path = download_s3_object(s3_object, "/tmp")
    clamav.update_defs_from_s3(AV_DEFINITION_S3_BUCKET, AV_DEFINITION_S3_PREFIX)
    scan_result = clamav.scan_file(file_path)
    print("Scan of s3://%s resulted in %s\n" % (os.path.join(s3_object.bucket_name, s3_object.key), scan_result))
    if "AV_UPDATE_METADATA" in os.environ:
        set_av_metadata(s3_object, scan_result)
    set_av_tags(s3_object, scan_result)
    sns_scan_results(s3_object, scan_result)
    metrics.send(env=ENV, bucket=s3_object.bucket_name, key=s3_object.key, status=scan_result)
    # Delete downloaded file to free up room on re-usable lambda function container
    try:
        os.remove(file_path)
    except OSError:
        pass
    print("Script finished at %s\n" %
          datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC"))
<commit_msg>removes AV_STATUS tags from object before writing new ones<commit_after># Upside Travel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import boto3
import clamav
import copy
import json
import metrics
import urllib
from common import *
from datetime import datetime

ENV = os.getenv("ENV", "")

def event_object(event):
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.unquote_plus(event['Records'][0]['s3']['object']['key'].encode('utf8'))
    if (not bucket) or (not key):
        print("Unable to retrieve object from event.\n%s" % event)
        raise Exception("Unable to retrieve object from event.")
    return s3.Object(bucket, key)

def download_s3_object(s3_object, local_prefix):
    local_path = "%s/%s/%s" % (local_prefix, s3_object.bucket_name, s3_object.key)
    create_dir(os.path.dirname(local_path))
    s3_object.download_file(local_path)
    return local_path

def set_av_metadata(s3_object, result):
    content_type = s3_object.content_type
    metadata = s3_object.metadata
    metadata[AV_STATUS_METADATA] = result
    metadata[AV_TIMESTAMP_METADATA] = datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC")
    s3_object.copy(
        {
            'Bucket': s3_object.bucket_name,
            'Key': s3_object.key
        },
        ExtraArgs={
            "ContentType": content_type,
            "Metadata": metadata,
            "MetadataDirective": "REPLACE"
        }
    )

def set_av_tags(s3_object, result):
    curr_tags = s3_client.get_object_tagging(Bucket=s3_object.bucket_name, Key=s3_object.key)["TagSet"]
    new_tags = copy.copy(curr_tags)
    for tag in curr_tags:
        if tag["Key"] in [AV_STATUS_METADATA, AV_TIMESTAMP_METADATA]:
            new_tags.remove(tag)
            break
    new_tags.append({"Key": AV_STATUS_METADATA, "Value": result})
    new_tags.append({"Key": AV_TIMESTAMP_METADATA, "Value": datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC")})
    s3_client.put_object_tagging(
        Bucket=s3_object.bucket_name,
        Key=s3_object.key,
        Tagging={"TagSet": new_tags}
    )

def sns_scan_results(s3_object, result):
    if AV_STATUS_SNS_ARN is None:
        return
    message = {
        "bucket": s3_object.bucket_name,
        "key": s3_object.key,
        AV_STATUS_METADATA: result,
        AV_TIMESTAMP_METADATA: datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC")
    }
    sns_client = boto3.client("sns")
    sns_client.publish(
        TargetArn=AV_STATUS_SNS_ARN,
        Message=json.dumps({'default': json.dumps(message)}),
        MessageStructure="json"
    )

def lambda_handler(event, context):
    start_time = datetime.utcnow()
    print("Script starting at %s\n" %
          (start_time.strftime("%Y/%m/%d %H:%M:%S UTC")))
    s3_object = event_object(event)
    file_path = download_s3_object(s3_object, "/tmp")
    clamav.update_defs_from_s3(AV_DEFINITION_S3_BUCKET, AV_DEFINITION_S3_PREFIX)
    scan_result = clamav.scan_file(file_path)
    print("Scan of s3://%s resulted in %s\n" % (os.path.join(s3_object.bucket_name, s3_object.key), scan_result))
    if "AV_UPDATE_METADATA" in os.environ:
        set_av_metadata(s3_object, scan_result)
    set_av_tags(s3_object, scan_result)
    sns_scan_results(s3_object, scan_result)
    metrics.send(env=ENV, bucket=s3_object.bucket_name, key=s3_object.key, status=scan_result)
    # Delete downloaded file to free up room on re-usable lambda function container
    try:
        os.remove(file_path)
    except OSError:
        pass
    print("Script finished at %s\n" %
          datetime.utcnow().strftime("%Y/%m/%d %H:%M:%S UTC"))
<|endoftext|>


<commit_before><commit_msg>Fix a aiohttp warning<commit_after><|endoftext|>


<commit_before><commit_msg>Adapt quitting method<commit_after><|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals

import frappe
import unittest
import datetime
from frappe.utils import now_datetime, nowdate
from erpnext.projects.doctype.time_sheet.time_sheet import OverlapError
from erpnext.projects.doctype.time_sheet.time_sheet import make_salary_slip, make_sales_invoice

class TestTimeSheet(unittest.TestCase):
	def test_time_sheet_billing_amount(self):
		salary_structure = make_salary_structure("_T-Employee-0001")
		time_sheet = make_time_sheet("_T-Employee-0001", True)

		self.assertEquals(time_sheet.total_hours, 2)
		self.assertEquals(time_sheet.timesheets[0].billing_rate, 50)
		self.assertEquals(time_sheet.timesheets[0].billing_amount, 100)

	def test_salary_slip_from_timesheet(self):
		salary_structure = make_salary_structure("_T-Employee-0001")
		time_sheet = make_time_sheet("_T-Employee-0001", simulate = True)
		salary_slip = make_salary_slip(time_sheet.name)
		salary_slip.submit()

		self.assertEquals(salary_slip.total_working_hours, 2)
		self.assertEquals(salary_slip.hour_rate, 50)
		self.assertEquals(salary_slip.net_pay, 150)
		self.assertEquals(salary_slip.timesheets[0].time_sheet, time_sheet.name)
		self.assertEquals(salary_slip.timesheets[0].working_hours, 2)

		time_sheet = frappe.get_doc('Time Sheet', time_sheet.name)
		self.assertEquals(time_sheet.status, 'Payslip')
		salary_slip.cancel()

		time_sheet = frappe.get_doc('Time Sheet', time_sheet.name)
		self.assertEquals(time_sheet.status, 'Submitted')

	def test_sales_invoice_from_timesheet(self):
		time_sheet = make_time_sheet("_T-Employee-0001", simulate = True, billable = 1)
		sales_invoice = make_sales_invoice(time_sheet.name)
		sales_invoice.customer = "_Test Customer"
		sales_invoice.due_date = nowdate()

		item = sales_invoice.append('items', {})
		item.item_code = '_Test Item'
		item.qty = 2
		item.rate = 100

		sales_invoice.submit()

		time_sheet = frappe.get_doc('Time Sheet', time_sheet.name)
		self.assertEquals(sales_invoice.total_billing_amount, 100)
		self.assertEquals(time_sheet.status, 'Billed')

def make_salary_structure(employee):
	name = frappe.db.get_value('Salary Structure', {'employee': employee, 'salary_slip_based_on_timesheet': 1}, 'name')
	if name:
		frappe.delete_doc('Salary Structure', name)

	salary_structure = frappe.new_doc("Salary Structure")
	salary_structure.salary_slip_based_on_timesheet = 1
	salary_structure.employee = employee
	salary_structure.from_date = nowdate()
	salary_structure.earning_type = "Basic"
	salary_structure.hour_rate = 50.0
	salary_structure.company= "_Test Company"

	salary_structure.set('earnings', [])
	salary_structure.set('deductions', [])

	es = salary_structure.append('earnings', {
		"e_type": "_Test Allowance",
		"modified_value": 100
	})

	ds = salary_structure.append('deductions', {
		"d_type": "_Test Professional Tax",
		"d_modified_amt": 50
	})

	salary_structure.save(ignore_permissions=True)

	return salary_structure

def make_time_sheet(employee, simulate=False, billable = 0):
	update_activity_type("_Test Activity Type")
	time_sheet = frappe.new_doc("Time Sheet")
	time_sheet.employee = employee
	time_sheet_detail = time_sheet.append('timesheets', {})
	time_sheet_detail.billable = billable
	time_sheet_detail.activity_type = "_Test Activity Type"
	time_sheet_detail.from_time = now_datetime()
	time_sheet_detail.hours = 2
	time_sheet_detail.to_time = time_sheet_detail.from_time + datetime.timedelta(hours= time_sheet_detail.hours)

	for data in time_sheet.get('timesheets'):
		if simulate:
			while True:
				try:
					time_sheet.save()
					break
				except OverlapError:
					data.from_time = data.from_time + datetime.timedelta(minutes=10)
					data.to_time = data.from_time + datetime.timedelta(hours= data.hours)
		else:
			time_sheet.save()

	time_sheet.submit()

	return time_sheet

def update_activity_type(activity_type):
	activity_type = frappe.get_doc('Activity Type',activity_type)
	activity_type.billing_rate = 50.0
	activity_type.save(ignore_permissions=True)<commit_msg>[Fixes] Timesheet<commit_after># -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals

import frappe
import unittest
import datetime
from frappe.utils import now_datetime, nowdate
from erpnext.projects.doctype.time_sheet.time_sheet import OverlapError
from erpnext.projects.doctype.time_sheet.time_sheet import make_salary_slip, make_sales_invoice

class TestTimeSheet(unittest.TestCase):
	def test_time_sheet_billing_amount(self):
		salary_structure = make_salary_structure("_T-Employee-0001")
		time_sheet = make_time_sheet("_T-Employee-0001", True)

		self.assertEquals(time_sheet.total_hours, 2)
		self.assertEquals(time_sheet.timesheets[0].billing_rate, 50)
		self.assertEquals(time_sheet.timesheets[0].billing_amount, 100)

	def test_salary_slip_from_timesheet(self):
		salary_structure = make_salary_structure("_T-Employee-0001")
		time_sheet = make_time_sheet("_T-Employee-0001", simulate = True)
		salary_slip = make_salary_slip(time_sheet.name)
		salary_slip.submit()

		self.assertEquals(salary_slip.total_working_hours, 2)
		self.assertEquals(salary_slip.hour_rate, 50)
		self.assertEquals(salary_slip.net_pay, 150)
		self.assertEquals(salary_slip.timesheets[0].time_sheet, time_sheet.name)
		self.assertEquals(salary_slip.timesheets[0].working_hours, 2)

		time_sheet = frappe.get_doc('Time Sheet', time_sheet.name)
		self.assertEquals(time_sheet.status, 'Payslip')
		salary_slip.cancel()

		time_sheet = frappe.get_doc('Time Sheet', time_sheet.name)
		self.assertEquals(time_sheet.status, 'Submitted')

	def test_sales_invoice_from_timesheet(self):
		time_sheet = make_time_sheet("_T-Employee-0001", simulate = True, billable = 1)
		sales_invoice = make_sales_invoice(time_sheet.name)
		sales_invoice.customer = "_Test Customer"
		sales_invoice.due_date = nowdate()

		item = sales_invoice.append('items', {})
		item.item_code = '_Test Item'
		item.qty = 2
		item.rate = 100

		sales_invoice.submit()

		time_sheet = frappe.get_doc('Time Sheet', time_sheet.name)
		self.assertEquals(sales_invoice.total_billing_amount, 100)
		self.assertEquals(time_sheet.status, 'Billed')

def make_salary_structure(employee):
	name = frappe.db.get_value('Salary Structure', {'employee': employee, 'salary_slip_based_on_timesheet': 1}, 'name')
	if name:
		frappe.delete_doc('Salary Structure', name)

	salary_structure = frappe.new_doc("Salary Structure")
	salary_structure.salary_slip_based_on_timesheet = 1
	salary_structure.employee = employee
	salary_structure.from_date = nowdate()
	salary_structure.earning_type = "Basic"
	salary_structure.hour_rate = 50.0
	salary_structure.company= "_Test Company"

	salary_structure.set('earnings', [])
	salary_structure.set('deductions', [])

	es = salary_structure.append('earnings', {
		"earning_type": "_Test Allowance",
		"modified_value": 100
	})

	ds = salary_structure.append('deductions', {
		"deduction_type": "_Test Professional Tax",
		"d_modified_amt": 50
	})

	salary_structure.save(ignore_permissions=True)

	return salary_structure

def make_time_sheet(employee, simulate=False, billable = 0):
	update_activity_type("_Test Activity Type")
	time_sheet = frappe.new_doc("Time Sheet")
	time_sheet.employee = employee
	time_sheet_detail = time_sheet.append('timesheets', {})
	time_sheet_detail.billable = billable
	time_sheet_detail.activity_type = "_Test Activity Type"
	time_sheet_detail.from_time = now_datetime()
	time_sheet_detail.hours = 2
	time_sheet_detail.to_time = time_sheet_detail.from_time + datetime.timedelta(hours= time_sheet_detail.hours)

	for data in time_sheet.get('timesheets'):
		if simulate:
			while True:
				try:
					time_sheet.save()
					break
				except OverlapError:
					data.from_time = data.from_time + datetime.timedelta(minutes=10)
					data.to_time = data.from_time + datetime.timedelta(hours= data.hours)
		else:
			time_sheet.save()

	time_sheet.submit()

	return time_sheet

def update_activity_type(activity_type):
	activity_type = frappe.get_doc('Activity Type',activity_type)
	activity_type.billing_rate = 50.0
	activity_type.save(ignore_permissions=True)<|endoftext|>


<commit_before>#!/usr/bin/python
# -*- coding: utf-8 -*-
#       Copyright 2010, Greg Baker.
#       All rights Reserved
#       Redistribution and use in source and binary forms, with or without
#       modification, are permitted provided that the following conditions are
#       met:
#
#       * Redistributions of source code must retain the above copyright
#         notice, this list of conditions and the following disclaimer.
#       * Redistributions in binary form must reproduce the above
#         copyright notice, this list of conditions and the following disclaimer
#         in the documentation and/or other materials provided with the
#         distribution.
#       * Neither the name of the  nor the names of its
#         contributors may be used to endorse or promote products derived from
#         this software without specific prior written permission.
#
#       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#       A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#       OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#       SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#       LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#       THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#       (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#       OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""No bullshit way to add items to a todo list."""

#TODO: Integrate with unix util 'at', notifications
#TODO: Integrate with gcal

import sys, os
import optparse
import sqlite3
import datetime

try:
    from dateutil.parser import parse
except:
    print 'Error loading module dateutil.'
    print 'Dates can not be parsed until the module is loaded.'

def main():
    """Run through the arguments, then run through user input until we're out"""
    optparser = optparse.OptionParser(
        usage='%prog [Options]',
        version='%prog 0.0')
    optparser.add_option('-d', '--database', default='~/.todo.db',
        type='string', help='Specify the database file used.')
    optparser.add_option('-l', '--list', default=False, action='store_true',
        help='List the current items.')
    optparser.add_option('--start_date', default=datetime.datetime.now(),
        nargs=1, help='Specify the starting date of the list.')
    optparser.add_option('--end_date', help='Specify the final listing date',
        nargs=1, default=datetime.datetime.now() + datetime.timedelta(days=1))
    optparser.add_option('--list_complete', default=False,
        action='store_true', help='List completed todo items')
    optparser.add_option('--hide_incomplete', default=False,
        action='store_true', help='Do not list incomplete todo items.')
    optparser.add_option('-c', '--complete', type='int', action='append',
        help='Mark an item as complete and exit.')
    optparser.add_option('--list_id', default=False, action='store_true',
        help='Include the item ID in the output listing')
    optparser.add_option('--list_date', default=False, action='store_true',
        help='Include the due date in the output listing')
    optparser.add_option('-r', '--remove', type='int', action='append',
        help='Remove an item from the list and exit.')
    optparser.add_option('-a', '--add', default=False, action='store_true',
        help='Add an item to the todo list.')
    optparser.add_option('--notify', type='int', default=0,
        help='Display a notification that the item is due.')
    (options, arguments) = optparser.parse_args()

    with TodoSqlite(os.path.expanduser(options.database)) as todofile:
        if options.notify is not 0:
            notify_item(todofile, options.notify)
        if options.complete is not None:
            complete_items(todofile, options.complete)
        if options.remove is not None:
            remove_items(todofile, options.remove)
        if options.list:
            if type(options.start_date) is str:
                options.start_date = parse(options.start_date)
            if type(options.end_date) is str:
                options.end_date = parse(options.end_date)
            list_items(todofile, options)
        if options.add:
            add_items(todofile)

def remove_items(todofile, items):
    """Remove several items from the database altogether."""
    for item in items:
        todofile.remove_todo(item)

def complete_items(todofile, items):
    """complete several items."""
    for item in items:
        todofile.finish_todo(item)

def list_items(todofile, options):
    """List each todo item, one per each line."""
    items = todofile.list_items(options)
    if items is not None:
        for item in items:
            list_str = []
            donestr = 'X' if item.done else ' '
            item_id = '{0:<3d}'.format(item.item_id)
            datestr = '{0:^19s} --'.format(item.date)
            list_str.append(donestr)
            if(options.list_id):
                list_str.append(item_id)
            if(options.list_date):
                list_str.append(datestr)
            list_str.append(item.text)
            print ' '.join(list_str)

def add_items(todofile):
    """Parse user input from the todo file."""
    def parse_item(todotext):
        """Parse an item from the following string: <date> -- <item>"""
        splittext = todotext.split('--')
        if(len(splittext) > 1):
            try:
                date = parse(splittext[0])
                note = ' '.join(splittext[1:])
                return TodoItem(date, note.strip())
            except:
                pass
        return TodoItem(None, todotext.strip())

    print "Recording todo items. Format: <date> -- <todo>. ^D to quit."
    todo = sys.stdin.readline()
    while (len(todo) != 0):
        todofile.write_todo(parse_item(todo))
        todo = sys.stdin.readline()

def notify_item(todofile, itemid):
    """Pop up a notification using the python notification library."""
    try:
        import pynotify
        todo = todofile.get_todo(itemid)
        notification = pynotify.Notification("Todo Reminder",
            todo.text, "help-hint")
        notification.show()
    except:
        print "Could not find itemid %d" % itemid

class TodoItem:
    """Contains the data used in constructing a Todo item."""
    def __init__(self, date, text):
        """Initialize the instance variables"""
        self.date = date
        self.text = text
        self.item_id = 0
        self.done = False

class TodoSqlite:
    """Abstraction of a sqlite database containing todo items"""
    create_sql = 'CREATE TABLE IF NOT EXISTS TodoItems(itemID INTEGER PRIMARY\
     KEY AUTOINCREMENT, time DATETIME, text TEXT, done INTEGER);'
    insert_sql = 'INSERT INTO TodoItems(time, text, done) VALUES (?,?,0)'
    finish_sql = 'UPDATE TodoItems SET done = 1 WHERE itemID = ?'
    delete_sql = 'DELETE FROM TodoItems WHERE itemID = ?'
    get_sql = 'SELECT itemID, time, text, done FROM TodoItems WHERE itemID = ?'
    select_sql = '''
SELECT TodoItems.itemID, TodoItems.time, TodoItems.text, TodoItems.done
FROM TodoItems
WHERE
((TodoItems.time > ? AND TodoItems.time < ?) OR
    TodoItems.time IS NULL) AND
((TodoItems.done = 1 AND ? = 1) OR
    (TodoItems.done = 0 AND ? = 0))
ORDER BY TodoItems.time, TodoItems.itemID
'''

    def __init__(self, path):
        """Open, create the required table Notes in the database.
        Initialize queries"""
        self.open_file = None
        sqldb = sqlite3.connect(path, detect_types=sqlite3.PARSE_DECLTYPES)
        sqldb.row_factory = sqlite3.Row
        sqldb.execute(TodoSqlite.create_sql)
        self.open_file = sqldb

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        if traceback is None:
            self.open_file.commit()
        else:
            self.open_file.rollback()
        self.open_file.close()
        self.open_file = None

    def write_todo(self, todo):
        """Write a todo item to the database."""
        if self.open_file is not None:
            sqldb = self.open_file.cursor()
            #write the todo item
            sqldb.execute(TodoSqlite.insert_sql, [todo.date, todo.text])
            todo.itemid = sqldb.lastrowid

    def finish_todo(self, todoid):
        """Mark a todo item as complete."""
        if(self.open_file is not None):
            sqldb = self.open_file.cursor()
            sqldb.execute(TodoSqlite.finish_sql, [todoid])

    def remove_todo(self, todoid):
        """Remove a todo item from the database."""
        if(self.open_file is not None):
            sqldb = self.open_file.cursor()
            sqldb.execute(TodoSqlite.delete_sql, [todoid])

    def get_todo(self, todoid):
        """Select a single item from the database."""
        if(self.open_file is not None):
            sqldb = self.open_file.cursor()
            sqldb.execute(TodoSqlite.get_sql, [todoid])
            return self.__row_to_todo(sqldb.fetchone())

    def list_items(self, options):
        """Returns a list of all todo items"""
        if self.open_file is not None:
            sqldb = self.open_file.cursor()
            sqldb.execute(TodoSqlite.select_sql,
                [options.start_date, options.end_date,
                options.list_complete, options.hide_incomplete])
            return [self.__row_to_todo(row) for row in sqldb.fetchall()]
        return None

    def __row_to_todo(self, row):
        """Converts a SQLite Row object into a TodoItem"""
        item = TodoItem(row['time'], row['text'])
        item.item_id = row['itemID']
        if(row['done'] == 1):
            item.done = True
        return item

if(__name__ == "__main__"):
    sys.exit(main())
<commit_msg>Changed the SQLite interaction to a less complicated, less efficient way of doing shite.<commit_after>#!/usr/bin/python
# -*- coding: utf-8 -*-
#       Copyright 2010, Greg Baker.
#       All rights Reserved
#       Redistribution and use in source and binary forms, with or without
#       modification, are permitted provided that the following conditions are
#       met:
#
#       * Redistributions of source code must retain the above copyright
#         notice, this list of conditions and the following disclaimer.
#       * Redistributions in binary form must reproduce the above
#         copyright notice, this list of conditions and the following disclaimer
#         in the documentation and/or other materials provided with the
#         distribution.
#       * Neither the name of the  nor the names of its
#         contributors may be used to endorse or promote products derived from
#         this software without specific prior written permission.
#
#       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
#       A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
#       OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#       SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
#       LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
#       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
#       THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#       (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
#       OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

"""No bullshit way to add items to a todo list."""

#TODO: Integrate with unix util 'at', notifications
#TODO: Integrate with gcal

import sys, os
import optparse
import sqlite3
import datetime
import collections

try:
    from dateutil.parser import parse
except:
    print 'Error loading module dateutil.'
    print 'Dates can not be parsed until the module is loaded.'

def main():
    """Run through the arguments, then run through user input until we're out"""
    optparser = optparse.OptionParser(
        usage='%prog [Options]',
        version='%prog 0.0')
    optparser.add_option('-d', '--database', default='~/.todo.db',
        type='string', help='Specify the database file used.')
    optparser.add_option('-l', '--list', default=False, action='store_true',
        help='List the current items.')
    optparser.add_option('--start_date', default=datetime.datetime.now(),
        nargs=1, help='Specify the starting date of the list.')
    optparser.add_option('--end_date', help='Specify the final listing date',
        nargs=1, default=datetime.datetime.now() + datetime.timedelta(days=1))
    optparser.add_option('--list_complete', default=False,
        action='store_true', help='List completed todo items')
    optparser.add_option('--hide_incomplete', default=False,
        action='store_true', help='Do not list incomplete todo items.')
    optparser.add_option('-c', '--complete', type='int', action='append',
        help='Mark an item as complete and exit.')
    optparser.add_option('--list_id', default=False, action='store_true',
        help='Include the item ID in the output listing')
    optparser.add_option('--list_date', default=False, action='store_true',
        help='Include the due date in the output listing')
    optparser.add_option('-r', '--remove', type='int', action='append',
        help='Remove an item from the list and exit.')
    optparser.add_option('-a', '--add', default=False, action='store_true',
        help='Add an item to the todo list.')
    optparser.add_option('--notify', type='int', action='append',
        help='Display a notification that the item is due.')
    (options, arguments) = optparser.parse_args()

    with TodoManager(os.path.expanduser(options.database)) as todofile:
        if options.notify is not None:
            notify_items(todofile, options.notify)
        if options.complete is not None:
            complete_items(todofile, options.complete)
        if options.remove is not None:
            remove_items(todofile, options.remove)
        if options.list:
            if type(options.start_date) is str:
                options.start_date = parse(options.start_date)
            if type(options.end_date) is str:
                options.end_date = parse(options.end_date)
            list_items(todofile, options)
        if options.add:
            add_items(todofile)

def find_items(todofile, items):
    """Return a list of items sharing itemid in items. """
    return [x for x in todofile.fetch_items() if x.itemid in items]

def remove_items(todofile, items):
    """Remove several items from the database altogether."""
    for item in find_items(todofile, items):
        todofile.remove_todo(item)

def complete_items(todofile, items):
    """Complete several items."""
    for item in find_items(todofile, items):
        todofile.finish_todo(item)

def notify_items(todofile, items):
    """Pop up a notification using the python notification library."""
    try:
        import pynotify
        def show_notification(item):
            """ Display a notification for the given item to the user. """
            notification = pynotify.Notification('Todo Reminder', item.text,
                'help-hint')
            notification.show()
        for item in find_items(todofile, items):
            show_notification(item)
    except:
        print 'Could not display the notification'

def list_items(todofile, options):
    """List each todo item, one per each line."""
    def filt(item):
        """Filter function based on options."""
        return ((item.done and options.list_complete) or
            (not item.done and not options.hide_incomplete))

    for item in [x for x in todofile.fetch_items if filt(x)]:
        list_str = []
        donestr = 'X' if item.done else ' '
        itemid = '{0:<3d}'.format(item.itemid)
        datestr = '{0:^19s} --'.format(item.date)
        list_str.append(donestr)
        if(options.list_id):
            list_str.append(itemid)
        if(options.list_date):
            list_str.append(datestr)
        list_str.append(item.text)
        print ' '.join(list_str)

def add_items(todofile):
    """Parse user input from the todo file."""
    def parse_item(todotext):
        """Parse an item from the following string: <date> -- <item>"""
        splittext = todotext.split('--')
        if(len(splittext) > 1):
            try:
                date = parse(splittext[0])
                text = ' '.join(splittext[1:]).strip()
                return TodoItem(date=date, text=text, itemid=0, done=False)
            except:
                pass
        return TodoItem(date=None, text=todotext.strip(), itemid=0,
            done=False)
    print "Recording todo items. Format: <date> -- <todo>. ^D to quit."
    todo = sys.stdin.readline()
    while (len(todo) != 0):
        todofile.write_todo(parse_item(todo))
        todo = sys.stdin.readline()

#Acts as the DAO for TodoManager's ORM
TodoItem = collections.namedtuple('TodoItem', 'date text itemid done')

class TodoManager:
    """ Sits atop the Todo DB and manages application interaction with it. """
    create_sql = '''
    CREATE TABLE IF NOT EXISTS TodoItems(
        itemID INTEGER PRIMARY KEY AUTOINCREMENT,
        time DATETIME,
        text TEXT,
        done INTEGER);
     '''
    insert_sql = '''
        INSERT INTO TodoItems(time, text, done) VALUES (
        :date,:text,:done)
    '''
    update_sql = '''
        UPDATE TodoItems
        SET time = :date,
            text = :text,
            done = :done,
        WHERE itemID = :itemid
    '''
    delete_sql = 'DELETE FROM TodoItems WHERE itemID = :itemid'
    select_sql = 'SELECT itemID, time, text, done FROM TodoItems'

    def __init__(self, dbpath):
        self.sqldb = sqlite3.connect(dbpath,
            detect_types=sqlite3.PARSE_DECLTYPES)
        self.sqldb.row_factory = sqlite3.Row
        self.sqldb.execute(TodoManager.create_sql)
        self.sqldb.commit()
        self.items = None
        self.updated_items = []
        self.new_items = []
        self.deleted_items = []

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        if traceback is None:
            self.__execsql(TodoManager.delete_sql, self.deleted_items)
            self.__execsql(TodoManager.update_sql, self.updated_items)
            self.__execsql(TodoManager.insert_sql, self.new_items)
            self.sqldb.commit()
        else:
            self.sqldb.rollback()

    def __execsql(self, sql, seq):
        """ Wrapper around executemany for line length. """
        if len(seq) > 0:
            self.sqldb.executemany(sql, [x._asdict() for x in seq])

    def write_todo(self, todo):
        """ Insert a new todo item in the database. """
        self.new_items.append(todo)

    def finish_todo(self, todo):
        """ Mark an item as completed in the database. """
        self.updated_items.append(todo._replace(done=True))

    def remove_todo(self, todo):
        """ Remove a todo from the database. """
        self.deleted_items.append(todo)

    def fetch_items(self):
        """ Fetch the set of inserted items """
        if self.items is not None:
            return self.items
        def row_to_todo(row):
            """ Convert a Row into a TodoItem """
            return TodoItem(itemid=row['itemID'], date=row['time'],
                text=row['text'], done=True if row['done'] else False)
        rows = self.sqldb.execute(TodoManager.select_sql).fetchall()
        self.items = [row_to_todo(row) for row in rows]
        return self.items

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


<commit_before><commit_msg>Fixes unindexed strfmt curly braces for python 2.6<commit_after><|endoftext|>


<commit_before><commit_msg>Switch command line parsing to argparse<commit_after><|endoftext|>


<commit_before>
from tornado.gen import coroutine, Return

import profile
import datetime
import uuid
import ujson

from common import Enum
from common.validate import validate
from common.database import DatabaseError, DuplicateError
from common.internal import Internal, InternalError

class RequestError(Exception):
    def __init__(self, code, message):
        self.code = code
        self.message = message

    def __str__(self):
        return str(self.code) + ": " + self.message

class NoSuchRequest(Exception):
    pass

class RequestAdapter(object):
    def __init__(self, data, current_account_id=None):
        self.type = RequestType(data.get("request_type", RequestType.ACCOUNT))
        self.account = str(data.get("account_id"))
        self.object = str(data.get("request_object"))
        self.profile = None
        self.key = data.get("request_key")
        self.payload = data.get("request_payload")

        # apparently, mysql returns LONGTEXT field type instead of JSON in case of union calls
        if isinstance(self.payload, (str, unicode)):
            self.payload = ujson.loads(self.payload)

        self.kind = RequestKind(RequestKind.OUTGOING
                                if current_account_id == self.account
                                else RequestKind.INCOMING)

    def dump(self):
        result = {
            "type": str(self.type),
            "kind": str(self.kind),
            "sender": self.account,
            "object": self.object,
            "key": self.key,
            "payload": self.payload
        }

        if self.profile:
            result["profile"] = self.profile

        return result

class RequestAdapterMapper(object):
    def __init__(self, account_id):
        self.account_id = account_id

    def __call__(self, data):
        return RequestAdapter(data, current_account_id=self.account_id)

class RequestType(Enum):
    ACCOUNT = 'account'
    GROUP = 'group'

    ALL = {
        ACCOUNT, GROUP
    }

class RequestKind(Enum):
    INCOMING = 'incoming'
    OUTGOING = 'outgoing'

    ALL = {
        INCOMING, OUTGOING
    }

class RequestsModel(profile.ProfilesModel):

    # a week
    REQUEST_EXPIRE_IN = 604800

    def __init__(self, db, cache):
        super(RequestsModel, self).__init__(db, cache)
        self.internal = Internal()

    def get_setup_db(self):
        return self.db

    def get_setup_tables(self):
        return ["requests"]

    def get_setup_events(self):
        return ["requests_expiration"]

    @coroutine
    @validate(gamespace_id="int", account_id="int", request_type='str_name', request_object="int",
              request_payload="json")
    def create_request(self, gamespace_id, account_id, request_type, request_object, request_payload=None):

        with (yield self.db.acquire()) as db:
            existing_request = yield db.get(
                """
                SELECT `request_key` FROM `requests`
                WHERE `gamespace_id`=%s AND `account_id`=%s AND `request_type`=%s AND `request_object`=%s
                LIMIT 1;
                """, gamespace_id, account_id, str(request_type), request_object)

            if existing_request:
                raise Return(existing_request["request_key"])

            expire = datetime.datetime.now() + datetime.timedelta(seconds=RequestsModel.REQUEST_EXPIRE_IN)
            key = str(uuid.uuid4())

            try:
                yield db.execute(
                    """
                    INSERT INTO `requests`
                    (`account_id`, `gamespace_id`, `request_type`, `request_object`, `request_expire`,
                        `request_key`, `request_payload`)
                    VALUES (%s, %s, %s, %s, %s, %s, %s);
                    """, account_id, gamespace_id, str(request_type), request_object, expire, key,
                    ujson.dumps(request_payload))
            except DuplicateError:
                raise RequestError(409, "Request already exists")
            except DatabaseError as e:
                raise RequestError(500, "Failed to create new request: " + str(e.args[1]))

            raise Return(key)

    @coroutine
    @validate(gamespace_id="int", account_id="int")
    def cleanup(self, gamespace_id, account_id):
        try:
            yield self.db.execute("""
                DELETE FROM `requests`
                WHERE `gamespace_id`=%s AND `account_id`=%s;
            """, gamespace_id, account_id)

        except DatabaseError as e:
            raise RequestError(500, "Failed to delete requests: " + str(e.args[1]))

    def __fetch_profile__(self, gamespace_id, account_ids, profile_fields):
        return self.internal.request(
            "profile", "mass_profiles",
            accounts=list(set(account_ids)),
            gamespace=gamespace_id,
            action="get_public",
            profile_fields=profile_fields)

    @coroutine
    @validate(gamespace_id="int", account_id="int", profile_fields="json_list_of_strings")
    def list_outgoing_account_requests(self, gamespace_id, account_id, profile_fields=None):
        try:
            data = yield self.db.query("""
                SELECT `account_id`, `request_type`, `request_object`, `request_key`, `request_payload`
                FROM `requests`
                WHERE `gamespace_id`=%s AND `account_id`=%s;
            """, gamespace_id, account_id)

        except DatabaseError as e:
            raise RequestError(500, "Failed to list requests: " + str(e.args[1]))

        requests = map(RequestAdapterMapper(account_id), data)

        if profile_fields is not None:
            account_ids = [r.object for r in requests]

            try:
                profiles = yield self.__fetch_profile__(gamespace_id, account_ids, profile_fields)
            except InternalError as e:
                raise RequestError(e.code, e.message)

            for r in requests:
                r.profile = profiles.get(str(r.object), None)

        raise Return(requests)

    @coroutine
    @validate(gamespace_id="int", account_id="int", profile_fields="json_list_of_strings")
    def list_incoming_account_requests(self, gamespace_id, account_id, profile_fields=None):
        try:
            data = yield self.db.query("""
                SELECT `account_id`, `request_type`, `request_object`, `request_key`, `request_payload`
                FROM `requests`
                WHERE `gamespace_id`=%s AND `request_type`=%s AND `request_object`=%s;
            """, gamespace_id, RequestType.ACCOUNT, account_id)

        except DatabaseError as e:
            raise RequestError(500, "Failed to list requests: " + str(e.args[1]))

        requests = map(RequestAdapterMapper(account_id), data)

        if profile_fields is not None:
            account_ids = [r.account for r in requests]

            try:
                profiles = yield self.__fetch_profile__(gamespace_id, account_ids, profile_fields)
            except InternalError as e:
                raise RequestError(e.code, e.message)

            for r in requests:
                r.profile = profiles.get(str(r.account), None)

        raise Return(requests)

    @coroutine
    @validate(gamespace_id="int", account_id="int", profile_fields="json_list_of_strings")
    def list_total_account_requests(self, gamespace_id, account_id, profile_fields=None):
        try:
            data = yield self.db.query("""
                SELECT `account_id`, `request_type`, `request_object`, `request_key`, `request_payload`
                FROM `requests`
                WHERE `gamespace_id`=%s AND `request_type`=%s AND `request_object`=%s

                UNION

                SELECT `account_id`, `request_type`, `request_object`, `request_key`, `request_payload`
                FROM `requests`
                WHERE `gamespace_id`=%s AND `account_id`=%s;
            """, gamespace_id, RequestType.ACCOUNT, account_id, gamespace_id, account_id)

        except DatabaseError as e:
            raise RequestError(500, "Failed to list requests: " + str(e.args[1]))

        requests = map(RequestAdapterMapper(account_id), data)

        if profile_fields is not None:
            account_ids = [r.account for r in requests]

            try:
                profiles = yield self.__fetch_profile__(gamespace_id, account_ids, profile_fields)
            except InternalError as e:
                raise RequestError(e.code, e.message)

            for r in requests:
                r.profile = profiles.get(str(r.account), None)

        raise Return(requests)

    @coroutine
    @validate(gamespace_id="int", account_id="int", request_type=RequestType, request_object="int")
    def delete(self, gamespace_id, account_id, request_type, request_object):

        try:
            deleted = yield self.db.execute(
                """
                    DELETE FROM `requests`
                    WHERE `account_id`=%s AND `gamespace_id`=%s AND `request_type`=%s AND `request_object`=%s
                    LIMIT 1;
                """, account_id, gamespace_id, str(request_type), request_object)

        except DatabaseError as e:
            raise RequestError(500, "Failed to delete a request: " + str(e.args[1]))

        raise Return(bool(deleted))

    @coroutine
    @validate(gamespace_id="int", account_id="int", key="str")
    def acquire(self, gamespace_id, account_id, key):

        with (yield self.db.acquire(auto_commit=False)) as db:
            try:
                request = yield db.get(
                    """
                        SELECT * FROM `requests`
                        WHERE `gamespace_id`=%s AND `account_id`=%s AND `request_key`=%s
                        LIMIT 1
                        FOR UPDATE;
                    """, gamespace_id, account_id, key)

                if not request:
                    raise NoSuchRequest()

                request = RequestAdapter(request)

                yield db.execute(
                    """
                    DELETE FROM `requests`
                    WHERE `gamespace_id`=%s AND `account_id`=%s AND `request_type`=%s AND `request_object`=%s
                    LIMIT 1;
                    """, gamespace_id, request.account, str(request.type), request.object)

                raise Return(request)

            except DatabaseError as e:
                raise RequestError(500, "Failed to acquire a request: " + str(e.args[1]))
            finally:
                yield db.commit()
<commit_msg>Fixed requests<commit_after>
from tornado.gen import coroutine, Return

import profile
import datetime
import uuid
import ujson

from common import Enum
from common.validate import validate
from common.database import DatabaseError, DuplicateError
from common.internal import Internal, InternalError

class RequestError(Exception):
    def __init__(self, code, message):
        self.code = code
        self.message = message

    def __str__(self):
        return str(self.code) + ": " + self.message

class NoSuchRequest(Exception):
    pass

class RequestAdapter(object):
    def __init__(self, data, current_account_id=None):
        self.type = RequestType(data.get("request_type", RequestType.ACCOUNT))
        self.account = str(data.get("account_id"))
        self.object = str(data.get("request_object"))
        self.profile = None
        self.key = data.get("request_key")
        self.payload = data.get("request_payload")

        # apparently, mysql returns LONGTEXT field type instead of JSON in case of union calls
        if isinstance(self.payload, (str, unicode)):
            self.payload = ujson.loads(self.payload)

        self.kind = RequestKind(RequestKind.OUTGOING
                                if current_account_id == self.account
                                else RequestKind.INCOMING)

    def dump(self):
        result = {
            "type": str(self.type),
            "kind": str(self.kind),
            "sender": self.account,
            "object": self.object,
            "key": self.key,
            "payload": self.payload
        }

        if self.profile:
            result["profile"] = self.profile

        return result

class RequestAdapterMapper(object):
    def __init__(self, account_id):
        self.account_id = str(account_id)

    def __call__(self, data):
        return RequestAdapter(data, current_account_id=self.account_id)

class RequestType(Enum):
    ACCOUNT = 'account'
    GROUP = 'group'

    ALL = {
        ACCOUNT, GROUP
    }

class RequestKind(Enum):
    INCOMING = 'incoming'
    OUTGOING = 'outgoing'

    ALL = {
        INCOMING, OUTGOING
    }

class RequestsModel(profile.ProfilesModel):

    # a week
    REQUEST_EXPIRE_IN = 604800

    def __init__(self, db, cache):
        super(RequestsModel, self).__init__(db, cache)
        self.internal = Internal()

    def get_setup_db(self):
        return self.db

    def get_setup_tables(self):
        return ["requests"]

    def get_setup_events(self):
        return ["requests_expiration"]

    @coroutine
    @validate(gamespace_id="int", account_id="int", request_type='str_name', request_object="int",
              request_payload="json")
    def create_request(self, gamespace_id, account_id, request_type, request_object, request_payload=None):

        with (yield self.db.acquire()) as db:
            existing_request = yield db.get(
                """
                SELECT `request_key` FROM `requests`
                WHERE `gamespace_id`=%s AND `account_id`=%s AND `request_type`=%s AND `request_object`=%s
                LIMIT 1;
                """, gamespace_id, account_id, str(request_type), request_object)

            if existing_request:
                raise Return(existing_request["request_key"])

            expire = datetime.datetime.now() + datetime.timedelta(seconds=RequestsModel.REQUEST_EXPIRE_IN)
            key = str(uuid.uuid4())

            try:
                yield db.execute(
                    """
                    INSERT INTO `requests`
                    (`account_id`, `gamespace_id`, `request_type`, `request_object`, `request_expire`,
                        `request_key`, `request_payload`)
                    VALUES (%s, %s, %s, %s, %s, %s, %s);
                    """, account_id, gamespace_id, str(request_type), request_object, expire, key,
                    ujson.dumps(request_payload))
            except DuplicateError:
                raise RequestError(409, "Request already exists")
            except DatabaseError as e:
                raise RequestError(500, "Failed to create new request: " + str(e.args[1]))

            raise Return(key)

    @coroutine
    @validate(gamespace_id="int", account_id="int")
    def cleanup(self, gamespace_id, account_id):
        try:
            yield self.db.execute("""
                DELETE FROM `requests`
                WHERE `gamespace_id`=%s AND `account_id`=%s;
            """, gamespace_id, account_id)

        except DatabaseError as e:
            raise RequestError(500, "Failed to delete requests: " + str(e.args[1]))

    def __fetch_profile__(self, gamespace_id, account_ids, profile_fields):
        return self.internal.request(
            "profile", "mass_profiles",
            accounts=list(set(account_ids)),
            gamespace=gamespace_id,
            action="get_public",
            profile_fields=profile_fields)

    @coroutine
    @validate(gamespace_id="int", account_id="int", profile_fields="json_list_of_strings")
    def list_outgoing_account_requests(self, gamespace_id, account_id, profile_fields=None):
        try:
            data = yield self.db.query("""
                SELECT `account_id`, `request_type`, `request_object`, `request_key`, `request_payload`
                FROM `requests`
                WHERE `gamespace_id`=%s AND `account_id`=%s;
            """, gamespace_id, account_id)

        except DatabaseError as e:
            raise RequestError(500, "Failed to list requests: " + str(e.args[1]))

        requests = map(RequestAdapterMapper(account_id), data)

        if profile_fields is not None:
            account_ids = [r.object for r in requests]

            try:
                profiles = yield self.__fetch_profile__(gamespace_id, account_ids, profile_fields)
            except InternalError as e:
                raise RequestError(e.code, e.message)

            for r in requests:
                r.profile = profiles.get(str(r.object), None)

        raise Return(requests)

    @coroutine
    @validate(gamespace_id="int", account_id="int", profile_fields="json_list_of_strings")
    def list_incoming_account_requests(self, gamespace_id, account_id, profile_fields=None):
        try:
            data = yield self.db.query("""
                SELECT `account_id`, `request_type`, `request_object`, `request_key`, `request_payload`
                FROM `requests`
                WHERE `gamespace_id`=%s AND `request_type`=%s AND `request_object`=%s;
            """, gamespace_id, RequestType.ACCOUNT, account_id)

        except DatabaseError as e:
            raise RequestError(500, "Failed to list requests: " + str(e.args[1]))

        requests = map(RequestAdapterMapper(account_id), data)

        if profile_fields is not None:
            account_ids = [r.account for r in requests]

            try:
                profiles = yield self.__fetch_profile__(gamespace_id, account_ids, profile_fields)
            except InternalError as e:
                raise RequestError(e.code, e.message)

            for r in requests:
                r.profile = profiles.get(str(r.account), None)

        raise Return(requests)

    @coroutine
    @validate(gamespace_id="int", account_id="int", profile_fields="json_list_of_strings")
    def list_total_account_requests(self, gamespace_id, account_id, profile_fields=None):
        try:
            data = yield self.db.query("""
                SELECT `account_id`, `request_type`, `request_object`, `request_key`, `request_payload`
                FROM `requests`
                WHERE `gamespace_id`=%s AND `request_type`=%s AND `request_object`=%s

                UNION

                SELECT `account_id`, `request_type`, `request_object`, `request_key`, `request_payload`
                FROM `requests`
                WHERE `gamespace_id`=%s AND `account_id`=%s;
            """, gamespace_id, RequestType.ACCOUNT, account_id, gamespace_id, account_id)

        except DatabaseError as e:
            raise RequestError(500, "Failed to list requests: " + str(e.args[1]))

        requests = map(RequestAdapterMapper(account_id), data)

        if profile_fields is not None:
            account_ids = [r.account for r in requests]

            try:
                profiles = yield self.__fetch_profile__(gamespace_id, account_ids, profile_fields)
            except InternalError as e:
                raise RequestError(e.code, e.message)

            for r in requests:
                r.profile = profiles.get(str(r.account), None)

        raise Return(requests)

    @coroutine
    @validate(gamespace_id="int", account_id="int", request_type=RequestType, request_object="int")
    def delete(self, gamespace_id, account_id, request_type, request_object):

        try:
            deleted = yield self.db.execute(
                """
                    DELETE FROM `requests`
                    WHERE `account_id`=%s AND `gamespace_id`=%s AND `request_type`=%s AND `request_object`=%s
                    LIMIT 1;
                """, account_id, gamespace_id, str(request_type), request_object)

        except DatabaseError as e:
            raise RequestError(500, "Failed to delete a request: " + str(e.args[1]))

        raise Return(bool(deleted))

    @coroutine
    @validate(gamespace_id="int", account_id="int", key="str")
    def acquire(self, gamespace_id, account_id, key):

        with (yield self.db.acquire(auto_commit=False)) as db:
            try:
                request = yield db.get(
                    """
                        SELECT * FROM `requests`
                        WHERE `gamespace_id`=%s AND `account_id`=%s AND `request_key`=%s
                        LIMIT 1
                        FOR UPDATE;
                    """, gamespace_id, account_id, key)

                if not request:
                    raise NoSuchRequest()

                request = RequestAdapter(request)

                yield db.execute(
                    """
                    DELETE FROM `requests`
                    WHERE `gamespace_id`=%s AND `account_id`=%s AND `request_type`=%s AND `request_object`=%s
                    LIMIT 1;
                    """, gamespace_id, request.account, str(request.type), request.object)

                raise Return(request)

            except DatabaseError as e:
                raise RequestError(500, "Failed to acquire a request: " + str(e.args[1]))
            finally:
                yield db.commit()
<|endoftext|>


<commit_before>class Bacteria:
    _name = ""

    def __init__(self, name):
        self._name = name

    def printName(self):
        print "My name is: " + self._name<commit_msg>Added species and metabolism type into bacteria class<commit_after>class Bacteria:
    _name = ""
    _species = ""
    metabolismType = ""

    def __init__(self, name, species):
        self._name = name
        self._species = species

    def printName(self):
        print "My name is: " + self._name

    def printSpecies(self):
        print "My species name is: " + self._species

    def printMetabolism(self):
        print "My metabolism type is: " + self.metabolismType<|endoftext|>


<commit_before>#!/usr/bin/env python2
'''
Parse a lsp fields or scalar file and output a 3D array sampling the
given variables. The scheme used here is reading the data in, histogramming
the scalar based on position and using the scalar value as a histogram
weight, and then dividing by the number of points to obtain an average
over each point. The input to this script should be a .p4 output from lsp.

There are currently no known limitations of this script. It is sequential.

Usage:
  sclr.py [options] <input> (<var> <output>)...

Options:
  -h --help               Show this help.
  -v --verbose            Turn on verbosity.
  -x --X                  Use X in interpolation.
  -y --Y                  Use Y in interpolation.
  -z --Z                  Use Z in interpolation.
  --xres=XRES             Set the resolution along the x direction [default: 100].
  --yres=XRES             Set the resolution along the y direction [default: 100].
  --zres=XRES             Set the resolution along the z direction [default: 100].
  --use-sort              Use the experimental sorting algorithm.
  --interpolate -i        Interpolate instead.
'''

import lspreader as rd;
import cPickle;
import numpy as np;
import matplotlib.pyplot as plt;
import scipy.interpolate as interp;
from docopt import docopt;
from time import time;

def logprint(s):
    global verbose;
    if verbose:
        print("{}: {}".format(name,s));
    pass;

def histogram_scalar_3d(x,y,z,s,
                        xres=100,yres=100,zres=100):
    '''Histograms the scalar s on the grid x,y,z
       with the resolutions *res along each axis.'''
    xbins = np.linspace(min(x),max(x),xres+1);
    ybins = np.linspace(min(y),max(y),yres+1);
    zbins = np.linspace(min(z),max(z),zres+1);
    t=time();
    H,_= np.histogramdd([x,y,z],bins=[xbins,ybins,zbins],weights=s);
    logprint('It took {} seconds'.format(time()-t));
    #divide to get the average
    norm=float(len(s))/float(xres*yres*zres);
    logprint('we have a norm of {}'.format(norm));
    return H/norm;

def interpolate_scalar_3d(x,y,z,s,
                          xres=100,yres=100,zres=100):
    '''Interpolates the scalar s on the grid x,y
       with the resolutions *res along each axis.
       Uses nearest, hopefully this looks right'''
    X,Y,Z = np.mgrid[ x.min():x.max():xres*1j,
                      y.min():y.max():yres*1j,
                      z.min():z.max():zres*1j ];
    t=time();
    H = interp.griddata((x,y,z),s,(X,Y,Z),method='nearest');
    logprint('It took {} seconds'.format(time()-t));
    return H;

def histogram_scalar_2d(x,y,s,
                        xres=100,yres=100):
    '''Histograms the scalar s on the grid x,y
       with the resolutions *res along each axis.'''
    xbins = np.linspace(min(x),max(x),xres+1);
    ybins = np.linspace(min(y),max(y),yres+1);
    H,_,_ = np.histogram2d(x,y,bins=(xbins,ybins),weights=s);
    norm=float(len(s))/float(xres*yres);
    logprint('we have a norm of {}'.format(norm));
    return H/norm;

def interpolate_scalar_2d(x,y,s,
                          xres=100,yres=100):
    '''Interpolates the scalar s on the grid x,y
       with the resolutions *res along each axis.
       Uses nearest, hopefully this looks right'''
    X,Y = np.mgrid[ x.min():x.max():xres*1j,
                    y.min():y.max():yres*1j];
    H = interp.griddata((x,y),s,(X,Y),method='nearest');
    return H;

def histogram_scalar_1d(x,s,res=100):
    '''Histograms the scalar s on the grid x
       with the resolution res.'''
    xbins = np.linspace(min(x),max(x),res+1);
    H,_ = np.histogram(x,bins=xbins,weights=s);
    norm=float(len(s))/float(res);
    logprint('we have a norm of {}'.format(norm));
    return H/norm;

def main():
    global name,verbose;
    #procesing arguments
    opts=docopt(__doc__,help=True);
    var=opts['<var>'];
    outnames= opts['<output>'];
    vopairs = zip(var,outnames);
    name = opts['<input>'];

    verbose = opts['--verbose'];
    use = [];
    res = [];
    if opts['--X']:
        use.append('x');
        res.append(int(opts['--xres']));
    if opts['--Y']:
        use.append('y');
        res.append(int(opts['--yres']));
    if opts['--Z']:
        use.append('z');
        res.append(int(opts['--zres']));
    if use == []:
        use = ['x','y','z'];
        res = map(lambda k: int(opts[k]),['--xres','--yres','--zres']);
    # A couple of things to note; written in this way, whatever
    # this list (and thus, what is read) becomes, it is ordered
    # alphabetically. This is important, as this determines what
    # each resulting row and column and breadth in the output
    # array corresponds to from the actual simulation.
    #
    # It is probably worth mentioning that the xz in simulation
    # axes will be [0,1] in numpy axes, that is, it will be left-handed.
    # Using xz leads to this anyway, but it's worth reminding the reader.
    logprint('reading in {}'.format(name));
    with rd.LspOutput(name, verbose=verbose, prefix=name) as f:
        d = f.get_data(var=var);
    # removing stuff that won't be read;
    vs = [v[0] for v in vopairs] +  ['x','y','z'];
    d = {k:d[k] for k in d if k in vs};

    if opts['--use-sort']:
        # making numpy array for sorting
        t = time();
        l = len(d[d.keys()[0]]);
        dt = zip(d.keys(),['f32']*len(d));
        #magic
        d = np.array(zip(*[iter(np.array(d.values()).T.ravel())]*len(d)),dtype=dt);
        d.sort(order=['x','y','z'])
        logprint("sorting took {} seconds.".format(time()-t));

    for v,outname in vopairs:
        logprint('histogramming');
        if len(use) == 3:
            if opts['--interpolate']:
                S = interpolate_scalar_3d(d['x'],d['y'],d['z'],d[v],xres=res[0],yres=res[1],zres=res[2]);
            else:
                S = histogram_scalar_3d(d['x'],d['y'],d['z'],d[v],xres=res[0],yres=res[1],zres=res[2]);
        elif len(use) == 2:
            if opts['--interpolate']:
                S = interpolate_scalar_2d(d[use[0]],d[use[1]],d[v],
                                          xres=res[0],yres=res[1]);

            else:
                S = histogram_scalar_2d(d[use[0]],d[use[1]],d[v],
                                        xres=res[0],yres=res[1]);
        else:
            S = histogram_scalar_1d(d[use[0]],d[v],res=res[0]);
        logprint("dumping");
        with open(outname,"wb") as f:
            cPickle.dump(S,f,2);
    pass;

if __name__ == '__main__':
    main();
<commit_msg>added sclr.py changes<commit_after>#!/usr/bin/env python2
'''
Parse a lsp fields or scalar file and output a 3D array sampling the
given variables. The scheme used here is reading the data in, either
interpolating the scalar value (using the "nearest" point in actual data)
to a have a lower number of samples of the actual data, or performing a
N-D histogram of the data using the value of the scalar field as a histogram
weight, and then dividing by the number of points to obtain an average
over each point. The default is "nearest" interpolation.
The input to this script should be a .p4 output from lsp.

There are currently no known limitations of this script. It is sequential.

Usage:
  sclr.py [options] <input> (<var> <output>)...

Options:
  -h --help               Show this help.
  -v --verbose            Turn on verbosity.
  -x --X                  Use X in interpolation.
  -y --Y                  Use Y in interpolation.
  -z --Z                  Use Z in interpolation.
  --xres=XRES             Set the resolution along the x direction [default: 100].
  --yres=XRES             Set the resolution along the y direction [default: 100].
  --zres=XRES             Set the resolution along the z direction [default: 100].
  --use-sort              Use the experimental sorting algorithm.
  --histogram -H          Histogram instead.
'''

import lspreader as rd;
import cPickle;
import numpy as np;
import matplotlib.pyplot as plt;
import scipy.interpolate as interp;
from docopt import docopt;
from time import time;

def logprint(s):
    global verbose;
    if verbose:
        print("{}: {}".format(name,s));
    pass;

def histogram_scalar_3d(x,y,z,s,
                        xres=100,yres=100,zres=100):
    '''Histograms the scalar s on the grid x,y,z
       with the resolutions *res along each axis.'''
    xbins = np.linspace(min(x),max(x),xres+1);
    ybins = np.linspace(min(y),max(y),yres+1);
    zbins = np.linspace(min(z),max(z),zres+1);
    t=time();
    H,_= np.histogramdd([x,y,z],bins=[xbins,ybins,zbins],weights=s);
    logprint('It took {} seconds'.format(time()-t));
    #divide to get the average
    norm=float(len(s))/float(xres*yres*zres);
    logprint('we have a norm of {}'.format(norm));
    return H/norm;

def interpolate_scalar_3d(x,y,z,s,
                          xres=100,yres=100,zres=100):
    '''Interpolates the scalar s on the grid x,y
       with the resolutions *res along each axis.
       Uses "nearest" interpolation'''
    X,Y,Z = np.mgrid[ x.min():x.max():xres*1j,
                      y.min():y.max():yres*1j,
                      z.min():z.max():zres*1j ];
    t=time();
    H = interp.griddata((x,y,z),s,(X,Y,Z),method='nearest');
    logprint('It took {} seconds'.format(time()-t));
    return H;

def histogram_scalar_2d(x,y,s,
                        xres=100,yres=100):
    '''Histograms the scalar s on the grid x,y
       with the resolutions *res along each axis.'''
    xbins = np.linspace(min(x),max(x),xres+1);
    ybins = np.linspace(min(y),max(y),yres+1);
    H,_,_ = np.histogram2d(x,y,bins=(xbins,ybins),weights=s);
    norm=float(len(s))/float(xres*yres);
    logprint('we have a norm of {}'.format(norm));
    return H/norm;

def interpolate_scalar_2d(x,y,s,
                          xres=100,yres=100):
    '''Interpolates the scalar s on the grid x,y
       with the resolutions *res along each axis.
       Uses "nearest" interpolation'''
    X,Y = np.mgrid[ x.min():x.max():xres*1j,
                    y.min():y.max():yres*1j];
    H = interp.griddata((x,y),s,(X,Y),method='nearest');
    return H;

def interpolate_scalar_1d(x,s,
                          res=100):
    '''Interpolates the scalar s on the grid x
       with the resolutions *res along each axis.
       Uses "nearest" interpolation'''
    X = np.mgrid[ x.min():x.max():res*1j];
    H = interp.griddata(x,s,X,method='nearest');
    return H;

def histogram_scalar_1d(x,s,res=100):
    '''Histograms the scalar s on the grid x
       with the resolution res.'''
    xbins = np.linspace(min(x),max(x),res+1);
    H,_ = np.histogram(x,bins=xbins,weights=s);
    norm=float(len(s))/float(res);
    logprint('we have a norm of {}'.format(norm));
    return H/norm;

def main():
    global name,verbose;
    #procesing arguments
    opts=docopt(__doc__,help=True);
    var=opts['<var>'];
    outnames= opts['<output>'];
    vopairs = zip(var,outnames);
    name = opts['<input>'];

    verbose = opts['--verbose'];
    use = [];
    res = [];
    if opts['--X']:
        use.append('x');
        res.append(int(opts['--xres']));
    if opts['--Y']:
        use.append('y');
        res.append(int(opts['--yres']));
    if opts['--Z']:
        use.append('z');
        res.append(int(opts['--zres']));
    if use == []:
        use = ['x','y','z'];
        res = map(lambda k: int(opts[k]),['--xres','--yres','--zres']);
    # A couple of things to note; written in this way, whatever
    # this list (and thus, what is read) becomes, it is ordered
    # alphabetically. This is important, as this determines what
    # each resulting row and column and breadth in the output
    # array corresponds to from the actual simulation.
    #
    # It is probably worth mentioning that the xz in simulation
    # axes will be [0,1] in numpy axes, that is, it will be left-handed.
    # Using xz leads to this anyway, but it's worth reminding the reader.
    logprint('reading in {}'.format(name));
    with rd.LspOutput(name, verbose=verbose, prefix=name) as f:
        d = f.get_data(var=var);
    # removing stuff that won't be read;
    vs = [v[0] for v in vopairs] +  ['x','y','z'];
    d = {k:d[k] for k in d if k in vs};

    if opts['--use-sort']:
        # making numpy array for sorting
        t = time();
        l = len(d[d.keys()[0]]);
        dt = zip(d.keys(),['f32']*len(d));
        #magic
        d = np.array(zip(*[iter(np.array(d.values()).T.ravel())]*len(d)),dtype=dt);
        d.sort(order=['x','y','z'])
        logprint("sorting took {} seconds.".format(time()-t));

    for v,outname in vopairs:
        logprint('histogramming');
        if len(use) == 3:
            if opts['--histogram']:
                S = histogram_scalar_3d(d['x'],d['y'],d['z'],d[v],
                                        xres=res[0],yres=res[1],zres=res[2]);
            else:
                S = interpolate_scalar_3d(d['x'],d['y'],d['z'],d[v],
                                          xres=res[0],yres=res[1],zres=res[2]);
        elif len(use) == 2:
            if opts['--histogram']:
                S = histogram_scalar_2d(d[use[0]],d[use[1]],d[v],
                                          xres=res[0],yres=res[1]);

            else:
                S = interpolate_scalar_2d(d[use[0]],d[use[1]],d[v],
                                        xres=res[0],yres=res[1]);
        else:
            if opts['--histogram']:
                S = histogram_scalar_1d(d[use[0]],d[v],res=res[0]);
            else:
                S = interpolate_scalar_1d(d[use[0]],d[v],res=res[0]);
        logprint("dumping");
        with open(outname,"wb") as f:
            cPickle.dump(S,f,2);
    pass;

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


<commit_before><commit_msg>Remove diacritics when auto-creating "id" attributes for headers<commit_after><|endoftext|>


<commit_before># Copyright (c) 2009, TPSi
# Licensed under BSD license; see LICENSE

# These are standard python...
import os, os.path, shutil

# And for this you need PIL
import Image

# and for this, you need either Python 2.6+, which has built-in JSON,
# or simplejson
try:
	import simplejson as josn
except ImportError:
	import json

# process_set: 	processes a set folder. set_name supplied for errors and CSS
def process_set(set_name, set_path, output_set_path, url_form):
	# Check that the input set exists.
	if not os.path.exists(set_path):
		print "Could not process set ", set_name, "; input not found."
		print "Note: since scanning occurs automatically, this should not happen,"
		print "and as such, is a bug. Please report."

	# Make the output set if needed
	if not os.path.exists(output_set_path):
		os.makedirs(output_set_path)

	# Need to know if there is a config file
	# First, prepare default config.
	config = {
		"max-size": 128,
		"repeat": "none",
		"repeat-width": 32,
		"repeat-height": 32
	}

	# Now, load actual config if necessary, mixing in to default
	config_path = os.path.join(set_path, "config.json")
	if os.path.exists(config_path):
		try:
			config_file = open(config_path)
			config.update(json.loads(config_file.read()))
		except:
			print "Could not load config file. Proceeding anyway. What's the worst tha"

	# Loaded config.
	# So... time to get a list of pngs and jpegs.
	raw_items = os.listdir(set_path)
	items = []
	for item in raw_items:
		# if NOT a jpg or png, well... we can't really do anything with it, can we?
		if not item.endswith(".jpg") and not item.endswith(".png"):
			continue

		# Otherwise, we are in luck: add to the item set!
		items.append(item)

	# Now that we have images to use,we need to get their sizes so we can calculate a
	# layout plan. We just create a new set of images.
	images = []
	for item in items:
		path = os.path.join(set_path, item)
		img = Image.open(path)

		image_info = {
			"name": os.path.splitext(item)[0],
			"path": path,
			"size": img.size
		}
		images.append(image_info)

	# Sort by size, in reverse.
	# Why? Because putting all the large ones in one place should make things a bit
	# more organized, I would think.
	images.sort(lambda a, b: cmp(a["size"][0], b["size"][0]), reverse=True)

	# Calculate plan
	sprite_width = 0	# Current calculated width of the final image. max-size unless in a repeat mode
	sprite_height = 0	# Current calculated height of the final image. Usually calculated, unless in repeat-y
	cx = 0				# X position for the next sprite
	cy = 0				# Y position for the next sprite
	ch = 0				# height of the current row.

	plain = []			# Images which are NOT being sprited for one reason or another (usually too large)
	sprites = []		# Images which ARE being sprited.
	for image in images:
		# Do things differently in a repeat mode
		if config["repeat"] == "x":
			# Lay it out, but instead of the normal algorithm, just arrange in rows.
			sprite = {
				"x": cx, # should always 0
				"y": cy, # should increment each time
				"width": config["repeat-width"],
				"height": image["size"][1],
				"name": image["name"],
				"path": image["path"],
				"url": url_form.format(set=set_name, image="sprites.png"),
				"repeat": "repeat-x"
			}

			sprite_width = config["repeat-width"]
			cy += image["size"][1]
			sprite_height = cy
			sprites.append(sprite)
			continue

		elif config["repeat"] == "y":
			# Similar to above, but arranges in columns.
			sprite = {
				"x": cx, # always 0 here
				"y": cy,
				"width": image["size"][0],
				"height": config["repeat-height"],
				"name": image["name"],
				"path": image["path"],
				"url": url_form.format(set=set_name, image="sprites.png"),
				"repeat": "repeat-y"
			}

			sprite_height = config["repeat-height"]
			cx += image["size"][0]
			sprite_width = cx
			sprites.append(sprite)
			continue

		# First, leave alone images larger than max-size. Yes, even for repeat.
		if image["size"][0] > config["max-size"] or image["size"][1] > config["max-size"]:
			# add to plain set
			target = os.path.join(output_set_path, image["name"] + ".png")
			plain.append({
				"width": image["size"][0],
				"height": image["size"][1],
				"name": image["name"],
				"path": image["path"],
				"target": target,
				"url": url_form.format(set=set_name, image=image["name"] + ".png")
			})

			# and copy file
			shutil.copy2(image["path"], target)

			continue

		# Move to next row if needed
		if cx + image["size"][1] > config["max-size"]:
			# IT IS NEEDED! OH NOES!
			cx = 0
			cy += ch
			ch = 0

		# Move now
		sprite = {
			"x": cx,
			"y": cy,
			"width": image["size"][0],
			"height": image["size"][1],
			"name": image["name"],
			"path": image["path"],
			"repeat": "no-repeat",
			"url": url_form.format(set=set_name, image="sprites.png")
		}

		# add to row
		sprites.append(sprite)
		cx += image["size"][0]
		ch = max(ch, image["size"][1])

		# update sprite width and height
		sprite_width = max(sprite_width, cx)
		sprite_height = max(sprite_height, cy + ch)

	# allocate image and prepare a CSS string
	css = ""
	gen = Image.new("RGBA", (sprite_width, sprite_height), (0, 0, 0, 0))

	# Start generating
	for sprite in sprites:
		img = Image.open(sprite["path"])
		gen.paste(img, (sprite["x"], sprite["y"]))

		# Create some CSS
		css += ".{setName} .{className}.icon, .{setName}.{className}.icon {{ background:{url} {repeat}; background-position:-{x!s}px -{y!s}px; }}\n".format(
			setName=set_name,
			className=sprite["name"],
			width=sprite['width'],
			height=sprite['height'],
			x=sprite['x'],
			y=sprite['y'],
			repeat=sprite["repeat"],
			url=sprite["url"]
		)

	# Save image... if there is anything to save
	if len(sprites) > 0:
		gen.save(os.path.join(output_set_path, "sprites.png"), "PNG")

	# now, prepare non-sprite CSS
	for image in plain:
		css += ".{setName} .{className}.icon, .{setName}.{className}.icon {{ background:{url}; }}\n".format(
			setName=set_name,
			className=image["name"],
			width=image["width"],
			height=image["height"],
			url=image["url"]
		)

	css_file = open(os.path.join(output_set_path, "images.css"), "w")
	css_file.write(css)
	css_file.close()

	# Return statistics
	return {
		"plain": len(plain),
		"sprited": len(sprites)
	}

<commit_msg>Changed how CSS classes are outputted and .icon is no longer added by default, it needs to be added to image file name<commit_after># Copyright (c) 2009, TPSi
# Licensed under BSD license; see LICENSE

# These are standard python...
import os, os.path, shutil

# And for this you need PIL
import Image

# and for this, you need either Python 2.6+, which has built-in JSON,
# or simplejson
try:
	import simplejson as josn
except ImportError:
	import json

# process_set: 	processes a set folder. set_name supplied for errors and CSS
def process_set(set_name, set_path, output_set_path, url_form):
	# Check that the input set exists.
	if not os.path.exists(set_path):
		print "Could not process set ", set_name, "; input not found."
		print "Note: since scanning occurs automatically, this should not happen,"
		print "and as such, is a bug. Please report."

	# Make the output set if needed
	if not os.path.exists(output_set_path):
		os.makedirs(output_set_path)

	# Need to know if there is a config file
	# First, prepare default config.
	config = {
		"prepend-set-name": True,
		"max-size": 128,
		"repeat": "none",
		"repeat-width": 32,
		"repeat-height": 32
	}

	# Now, load actual config if necessary, mixing in to default
	config_path = os.path.join(set_path, "config.json")
	if os.path.exists(config_path):
		try:
			config_file = open(config_path)
			config.update(json.loads(config_file.read()))
		except:
			print "Could not load config file. Proceeding anyway. What's the worst tha"

	# Loaded config.
	# So... time to get a list of pngs and jpegs.
	raw_items = os.listdir(set_path)
	items = []
	for item in raw_items:
		# if NOT a jpg or png, well... we can't really do anything with it, can we?
		if not item.endswith(".jpg") and not item.endswith(".png"):
			continue

		# Otherwise, we are in luck: add to the item set!
		items.append(item)

	# Now that we have images to use,we need to get their sizes so we can calculate a
	# layout plan. We just create a new set of images.
	images = []
	for item in items:
		path = os.path.join(set_path, item)
		img = Image.open(path)

		image_info = {
			"name": os.path.splitext(item)[0],
			"path": path,
			"size": img.size
		}
		images.append(image_info)

	# Sort by size, in reverse.
	# Why? Because putting all the large ones in one place should make things a bit
	# more organized, I would think.
	images.sort(lambda a, b: cmp(a["size"][0], b["size"][0]), reverse=True)

	# Calculate plan
	sprite_width = 0	# Current calculated width of the final image. max-size unless in a repeat mode
	sprite_height = 0	# Current calculated height of the final image. Usually calculated, unless in repeat-y
	cx = 0				# X position for the next sprite
	cy = 0				# Y position for the next sprite
	ch = 0				# height of the current row.

	plain = []			# Images which are NOT being sprited for one reason or another (usually too large)
	sprites = []		# Images which ARE being sprited.
	for image in images:
		# Do things differently in a repeat mode
		if config["repeat"] == "x":
			# Lay it out, but instead of the normal algorithm, just arrange in rows.
			sprite = {
				"x": cx, # should always 0
				"y": cy, # should increment each time
				"width": config["repeat-width"],
				"height": image["size"][1],
				"name": image["name"],
				"path": image["path"],
				"url": url_form.format(set=set_name, image="sprites.png"),
				"repeat": "repeat-x"
			}

			sprite_width = config["repeat-width"]
			cy += image["size"][1]
			sprite_height = cy
			sprites.append(sprite)
			continue

		elif config["repeat"] == "y":
			# Similar to above, but arranges in columns.
			sprite = {
				"x": cx, # always 0 here
				"y": cy,
				"width": image["size"][0],
				"height": config["repeat-height"],
				"name": image["name"],
				"path": image["path"],
				"url": url_form.format(set=set_name, image="sprites.png"),
				"repeat": "repeat-y"
			}

			sprite_height = config["repeat-height"]
			cx += image["size"][0]
			sprite_width = cx
			sprites.append(sprite)
			continue

		# First, leave alone images larger than max-size. Yes, even for repeat.
		if image["size"][0] > config["max-size"] or image["size"][1] > config["max-size"]:
			# add to plain set
			target = os.path.join(output_set_path, image["name"] + ".png")
			plain.append({
				"width": image["size"][0],
				"height": image["size"][1],
				"name": image["name"],
				"path": image["path"],
				"target": target,
				"url": url_form.format(set=set_name, image=image["name"] + ".png")
			})

			# and copy file
			shutil.copy2(image["path"], target)

			continue

		# Move to next row if needed
		if cx + image["size"][1] > config["max-size"]:
			# IT IS NEEDED! OH NOES!
			cx = 0
			cy += ch
			ch = 0

		# Move now
		sprite = {
			"x": cx,
			"y": cy,
			"width": image["size"][0],
			"height": image["size"][1],
			"name": image["name"],
			"path": image["path"],
			"repeat": "no-repeat",
			"url": url_form.format(set=set_name, image="sprites.png")
		}

		# add to row
		sprites.append(sprite)
		cx += image["size"][0]
		ch = max(ch, image["size"][1])

		# update sprite width and height
		sprite_width = max(sprite_width, cx)
		sprite_height = max(sprite_height, cy + ch)

	# allocate image and prepare a CSS string
	css = ""
	cssString = ".{className} {{ background:{url} {repeat}; background-position:-{x!s}px -{y!s}px; }}\n"
	if config["prepend-set-name"]:
		cssString = ".{setName} .{className}, .{setName}.{className} {{ background:{url} {repeat}; background-position:-{x!s}px -{y!s}px; }}\n"
	gen = Image.new("RGBA", (sprite_width, sprite_height), (0, 0, 0, 0))

	# Start generating
	for sprite in sprites:
		img = Image.open(sprite["path"])
		gen.paste(img, (sprite["x"], sprite["y"]))

		# Create some CSS
		css += cssString.format(
			setName=set_name,
			className=sprite["name"],
			width=sprite['width'],
			height=sprite['height'],
			x=sprite['x'],
			y=sprite['y'],
			repeat=sprite["repeat"],
			url=sprite["url"]
		)

	# Save image... if there is anything to save
	if len(sprites) > 0:
		gen.save(os.path.join(output_set_path, "sprites.png"), "PNG")

		cssString = ".{className} {{ background:{url}; }}\n"
		if config["prepend-set-name"]:
			cssString = ".{setName} .{className}, .{setName}.{className} {{ background:{url}; }}\n"

	# now, prepare non-sprite CSS
	for image in plain:
		css += cssString.format(
			setName=set_name,
			className=image["name"],
			width=image["width"],
			height=image["height"],
			url=image["url"]
		)

	css_file = open(os.path.join(output_set_path, "images.css"), "w")
	css_file.write(css)
	css_file.close()

	# Return statistics
	return {
		"plain": len(plain),
		"sprited": len(sprites)
	}

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""A package for parsing, handling, and generating NDEF messages.

The NFC Data Exchange Format (NDEF) is a binary message format that
can be used to encapsulate one or more application-defined payloads
into a single message construct for exchange between NFC Devices and
Tags. Each payload is described by a type, a length, and an optional
identifier. The payload type determines the syntactical requirements
for decoding and encoding of the payload octets. The optional payload
identifier allows an application to locate a specific record within
the NDEF Message.

An NDEF Message is a sequence of one or more NDEF Records, started
with a Message Begin (MB) flag in the first record and terminated with
a Message End (ME) flag in the last record. By convention, the first
record provides the processing context for the whole NDEF Message.

An NDEF Message can be decoded from bytes, bytearray, or a file-like,
byte-oriented stream with the ndef.message_decoder() generator
function. Note that the NDEF Records are decoded sequentially while
iterating, it is thus possible to successfully decode until some
malformed record later in the message.

>>> octets = bytearray.fromhex('910301414243005903010158595a30ff')
>>> for record in ndef.message_decoder(octets): print(record)
...
NDEF Record urn:nfc:wkt:ABC PAYLOAD 1 byte 00
NDEF Record urn:nfc:wkt:XYZ ID 0 PAYLOAD 1 byte ff

An NDEF Message can be encoded to bytes or into a stream with the
ndef.message_encoder() generator function. When not encoding into a
stream the generator returns the bytes for each encoded record,
joining them produces the complete message bytes.

>>> record_1 = ndef.Record('urn:nfc:wkt:ABC', None, b'\\x00')
>>> record_2 = ndef.Record('urn:nfc:wkt:XYZ', '0', b'\\xff')
>>> octets = b''.join(ndef.message_encoder([record_1, record_2]))
>>> bytearray(octets).hex() # octets.encode('hex') for Python 2
'910301414243005903010158595a30ff'

An ndef.Record is initialized with the record type, name, and
data. The data argument is the sequence of octets that will become the
NDEF Record PAYLOAD. The name argument gives the NDEF Record ID with
up to 255 latin characters. The type argument gives the NDEF Record
TNF (Type Name Format) and TYPE field in a combined string
representation. For NFC Forum well-known and external record types
this is the prefix 'urn:nfc:wkt:' and 'urn:nfc:ext:' followed by
TYPE. The media-type and absolute URI type name formats ares
recognized by '<media-type>/<subtype>' and '<scheme>://<here-part>
pattern matching. The unknown and unchanged (chunked) type name
formats are selected with the single words 'unknown' and
'unchanged'. Finally, an empty string (or None) selects the empty
record type name format.

The ndef.Record type, name, and data attributes provide access to the
init arguments (None arguments are set as to defaults). The type
attribute is generally read-only while the name attribute is
read-writable. The data attribute is read-only but is a bytearray that
can itself be modified. Note that for derived record classes the data
attribute is an non-mutable bytes object.

The ndef.message_decoder() may return and the ndef.message_encoder()
does also accept ndef.Record derived class instances. Those are either
implemented within the ndef package or may be registered by the
application with ndef.Record.register_type().

>>> class MyRecord(ndef.Record):
...     _type = 'urn:nfc:ext:nfcpy.org:MyRecord'
...     def __init__(self, integer):
...         self._value = value
...     def _encode_payload(self):
...         return struct.pack('>H', self._value)
...     _decode_min_payload_length = 2
...     _decode_max_payload_length = 2
...     @classmethod
...     def _decode_payload(cls, octets, errors, known_types):
...         return cls(struct.unpack('>H', octets)[0])
...
>>> ndef.Record.register_type(MyRecord)

The ndef package provides a number of well-known record type classes,
specifically the NFC Forum Text, URI, Smartposter and Connection
Handover Records, as well as the Bluetooth SIG and Wi-Fi Alliance
Carrier Configuration Records for connection handover. They are
documented in the package contents files.

"""
import sys
if sys.version_info < (2, 7):  # pragma: no cover
    raise ImportError("The ndef module requires Python 2.7 or newer!")
else:
    from . import message
    from . import record
    from . import uri
    from . import text
    from . import smartposter
    from . import deviceinfo
    from . import handover
    # from . import bluetooth
    from . import wifi

message_decoder = message.message_decoder
message_encoder = message.message_encoder

DecodeError = record.DecodeError
EncodeError = record.EncodeError
Record = record.Record
UriRecord = uri.UriRecord
TextRecord = text.TextRecord
SmartposterRecord = smartposter.SmartposterRecord
DeviceInformationRecord = deviceinfo.DeviceInformationRecord
HandoverRequestRecord = handover.HandoverRequestRecord
HandoverSelectRecord = handover.HandoverSelectRecord
HandoverMediationRecord = handover.HandoverMediationRecord
HandoverInitiateRecord = handover.HandoverInitiateRecord
HandoverCarrierRecord = handover.HandoverCarrierRecord
WifiSimpleConfigRecord = wifi.WifiSimpleConfigRecord
WifiPeerToPeerRecord = wifi.WifiPeerToPeerRecord
# BluetoothConfigRecord = bluetooth.BluetoothConfigRecord

# METADATA ####################################################################

__version__ = "0.1.1"

__title__ = "ndef"
__description__ = "NFC Data Exchange Format decoder and encoder."
__uri__ = "https://ndeflib.readthedocs.io/"

__author__ = "Stephen Tiedemann"
__email__ = "stephen.tiedemann@gmail.com"

__license__ = "ISC"
__copyright__ = "Copyright (c) 2016 Stephen Tiedemann"

###############################################################################
<commit_msg>bump version number to 0.2.0<commit_after># -*- coding: utf-8 -*-
"""A package for parsing, handling, and generating NDEF messages.

The NFC Data Exchange Format (NDEF) is a binary message format that
can be used to encapsulate one or more application-defined payloads
into a single message construct for exchange between NFC Devices and
Tags. Each payload is described by a type, a length, and an optional
identifier. The payload type determines the syntactical requirements
for decoding and encoding of the payload octets. The optional payload
identifier allows an application to locate a specific record within
the NDEF Message.

An NDEF Message is a sequence of one or more NDEF Records, started
with a Message Begin (MB) flag in the first record and terminated with
a Message End (ME) flag in the last record. By convention, the first
record provides the processing context for the whole NDEF Message.

An NDEF Message can be decoded from bytes, bytearray, or a file-like,
byte-oriented stream with the ndef.message_decoder() generator
function. Note that the NDEF Records are decoded sequentially while
iterating, it is thus possible to successfully decode until some
malformed record later in the message.

>>> octets = bytearray.fromhex('910301414243005903010158595a30ff')
>>> for record in ndef.message_decoder(octets): print(record)
...
NDEF Record urn:nfc:wkt:ABC PAYLOAD 1 byte 00
NDEF Record urn:nfc:wkt:XYZ ID 0 PAYLOAD 1 byte ff

An NDEF Message can be encoded to bytes or into a stream with the
ndef.message_encoder() generator function. When not encoding into a
stream the generator returns the bytes for each encoded record,
joining them produces the complete message bytes.

>>> record_1 = ndef.Record('urn:nfc:wkt:ABC', None, b'\\x00')
>>> record_2 = ndef.Record('urn:nfc:wkt:XYZ', '0', b'\\xff')
>>> octets = b''.join(ndef.message_encoder([record_1, record_2]))
>>> bytearray(octets).hex() # octets.encode('hex') for Python 2
'910301414243005903010158595a30ff'

An ndef.Record is initialized with the record type, name, and
data. The data argument is the sequence of octets that will become the
NDEF Record PAYLOAD. The name argument gives the NDEF Record ID with
up to 255 latin characters. The type argument gives the NDEF Record
TNF (Type Name Format) and TYPE field in a combined string
representation. For NFC Forum well-known and external record types
this is the prefix 'urn:nfc:wkt:' and 'urn:nfc:ext:' followed by
TYPE. The media-type and absolute URI type name formats ares
recognized by '<media-type>/<subtype>' and '<scheme>://<here-part>
pattern matching. The unknown and unchanged (chunked) type name
formats are selected with the single words 'unknown' and
'unchanged'. Finally, an empty string (or None) selects the empty
record type name format.

The ndef.Record type, name, and data attributes provide access to the
init arguments (None arguments are set as to defaults). The type
attribute is generally read-only while the name attribute is
read-writable. The data attribute is read-only but is a bytearray that
can itself be modified. Note that for derived record classes the data
attribute is an non-mutable bytes object.

The ndef.message_decoder() may return and the ndef.message_encoder()
does also accept ndef.Record derived class instances. Those are either
implemented within the ndef package or may be registered by the
application with ndef.Record.register_type().

>>> class MyRecord(ndef.Record):
...     _type = 'urn:nfc:ext:nfcpy.org:MyRecord'
...     def __init__(self, integer):
...         self._value = value
...     def _encode_payload(self):
...         return struct.pack('>H', self._value)
...     _decode_min_payload_length = 2
...     _decode_max_payload_length = 2
...     @classmethod
...     def _decode_payload(cls, octets, errors, known_types):
...         return cls(struct.unpack('>H', octets)[0])
...
>>> ndef.Record.register_type(MyRecord)

The ndef package provides a number of well-known record type classes,
specifically the NFC Forum Text, URI, Smartposter and Connection
Handover Records, as well as the Bluetooth SIG and Wi-Fi Alliance
Carrier Configuration Records for connection handover. They are
documented in the package contents files.

"""
import sys
if sys.version_info < (2, 7):  # pragma: no cover
    raise ImportError("The ndef module requires Python 2.7 or newer!")
else:
    from . import message
    from . import record
    from . import uri
    from . import text
    from . import smartposter
    from . import deviceinfo
    from . import handover
    # from . import bluetooth
    from . import wifi

message_decoder = message.message_decoder
message_encoder = message.message_encoder

DecodeError = record.DecodeError
EncodeError = record.EncodeError
Record = record.Record
UriRecord = uri.UriRecord
TextRecord = text.TextRecord
SmartposterRecord = smartposter.SmartposterRecord
DeviceInformationRecord = deviceinfo.DeviceInformationRecord
HandoverRequestRecord = handover.HandoverRequestRecord
HandoverSelectRecord = handover.HandoverSelectRecord
HandoverMediationRecord = handover.HandoverMediationRecord
HandoverInitiateRecord = handover.HandoverInitiateRecord
HandoverCarrierRecord = handover.HandoverCarrierRecord
WifiSimpleConfigRecord = wifi.WifiSimpleConfigRecord
WifiPeerToPeerRecord = wifi.WifiPeerToPeerRecord
# BluetoothConfigRecord = bluetooth.BluetoothConfigRecord

# METADATA ####################################################################

__version__ = "0.2.0"

__title__ = "ndef"
__description__ = "NFC Data Exchange Format decoder and encoder."
__uri__ = "https://ndeflib.readthedocs.io/"

__author__ = "Stephen Tiedemann"
__email__ = "stephen.tiedemann@gmail.com"

__license__ = "ISC"
__copyright__ = "Copyright (c) 2016 Stephen Tiedemann"

###############################################################################
<|endoftext|>