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

from cgi import escape, FieldStorage
import cgitb
from dbclient import dbclient, dbcfg
from urllib import urlencode

cgitb.enable()

per_page = 32
outdata = []
client = dbclient(dbcfg(None, [".wellpapprc"]))
fs = FieldStorage(keep_blank_values=True)
user = "fake"
base = unicode(client.cfg.webbase)
assert base
thumbsize = unicode(client.cfg.thumb_sizes.split()[0])
assert thumbsize

def getarg(n, default=None, as_list=False):
	if default is not None and n not in fs: return default
	a = fs[n]
	if isinstance(a, list):
		a = [_argdec(e.value) for e in a]
		if not as_list: a = u' '.join(a)
	else:
		a = _argdec(a.value)
		if as_list: a = a.split()
	return a
def _argdec(v):
	try:
		v = v.decode("utf-8")
	except Exception:
		v = v.decode("iso-8859-1")
	return v

def notfound():
	print "Status: 404 Not Found"
	print "Content-Type: text/html; charset=UTF-8"
	print
	print "404 Not Found"
	exit()

def clean(n):
	if n[0] in u"-~": return n[1:]
	return n
def prefix(n):
	if n[0] in u"-~": return n[0]
	return ""

def prt(str):
	outdata.append(str)

def prtfields(*fields):
	map(lambda f: prt(f[0] + u'="' + escape(unicode(f[1])) + u'" '), fields)

def tagfmt(n, html_ok=True):
	for s in u":_-/><&":
		n = n.replace(s, s + u'\u200b')
	n = escape(n)
	if html_ok: n = n.replace(u'\u200b', u'\u200b')
	return n

def tagname(guid):
	tag = client.get_tag(guid)
	return tag.name

def taglist(post, impl):
	if impl:
		full, weak = post.impltags, post.implweaktags
	else:
		full, weak = post.tags, post.weaktags
	full = [(tagfmt(t.name), t, impl) for t in full]
	weak = [(tagfmt(u'~' + t.name), t, impl) for t in weak]
	return full + weak

def tagcloud(guids):
	guids = set(guids)
	posts = client.search_post(guids=guids, wanted=["tagname", "tagguid", "tagdata"])[0]
	tags = {}
	guid = {}
	for p in posts:
		for t in p.tags:
			tags[t.guid] = t
			guid[t.guid] = guid.get(t.guid, 0) + 1
	show = sorted(guid, lambda x, y: cmp(guid[y], guid[x]))
	show = [g for g in show[:20 + len(guids)] if g not in guids]
	return [(tagfmt(tags[g].name), tags[g], False) for g in show]

def tagtypes():
	# The server doesn't support this yet, so fake it for now
	return [u'unspecified', u'ambiguous', u'meta', u'inimage',
	        u'photographer', u'person', u'location', u'text', u'group']

def tag_post(p, full, weak, remove):
	post_full = set([t.guid for t in p.tags])
	post_weak = set([t.guid for t in p.weaktags])
	set_full = full.difference(post_full)
	set_weak = weak.difference(post_weak)
	set_remove_full = post_full.intersection(remove)
	set_remove_weak = post_weak.intersection(remove)
	set_remove = set_remove_full.union(set_remove_weak)
	if set_full or set_weak or set_remove:
		client.tag_post(p.md5, full_tags=set_full, weak_tags=set_weak, remove_tags=set_remove)
		return True

def prt_tags(tags):
	if not tags: return
	prt(u'
')
	for n, t, impl in tags:
		c = u'tag implied' if impl else u'tag'
		prt(u'
'+ n + u'
\n')
	prt(u'
')

def prt_posts(posts):
	prt(u'
\n')
	for post in posts:
		m = post.md5
		prt(u'\n')
	prt(u'
\n')

def tags_as_html(post):
	names = sorted([t.name for t in post.tags or []])
	names += sorted([u'~' + t.name for t in post.weaktags or []])
	return u' '.join([tagfmt(n, False) for n in names])

def prt_search_form(q=u''):
	prt(u'<form action="' + base + u'search" method="get">\n')
	prt(u'
\n')
	prt(u'<input type="text" name="q" id="search-q" value="' + escape(q, True))
	prt(u'" onfocus="init_completion(this);" />\n')
	prt(u'<input type="submit" value="Search" />\n')
	prt(u'
\n')
	prt(u'</form>\n')

def makelink(fn, *args):
	fn = base + fn
	if not args: return fn
	if u'?' in fn:
		middle = u'&'
	else:
		middle = u'?'
	args = urlencode([(a, v.encode("utf-8")) for a, v in args])
	return fn + middle + escape(args)

def pagelinks(link, page, result_count):
	global outdata
	pages = range(result_count // per_page + 1)
	if len(pages) == 1:
		if not user: return u''
		pages = []
	real_outdata = outdata
	outdata = []
	if len(pages) > 16:
		if page < 8:
			pages = pages[:10] + pages[-6:]
		elif page > len(pages) - 8:
			pages = pages[:6] + pages[-10:]
		else:
			pages = pages[:6] + pages[page - 2:page + 3] + pages[-5:]
	prt(u'
')
	prev = -1
	for p in pages:
		if p != prev + 1:
			prt(u'...\n')
		prev = p
		prt(u'')
		else:
			prt(u'">')
		prt(unicode(p))
		if p != page:
			prt(u'')
		prt(u'\n')
	if user:
		if pages:
			prt(u'ALL\n')
		prt(u'')
		prt(u'Tagmode\n')
	prt(u'
\n')
	res = u''.join(outdata)
	outdata = real_outdata
	return res

def prt_tagform(m):
	prt(u'<form action="' + base + u'post-tag" method="post">\n')
	prt(u'
\n')
	prt(u'<input type="hidden" name="post" value="' + m + u'" />\n')
	prt(u'<input type="text" name="tags" id="tag-q" ');
	prt(u' onfocus="init_completion(this);" />\n')
	prt(u'<input type="submit" value="Tag" />\n')
	prt(u'
\n')
	prt(u'</form>\n')

def prt_head(extra=u''):
	prt(u"""<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

	<title>WWWwellpapp</title>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
	<link rel="stylesheet" href="%(base)sstatic/style.css" />
	<link rel="stylesheet" href="%(base)sstatic/tagstyle.css" />

	""" % {"base": base})
	if user:
		prt(u'\n\t')
	prt(extra)
	prt(u'\n\n')
	if user:
		prt(u'

\n')

def prt_foot():
	prt(u'')

def finish(ctype = "text/html"):
	data = u''.join(outdata).encode("utf-8")
	ctype = str(ctype)
	if (ctype[:5] == "text/" or ctype == "application/json") and "charset" not in ctype:
		ctype += "; charset=UTF-8"
	print "Content-Type: " + ctype
	print "Content-Length: " + str(len(data) + 1)
	print
	print data
<commit_msg>Actually, we had another bare js, hide that too.<commit_after># -*- coding: utf-8 -*-

from cgi import escape, FieldStorage
import cgitb
from dbclient import dbclient, dbcfg
from urllib import urlencode

cgitb.enable()

per_page = 32
outdata = []
client = dbclient(dbcfg(None, [".wellpapprc"]))
fs = FieldStorage(keep_blank_values=True)
user = "fake"
base = unicode(client.cfg.webbase)
assert base
thumbsize = unicode(client.cfg.thumb_sizes.split()[0])
assert thumbsize

def getarg(n, default=None, as_list=False):
	if default is not None and n not in fs: return default
	a = fs[n]
	if isinstance(a, list):
		a = [_argdec(e.value) for e in a]
		if not as_list: a = u' '.join(a)
	else:
		a = _argdec(a.value)
		if as_list: a = a.split()
	return a
def _argdec(v):
	try:
		v = v.decode("utf-8")
	except Exception:
		v = v.decode("iso-8859-1")
	return v

def notfound():
	print "Status: 404 Not Found"
	print "Content-Type: text/html; charset=UTF-8"
	print
	print "404 Not Found"
	exit()

def clean(n):
	if n[0] in u"-~": return n[1:]
	return n
def prefix(n):
	if n[0] in u"-~": return n[0]
	return ""

def prt(str):
	outdata.append(str)

def prtfields(*fields):
	map(lambda f: prt(f[0] + u'="' + escape(unicode(f[1])) + u'" '), fields)

def tagfmt(n, html_ok=True):
	for s in u":_-/><&":
		n = n.replace(s, s + u'\u200b')
	n = escape(n)
	if html_ok: n = n.replace(u'\u200b', u'\u200b')
	return n

def tagname(guid):
	tag = client.get_tag(guid)
	return tag.name

def taglist(post, impl):
	if impl:
		full, weak = post.impltags, post.implweaktags
	else:
		full, weak = post.tags, post.weaktags
	full = [(tagfmt(t.name), t, impl) for t in full]
	weak = [(tagfmt(u'~' + t.name), t, impl) for t in weak]
	return full + weak

def tagcloud(guids):
	guids = set(guids)
	posts = client.search_post(guids=guids, wanted=["tagname", "tagguid", "tagdata"])[0]
	tags = {}
	guid = {}
	for p in posts:
		for t in p.tags:
			tags[t.guid] = t
			guid[t.guid] = guid.get(t.guid, 0) + 1
	show = sorted(guid, lambda x, y: cmp(guid[y], guid[x]))
	show = [g for g in show[:20 + len(guids)] if g not in guids]
	return [(tagfmt(tags[g].name), tags[g], False) for g in show]

def tagtypes():
	# The server doesn't support this yet, so fake it for now
	return [u'unspecified', u'ambiguous', u'meta', u'inimage',
	        u'photographer', u'person', u'location', u'text', u'group']

def tag_post(p, full, weak, remove):
	post_full = set([t.guid for t in p.tags])
	post_weak = set([t.guid for t in p.weaktags])
	set_full = full.difference(post_full)
	set_weak = weak.difference(post_weak)
	set_remove_full = post_full.intersection(remove)
	set_remove_weak = post_weak.intersection(remove)
	set_remove = set_remove_full.union(set_remove_weak)
	if set_full or set_weak or set_remove:
		client.tag_post(p.md5, full_tags=set_full, weak_tags=set_weak, remove_tags=set_remove)
		return True

def prt_tags(tags):
	if not tags: return
	prt(u'
')
	for n, t, impl in tags:
		c = u'tag implied' if impl else u'tag'
		prt(u'
'+ n + u'
\n')
	prt(u'
')

def prt_posts(posts):
	prt(u'
\n')
	for post in posts:
		m = post.md5
		prt(u'\n')
	prt(u'
\n')

def tags_as_html(post):
	names = sorted([t.name for t in post.tags or []])
	names += sorted([u'~' + t.name for t in post.weaktags or []])
	return u' '.join([tagfmt(n, False) for n in names])

def prt_search_form(q=u''):
	prt(u'<form action="' + base + u'search" method="get">\n')
	prt(u'
\n')
	prt(u'<input type="text" name="q" id="search-q" value="' + escape(q, True))
	prt(u'" onfocus="init_completion(this);" />\n')
	prt(u'<input type="submit" value="Search" />\n')
	prt(u'
\n')
	prt(u'</form>\n')

def makelink(fn, *args):
	fn = base + fn
	if not args: return fn
	if u'?' in fn:
		middle = u'&'
	else:
		middle = u'?'
	args = urlencode([(a, v.encode("utf-8")) for a, v in args])
	return fn + middle + escape(args)

def pagelinks(link, page, result_count):
	global outdata
	pages = range(result_count // per_page + 1)
	if len(pages) == 1:
		if not user: return u''
		pages = []
	real_outdata = outdata
	outdata = []
	if len(pages) > 16:
		if page < 8:
			pages = pages[:10] + pages[-6:]
		elif page > len(pages) - 8:
			pages = pages[:6] + pages[-10:]
		else:
			pages = pages[:6] + pages[page - 2:page + 3] + pages[-5:]
	prt(u'
')
	prev = -1
	for p in pages:
		if p != prev + 1:
			prt(u'...\n')
		prev = p
		prt(u'')
		else:
			prt(u'">')
		prt(unicode(p))
		if p != page:
			prt(u'')
		prt(u'\n')
	if user:
		if pages:
			prt(u'ALL\n')
		prt(u'')
		prt(u'Tagmode\n')
	prt(u'
\n')
	res = u''.join(outdata)
	outdata = real_outdata
	return res

def prt_tagform(m):
	prt(u'<form action="' + base + u'post-tag" method="post">\n')
	prt(u'
\n')
	prt(u'<input type="hidden" name="post" value="' + m + u'" />\n')
	prt(u'<input type="text" name="tags" id="tag-q" ');
	prt(u' onfocus="init_completion(this);" />\n')
	prt(u'<input type="submit" value="Tag" />\n')
	prt(u'
\n')
	prt(u'</form>\n')

def prt_head(extra=u''):
	prt(u"""<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

	<title>WWWwellpapp</title>
	<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
	<link rel="stylesheet" href="%(base)sstatic/style.css" />
	<link rel="stylesheet" href="%(base)sstatic/tagstyle.css" />

	""" % {"base": base})
	if user:
		prt(u'\n\t')
	prt(extra)
	prt(u'\n\n')
	if user:
		prt(u'

\n')

def prt_foot():
	prt(u'')

def finish(ctype = "text/html"):
	data = u''.join(outdata).encode("utf-8")
	ctype = str(ctype)
	if (ctype[:5] == "text/" or ctype == "application/json") and "charset" not in ctype:
		ctype += "; charset=UTF-8"
	print "Content-Type: " + ctype
	print "Content-Length: " + str(len(data) + 1)
	print
	print data
<|endoftext|>


<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: Ted Neykov
# 201303XX - Challenge 3
# Write a script that accepts a directory as an argument as well as a container name. The script should upload the contents of the specified directory to the container (or create it if it doesn't exist). The script should handle errors appropriately. (Check for invalid paths, etc.)

import os
import sys
import string
import pyrax
import pyrax.exceptions as exc
import time

print "Using credentials file: ~/.rackspace_cloud_credentials"
cred_file = os.path.expanduser("~/.rackspace_cloud_credentials")
try:
    pyrax.set_credential_file(cred_file)
except exc.AuthenticationFailed:
        print "Did you remember to replace the credential file with your actual username and api_key?"

if pyrax.identity.authenticated:
        print "Successfully authenticated."
else:
        print "Authentication failed. Exiting..."
        sys.exit(1)

print "Please enter the directory you wish to upload to Cloud Files: "
dir = raw_input()
dir = dir.strip()

# Check to see if it's a valid dir
if not os.path.exists(dir):
	print dir + ": No such file or directory. Exiting..."
	sys.exit(1)
else:
	if not os.path.isdir(dir):
		print dir + ": Path specified is not a directory. Exiting..."
		sys.exit(1)

print
print "Please enter the container name you want to upload to: "
dst_cont = raw_input()
dst_cont = dst_cont.strip()

cf = pyrax.cloudfiles
cont = cf.list_containers()

dst_exists = False
for c in cont:
	if c == dst_cont:
		dst_exists = True
		print "Containter '" + c + "' already exists."

if not dst_exists:
	#create container
	dst = cf.create_container(dst_cont)
	print "Created conainer '" + dst.name + "'."

upload_key, total_bytes = cf.upload_folder(dir, container=dst_cont)

print "Uploading: "
done = False
while not done:
	curr_bytes = pyrax.cloudfiles.get_uploaded(upload_key)
	progress = float(curr_bytes)/float(total_bytes)*100
	time.sleep(1)
	sys.stdout.write("\r%d%%" %progress)    # or print >> sys.stdout, "\r%d%%" %i,
	sys.stdout.flush()
	if progress == 100:
		done = True
		print

print "Upload complete."
<commit_msg>date change.<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Author: Ted Neykov
# 20130321 - Challenge 3
# Write a script that accepts a directory as an argument as well as a container name. The script should upload the contents of the specified directory to the container (or create it if it doesn't exist). The script should handle errors appropriately. (Check for invalid paths, etc.)

import os
import sys
import string
import pyrax
import pyrax.exceptions as exc
import time

print "Using credentials file: ~/.rackspace_cloud_credentials"
cred_file = os.path.expanduser("~/.rackspace_cloud_credentials")
try:
    pyrax.set_credential_file(cred_file)
except exc.AuthenticationFailed:
        print "Did you remember to replace the credential file with your actual username and api_key?"

if pyrax.identity.authenticated:
        print "Successfully authenticated."
else:
        print "Authentication failed. Exiting..."
        sys.exit(1)

print "Please enter the directory you wish to upload to Cloud Files: "
dir = raw_input()
dir = dir.strip()

# Check to see if it's a valid dir
if not os.path.exists(dir):
	print dir + ": No such file or directory. Exiting..."
	sys.exit(1)
else:
	if not os.path.isdir(dir):
		print dir + ": Path specified is not a directory. Exiting..."
		sys.exit(1)

print
print "Please enter the container name you want to upload to: "
dst_cont = raw_input()
dst_cont = dst_cont.strip()

cf = pyrax.cloudfiles
cont = cf.list_containers()

dst_exists = False
for c in cont:
	if c == dst_cont:
		dst_exists = True
		print "Containter '" + c + "' already exists."

if not dst_exists:
	#create container
	dst = cf.create_container(dst_cont)
	print "Created conainer '" + dst.name + "'."

upload_key, total_bytes = cf.upload_folder(dir, container=dst_cont)

print "Uploading: "
done = False
while not done:
	curr_bytes = pyrax.cloudfiles.get_uploaded(upload_key)
	progress = float(curr_bytes)/float(total_bytes)*100
	time.sleep(1)
	sys.stdout.write("\r%d%%" %progress)    # or print >> sys.stdout, "\r%d%%" %i,
	sys.stdout.flush()
	if progress == 100:
		done = True
		print

print "Upload complete."
<|endoftext|>


<commit_before>#!/usr/bin/env python2
#import all the libraries and elements that we need
import time
import sys
import RPi.GPIO as GPIO
#import CloudFunctions

#turn off meaningless warnings
GPIO.setwarnings(False)

#define global variables defaults
LED_FREQ = 120	#frequency of LED PWM switching
LOCATION = "CAXX0518"	#location of the weather we want
MODE = "lamp"	#mode we want to be in (default is mirror weather at location)
DEFAULT_COLOR = [255, 255, 255]	#default colour shown in lamp mode

#open the settings file and grab all our globals from there
config = open("settings.conf", "r")
line = config.readline()
print "Line Read: %s" % (line)
line = config.readline()
print "Line Read: %s" % (line)
line = config.readline()
print "Line Read: %s" % (line)

#define all the functions we need in the program
#function to start all the pins with current duty cycle
def startLeds( leds, duty_cycles ):
	"Function turns on all LEDs in array of LED objects provided"
	print "\nStarting all LEDs!"
	for index in range(len(leds)):
		leds[index].start(duty_cycles[index])
	return

#function to stop all pins
def stopLeds( leds ):
	"Turns off all LEDs in array of LED objects provided"
	print "\nSwitching off all LEDs!"
	for index in range(len(leds)):
		leds[index].stop()
	return

#function to update the pin duty cylces
def updateLeds( leds, duty_cycles ):
	"Updates all leds with latest duty cycle provided"
	print "\nUpdating LED duty cycles!"
	for index in range(len(leds)):
		leds[index].ChangeDutyCycle(duty_cycles[index])
	return

#function to define lamp mode
def lampMode(duty_cycles, DEFAULT_COLOR):
	"Outputs duty cycle values based on lamp mode and default colour"
	for channel in range(0, 2, 1):
		for colour in range (0, 2, 1):
			duty_cycles[channel + colour] = DEFAULT_COLOR[colour] * 100 / 255
	return

#function which runs the duty cycles for weather mode
def weatherMirrorMode(duty_cycles):
	"Outputs duty cycle values based on weather outside"
	return

#function which runs the duty cycles for weather mode inverted
def weatherMirrorMode(duty_cycles):
	"Outputs duty cycle values based on weather outside inverted"
	return

#welcome statements
print "\nCloud Clouds is starting..."

#setup GPIO output pins
print "\nSetting up GPIO pins and libraries..."
GPIO.setmode(GPIO.BCM)
#LED Strip 1
GPIO.setup(17, GPIO.OUT)	#LED Red 1
GPIO.setup(18, GPIO.OUT)	#LED Green 1
GPIO.setup(27, GPIO.OUT)	#LED Blue 1
#LED Strip 2w
GPIO.setup(23, GPIO.OUT)	#LED Red 2
GPIO.setup(24, GPIO.OUT)	#LED Green 2
GPIO.setup(25, GPIO.OUT)	#LED Blue 2
#LED Strip 3
GPIO.setup(10, GPIO.OUT)	#LED Red 3
GPIO.setup(9, GPIO.OUT)	#LED Green 3
GPIO.setup(11, GPIO.OUT)	#LED Blue 3

#create an array to store all the led objects
leds = []
#LED Strip 1
led = GPIO.PWM(17, LED_FREQ)
leds.append(led)
led = GPIO.PWM(18, LED_FREQ)
leds.append(led)
led = GPIO.PWM(27, LED_FREQ)
leds.append(led)
#LED strip 2
led = GPIO.PWM(23, LED_FREQ)
leds.append(led)
led = GPIO.PWM(24, LED_FREQ)
leds.append(led)
led = GPIO.PWM(25, LED_FREQ)
leds.append(led)
#LED strip 3
led = GPIO.PWM(10, LED_FREQ)
leds.append(led)
led = GPIO.PWM(9, LED_FREQ)
leds.append(led)
led = GPIO.PWM(11, LED_FREQ)
leds.append(led)

#create an array to store all the led pin duty cycles
duty_cycles = [1, 1, 1, 1, 1, 1, 1, 1, 1]

#start all the leds
startLeds(leds, duty_cycles)

#main program loop
#inifite, just updates the LED duty cycles from the duty_cycles array and drives them up and down
while (1):
	print "\nMain program loop started."

	#check what mode we are in and run appropriate function
	if mode == "mirror":
		print "\nGetting latest from weather mirror mode"
		mirrorWeatherMode(duty_cycles)
	else if mode == "invert":
		print "\nGetting latest from weather invert mode"
		invertWeatherMode(duty_cycles)
	else:
		print "\nGetting latest from lamp mode"
		lampMode(duty_cycles, DEFAULT_COLOR)

	#update the LED duty cycles
	print "\nUpdating LED duty cycles."
	updateLeds(leds, duty_cycles)

	#update the duty cycles from 0% to 100%
	#for i in range(0, 100, 1):
	#	for j in range(len(duty_cycles)):
	#		duty_cycles[j] = i

		#send new duty cyles to LEDs
	#	updateLeds( leds, duty_cycles)

		#wait for some time for the changes to take effect
	#	time.sleep(0.1)

	#update the duty cycles from 100% to 0%
	#for i in range(100, 0, -1):
	#	for j in range(len(duty_cycles)):
	#		duty_cycles[j] = i

		#send new duty cyles to LEDs
	#	updateLeds( leds, duty_cycles)

		#wait for some time for the changes to take effect
	#	time.sleep(0.1)<commit_msg>elif update<commit_after>#!/usr/bin/env python2
#import all the libraries and elements that we need
import time
import sys
import RPi.GPIO as GPIO
#import CloudFunctions

#turn off meaningless warnings
GPIO.setwarnings(False)

#define global variables defaults
LED_FREQ = 120	#frequency of LED PWM switching
LOCATION = "CAXX0518"	#location of the weather we want
MODE = "lamp"	#mode we want to be in (default is mirror weather at location)
DEFAULT_COLOR = [255, 0, 0]	#default colour shown in lamp mode

#open the settings file and grab all our globals from there
config = open("settings.conf", "r")
line = config.readline()
print "Line Read: %s" % (line)
line = config.readline()
print "Line Read: %s" % (line)
line = config.readline()
print "Line Read: %s" % (line)

#define all the functions we need in the program
#function to start all the pins with current duty cycle
def startLeds( leds, duty_cycles ):
	"Function turns on all LEDs in array of LED objects provided"
	print "\nStarting all LEDs!"
	for index in range(len(leds)):
		leds[index].start(duty_cycles[index])
	return

#function to stop all pins
def stopLeds( leds ):
	"Turns off all LEDs in array of LED objects provided"
	print "\nSwitching off all LEDs!"
	for index in range(len(leds)):
		leds[index].stop()
	return

#function to update the pin duty cylces
def updateLeds( leds, duty_cycles ):
	"Updates all leds with latest duty cycle provided"
	print "\nUpdating LED duty cycles!"
	for index in range(len(leds)):
		leds[index].ChangeDutyCycle(duty_cycles[index])
	return

#function to define lamp mode
def lampMode(duty_cycles, DEFAULT_COLOR):
	"Outputs duty cycle values based on lamp mode and default colour"
	for channel in range(0, 2, 1):
		for colour in range (0, 2, 1):
			duty_cycles[channel + colour] = DEFAULT_COLOR[colour] * 100 / 255
	return

#function which runs the duty cycles for weather mode
def weatherMirrorMode(duty_cycles):
	"Outputs duty cycle values based on weather outside"
	return

#function which runs the duty cycles for weather mode inverted
def weatherMirrorMode(duty_cycles):
	"Outputs duty cycle values based on weather outside inverted"
	return

#welcome statements
print "\nCloud Clouds is starting..."

#setup GPIO output pins
print "\nSetting up GPIO pins and libraries..."
GPIO.setmode(GPIO.BCM)
#LED Strip 1
GPIO.setup(17, GPIO.OUT)	#LED Red 1
GPIO.setup(18, GPIO.OUT)	#LED Green 1
GPIO.setup(27, GPIO.OUT)	#LED Blue 1
#LED Strip 2w
GPIO.setup(23, GPIO.OUT)	#LED Red 2
GPIO.setup(24, GPIO.OUT)	#LED Green 2
GPIO.setup(25, GPIO.OUT)	#LED Blue 2
#LED Strip 3
GPIO.setup(10, GPIO.OUT)	#LED Red 3
GPIO.setup(9, GPIO.OUT)	#LED Green 3
GPIO.setup(11, GPIO.OUT)	#LED Blue 3

#create an array to store all the led objects
leds = []
#LED Strip 1
led = GPIO.PWM(17, LED_FREQ)
leds.append(led)
led = GPIO.PWM(18, LED_FREQ)
leds.append(led)
led = GPIO.PWM(27, LED_FREQ)
leds.append(led)
#LED strip 2
led = GPIO.PWM(23, LED_FREQ)
leds.append(led)
led = GPIO.PWM(24, LED_FREQ)
leds.append(led)
led = GPIO.PWM(25, LED_FREQ)
leds.append(led)
#LED strip 3
led = GPIO.PWM(10, LED_FREQ)
leds.append(led)
led = GPIO.PWM(9, LED_FREQ)
leds.append(led)
led = GPIO.PWM(11, LED_FREQ)
leds.append(led)

#create an array to store all the led pin duty cycles
duty_cycles = [1, 1, 1, 1, 1, 1, 1, 1, 1]

#start all the leds
startLeds(leds, duty_cycles)

#main program loop
#inifite, just updates the LED duty cycles from the duty_cycles array and drives them up and down
while (1):
	print "\nMain program loop started."

	#check what mode we are in and run appropriate function
	if mode == "mirror":
		print "\nGetting latest from weather mirror mode"
		mirrorWeatherMode(duty_cycles)
	elif mode == "invert":
		print "\nGetting latest from weather invert mode"
		invertWeatherMode(duty_cycles)
	else:
		print "\nGetting latest from lamp mode"
		lampMode(duty_cycles, DEFAULT_COLOR)

	#update the LED duty cycles
	print "\nUpdating LED duty cycles."
	updateLeds(leds, duty_cycles)

	#update the duty cycles from 0% to 100%
	#for i in range(0, 100, 1):
	#	for j in range(len(duty_cycles)):
	#		duty_cycles[j] = i

		#send new duty cyles to LEDs
	#	updateLeds( leds, duty_cycles)

		#wait for some time for the changes to take effect
	#	time.sleep(0.1)

	#update the duty cycles from 100% to 0%
	#for i in range(100, 0, -1):
	#	for j in range(len(duty_cycles)):
	#		duty_cycles[j] = i

		#send new duty cyles to LEDs
	#	updateLeds( leds, duty_cycles)

		#wait for some time for the changes to take effect
	#	time.sleep(0.1)<|endoftext|>


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

import sys
import time
import datetime
import json
import traceback
import zlib
import glob
import re

import multiprocessing      as mp
import Queue                as queue

import RPi.GPIO             as io

import boto.dynamodb2       as ddb
import boto.dynamodb.types  as ddb_types

import boto.dynamodb2.table
import boto.dynamodb2.fields

import boto.sqs             as sqs

import logging
from   logging              import warning as warn
from   logging              import debug
from   logging              import info
from   logging              import error

_AWS_REGION = "us-west-2"

_AC_RELAY_PIN               = 17
_AC_RELAY_MIN_TOGGLE_TIME   = 5.0
_PIR_PIN                    = 18
_RED_LED_PIN                = 23
_GREEN_LED_PIN              = 24

_TEMPERATURE_SLEEP_TIME     = 0.25
_TEMPERATURE_TOLERANCE      = 0.25

_SLEEP_TIME                 = 0.05
_LOG_TIME                   = 5.0
_LOG_FREQ                   = int(_LOG_TIME / _SLEEP_TIME)

_IDLE_AC_TURNOFF_TIME               = 180.0

_MAX_EVENTS = 100

def now_str() :
    return str(datetime.datetime.now())

def now_secs() :
    return time.time()

class State(object) :
    '''Controller state'''
    def __init__(self, state_table_name, unit_id) :
        self.__table = ddb.table.Table(
            state_table_name,
            schema = [ddb.fields.HashKey('id')],
            connection = ddb.connect_to_region(_AWS_REGION)
        )
        self.__id = unit_id
        # bootstrap get--assume single thread/no concurrent writers
        self.__item = self.__table.get_item(consistent = True, id = self.__id)
        self.__item['id'] = self.__id

        state_data = self.__item.get('state', None)
        if state_data is None :
            self.__state = {}
        else :
            self.__state = json.loads(zlib.decompress(state_data.value))
    def __update_state(self) :
        self.__item['state'] = ddb_types.Binary(zlib.compress(json.dumps(self.__state)))
        self.__item['mtime'] = now_str()
        self.__item.save(overwrite = True)
    def __update_value_field(self, field, value) :
        if self.__state.get(field, None) != value :
            self.__state[field] = value
            self.__state['%s_time' % field] = now_secs()
            self.__update_state()
    def __update_state_field(self, field, flag) :
        state_label = 'ACTIVE' if flag else 'IDLE'
        self.__update_value_field(field, state_label)
    def update_motion(self, has_motion) :
        self.__update_state_field('pir_state', has_motion)
    def update_lamp(self, is_on) :
        self.__update_state_field('lamp_state', is_on)
    def update_temperature(self, temperature) :
        self.__update_value_field('temperature', temperature)
    @property
    def lamp_last_updated_secs(self) :
        return now_secs() - self.__state.get('lamp_state_time', 0.0)
    @property
    def pir_last_updated_secs(self) :
        return now_secs() - self.__state.get('pir_state_time', 0.0)
    @property
    def pir_state(self) :
        return self.__state.get('pir_state', None)
    @property
    def temperature(self) :
        return self.__state.get('temperature', None)

class Toggle(object) :
    '''Represents an output to a GPIO pin'''
    def __init__(self, pin, min_time) :
        io.setup(pin, io.OUT)
        io.output(pin, io.LOW)
        self.__pin = pin
        self.__on = False
        self.__min_time = min_time
        self.__last = now_secs()
    def toggle(self) :
        return self.enable(not self.__on)
    def enable(self, on) :
        duration = now_secs() - self.__last

        if duration < self.__min_time :
            return False
        if on == self.__on :
            return True
        io.output(self.__pin, io.HIGH if on else io.LOW)
        self.__last = now_secs()
        self.__on = on
        return True
    @property
    def is_enabled(self) :
        return self.__on

def led_toggle(pin) :
    return Toggle(pin, 0.0)

def ac_toggle(pin) :
    return Toggle(pin, _AC_RELAY_MIN_TOGGLE_TIME)

_TEMP_DATA_PAT = re.compile(r't=(\d+)')

def read_w1_temp() :
    fnames = glob.glob('/sys/bus/w1/devices/28-*/w1_slave')
    if len(fnames) == 0 :
        raise Exception, 'No Wire-1 slaves detected'
    debug('Found %d W1 slave files' % len(fnames))
    with file(fnames[0], 'rb') as f :
        header = f.readline().strip()
        if not header.endswith('YES') :
            return None
        data = f.readline().strip()
        temp_match = _TEMP_DATA_PAT.search(data)
        if not temp_match :
            raise Exception, 'Invalid data from temperature sensor: %s' % data
        temp_c = int(temp_match.group(1)) / 1000.0
        temp_f = temp_c * 1.8 + 32.0
        return temp_f

def temperature_proc_func(temperature_queue, termination_flag) :
    '''Seperate process for reading temperature'''
    while not termination_flag.value :
        try :
            temperature_queue.put(read_w1_temp())
            time.sleep(_TEMPERATURE_SLEEP_TIME)
        except :
            error('Temperature loop error', exc_info = True)

def command_proc_func(sqs_queue_name, control_queue, termination_flag) :
    '''Command queue for SQS commands'''
    info('Initializing SQS command processor...')
    sqs_conn = sqs.connect_to_region(_AWS_REGION)
    sqs_queue = sqs_conn.get_queue(sqs_queue_name)
    sqs_queue.set_message_class(sqs.message.RawMessage)
    while not termination_flag.value :
        messages = sqs_queue.get_messages(10, wait_time_seconds = 20)
        info('Read %d messages from SQS' % len(messages))
        for message in messages :
            command = message.get_body()
            control_queue.put(command)
            sqs_queue.delete_message(message)

def spawn_process(target, args) :
    proc = mp.Process(target = target, args = args)
    proc.start()
    return proc

class Processes(object) :
    def __init__(self) :
        self.__termination_flag = mp.Value('l', 0)
        self.__processes = []
    def spawn(self, target, args) :
        self.__processes.append(spawn_process(target, args))
    @property
    def termination_flag(self) :
        return self.__termination_flag
    def finish(self) :
        self.__termination_flag.value = 1
        for process in self.__processes :
            process.join()

def drain_queue(data_queue) :
    values = []
    try :
        while True :
            values.append(data_queue.get_nowait())
    except queue.Empty :
        pass
    return values

def main(args) :
    if len(args) != 4 :
        print >> sys.stderr, 'USAGE: %s TABLE QUEUE ID' % args[0]
        sys.exit(2)
    # logging
    logging.basicConfig(
        format = '%(asctime)s %(levelname)-8s%(funcName)-20s %(message)s',
        level = logging.INFO
    )

    # args
    state_table_name = args[1]
    command_queue_name = args[2]
    unit_id = args[3]

    processes = Processes()
    # spawn off command process
    control_queue = mp.Queue()
    processes.spawn(
        target = command_proc_func,
        args = (command_queue_name, control_queue, processes.termination_flag)
    )
    # spawn off temperature process
    temperature_queue = mp.Queue()
    processes.spawn(
        target = temperature_proc_func,
        args = (temperature_queue, processes.termination_flag)
    )

    # operational table
    info('Initializing DDB backed operational state...')
    state = State(state_table_name, unit_id)

    # GPIO setup
    try :
        io.setmode(io.BCM)
        io.setup(_PIR_PIN, io.IN)

        error_led = led_toggle(_RED_LED_PIN)
        pir_led = led_toggle(_GREEN_LED_PIN)
        lamp = ac_toggle(_AC_RELAY_PIN)

        # command jump table via the class/lexical closure
        class CommandProcessor(object) :
            def cmd_lamp(self, val) :
                if val == 'on' :
                    if lamp.enable(True) :
                        info('Turning on lamp via command')
                        state.update_lamp(True)
                elif val == 'off' :
                    if lamp.enable(False) :
                        info('Turning off lamp via command')
                        state.update_lamp(False)
                else :
                    warn('Invalid lamp command value: %s' % val)
        command_processor = CommandProcessor()

        # event loop
        event_count = 0
        motion_count = 0
        temperatures = []
        info('Starting main event loop...')
        while True :
            try :
                event_count += 1

                if io.input(_PIR_PIN) :
                    motion_count += 1
                    pir_led.enable(True)
                    # turn on lamp
                    if not lamp.is_enabled :
                        if lamp.enable(True) :
                            info('Turning on lamp via motion')
                            state.update_lamp(True)
                elif event_count == 1 :
                    pir_led.enable(False)

                temperatures.extend(drain_queue(temperature_queue))

                # get commands
                command_strs = drain_queue(control_queue)
                for command_str in command_strs :
                    info('Received command: %s' % command_str)
                    command = json.loads(command_str)
                    command_unit_id = command.get('id', None)
                    if command_unit_id == unit_id :
                        command_type = command.get('type', None)
                        if command_type == None :
                            warn('No command type specified')
                        else :
                            command_func = getattr(command_processor, 'cmd_%s' % command_type, None)
                            if command_func is None :
                                warn('Unknown command: %s' % command_type)
                            else :
                                command_func(command['value'])
                    else :
                        info('Ignoring command for unit: %s' % command_unit_id)

                # flush event
                if event_count >= _LOG_FREQ :
                    info('Detected %d/%d events in %0.2f seconds' \
                            % (motion_count, event_count, _LOG_TIME))
                    state.update_motion(motion_count > 0)
                    # turn off lamp
                    if state.pir_state == 'IDLE' \
                            and state.pir_last_updated_secs > _IDLE_AC_TURNOFF_TIME \
                            and state.lamp_last_updated_secs > _IDLE_AC_TURNOFF_TIME \
                            and lamp.is_enabled:
                        info('Idle for %0.2fs, shutting off lamp' % state.pir_last_updated_secs)
                        lamp.enable(False)
                        state.update_lamp(False)

                    event_count = 0
                    motion_count = 0

                    # record temperature
                    if len(temperatures) > 0 :
                        temperature = sum(temperatures) / len(temperatures)
                        if state.temperature is None \
                                or abs(state.temperature - temperature) > _TEMPERATURE_TOLERANCE :
                            info('Detected temperature: %0.2f F' % temperature)
                            state.update_temperature(temperature)
                        del temperatures[:]

                if error_led.is_enabled :
                    info('Event loop succeeded, switching error LED off')
                    error_led.enable(False)
                time.sleep(_SLEEP_TIME)
            except :
                error('Event loop error', exc_info = True)
                error_led.enable(True)
                if sys.exc_info()[0] is KeyboardInterrupt :
                    raise sys.exc_info()[1]
    finally :
        processes.finish()
        io.cleanup()

if __name__ == '__main__' :
    main(sys.argv)
<commit_msg>Added windowing for temperature calculation. Added basic event logging for temperature.<commit_after>#!/usr/bin/env python

import sys
import time
import datetime
import json
import traceback
import zlib
import glob
import re
import collections

import multiprocessing      as mp
import Queue                as queue

import RPi.GPIO             as io

import boto.dynamodb2       as ddb
import boto.dynamodb.types  as ddb_types

import boto.dynamodb2.table
import boto.dynamodb2.fields

import boto.sqs             as sqs

import logging
from   logging              import warning as warn
from   logging              import debug
from   logging              import info
from   logging              import error

_AWS_REGION                 = "us-west-2"

_AC_RELAY_PIN               = 17
_AC_RELAY_MIN_TOGGLE_TIME   = 5.0
_PIR_PIN                    = 18
_RED_LED_PIN                = 23
_GREEN_LED_PIN              = 24

_SLEEP_TIME                 = 0.05
_LOG_TIME                   = 5.00
_LOG_FREQ                   = int(_LOG_TIME / _SLEEP_TIME)

_TEMPERATURE_SLEEP_TIME     = 0.50
_TEMPERATURE_TOLERANCE      = 0.25
_TEMPERATURE_WINDOW_SIZE    = int(_LOG_TIME / _TEMPERATURE_SLEEP_TIME) * 2

_IDLE_AC_TURNOFF_TIME       = 180.0

_MAX_EVENTS                 = 300

def now_str() :
    return str(datetime.datetime.now())

def now_secs() :
    return time.time()

class State(object) :
    '''Controller state'''
    def __init__(self, state_table_name, unit_id) :
        self.__table = ddb.table.Table(
            state_table_name,
            schema = [ddb.fields.HashKey('id')],
            connection = ddb.connect_to_region(_AWS_REGION)
        )
        self.__id = unit_id
        # bootstrap get--assume single thread/no concurrent writers
        self.__item = self.__table.get_item(consistent = True, id = self.__id)
        self.__item['id'] = self.__id

        state_data = self.__item.get('state', None)
        if state_data is None :
            self.__state = {}
        else :
            self.__state = json.loads(zlib.decompress(state_data.value))
    def __update_state(self) :
        self.__item['state'] = ddb_types.Binary(zlib.compress(json.dumps(self.__state)))
        self.__item['mtime'] = now_str()
        self.__item.save(overwrite = True)
    def __update_value_field(self, field, value) :
        if self.__state.get(field, None) != value :
            self.__state[field] = value
            self.__state['%s_time' % field] = now_secs()
            self.__update_state()
    def __update_state_field(self, field, flag) :
        state_label = 'ACTIVE' if flag else 'IDLE'
        self.__update_value_field(field, state_label)
    def update_motion(self, has_motion) :
        self.__update_state_field('pir_state', has_motion)
    def update_lamp(self, is_on) :
        self.__update_state_field('lamp_state', is_on)
    def update_temperature(self, temperature) :
        self.__update_value_field('temperature', temperature)
    def add_temperature_event(self, temperature) :
        events = self.__state.get('temperature_events', None)
        if events is None :
            events = []
            self.__state['temperature_events'] = events
        events.append(dict(temperature = temperature, time = now_secs()))
        if len(events) > _MAX_EVENTS :
            del events[0]
        self.__update_state()
    @property
    def lamp_last_updated_secs(self) :
        return now_secs() - self.__state.get('lamp_state_time', 0.0)
    @property
    def pir_last_updated_secs(self) :
        return now_secs() - self.__state.get('pir_state_time', 0.0)
    @property
    def pir_state(self) :
        return self.__state.get('pir_state', None)
    @property
    def temperature(self) :
        return self.__state.get('temperature', None)
    @property
    def temperature_event_last_updated_secs(self) :
        events = self.__state.get('temperature_events',[])
        last_updated = 0.0
        if len(events) > 0 :
            last_updated = events[-1]['time']
        return now_secs() - last_updated

class Toggle(object) :
    '''Represents an output to a GPIO pin'''
    def __init__(self, pin, min_time) :
        io.setup(pin, io.OUT)
        io.output(pin, io.LOW)
        self.__pin = pin
        self.__on = False
        self.__min_time = min_time
        self.__last = now_secs()
    def toggle(self) :
        return self.enable(not self.__on)
    def enable(self, on) :
        duration = now_secs() - self.__last

        if duration < self.__min_time :
            return False
        if on == self.__on :
            return True
        io.output(self.__pin, io.HIGH if on else io.LOW)
        self.__last = now_secs()
        self.__on = on
        return True
    @property
    def is_enabled(self) :
        return self.__on

def led_toggle(pin) :
    return Toggle(pin, 0.0)

def ac_toggle(pin) :
    return Toggle(pin, _AC_RELAY_MIN_TOGGLE_TIME)

_TEMP_DATA_PAT = re.compile(r't=(\d+)')

def read_w1_temp() :
    fnames = glob.glob('/sys/bus/w1/devices/28-*/w1_slave')
    if len(fnames) == 0 :
        raise Exception, 'No Wire-1 slaves detected'
    debug('Found %d W1 slave files' % len(fnames))
    with file(fnames[0], 'rb') as f :
        header = f.readline().strip()
        if not header.endswith('YES') :
            return None
        data = f.readline().strip()
        temp_match = _TEMP_DATA_PAT.search(data)
        if not temp_match :
            raise Exception, 'Invalid data from temperature sensor: %s' % data
        temp_c = int(temp_match.group(1)) / 1000.0
        temp_f = temp_c * 1.8 + 32.0
        return temp_f

def temperature_proc_func(temperature_queue, termination_flag) :
    '''Seperate process for reading temperature'''
    while not termination_flag.value :
        try :
            temperature_queue.put(read_w1_temp())
            time.sleep(_TEMPERATURE_SLEEP_TIME)
        except :
            error('Temperature loop error', exc_info = True)

def command_proc_func(sqs_queue_name, control_queue, termination_flag) :
    '''Command queue for SQS commands'''
    info('Initializing SQS command processor...')
    sqs_conn = sqs.connect_to_region(_AWS_REGION)
    sqs_queue = sqs_conn.get_queue(sqs_queue_name)
    sqs_queue.set_message_class(sqs.message.RawMessage)
    while not termination_flag.value :
        messages = sqs_queue.get_messages(10, wait_time_seconds = 20)
        info('Read %d messages from SQS' % len(messages))
        for message in messages :
            command = message.get_body()
            control_queue.put(command)
            sqs_queue.delete_message(message)

def spawn_process(target, args) :
    proc = mp.Process(target = target, args = args)
    proc.start()
    return proc

class Processes(object) :
    def __init__(self) :
        self.__termination_flag = mp.Value('l', 0)
        self.__processes = []
    def spawn(self, target, args) :
        self.__processes.append(spawn_process(target, args))
    @property
    def termination_flag(self) :
        return self.__termination_flag
    def finish(self) :
        self.__termination_flag.value = 1
        for process in self.__processes :
            process.join()

def drain_queue(data_queue) :
    values = []
    try :
        while True :
            values.append(data_queue.get_nowait())
    except queue.Empty :
        pass
    return values

def main(args) :
    if len(args) != 4 :
        print >> sys.stderr, 'USAGE: %s TABLE QUEUE ID' % args[0]
        sys.exit(2)
    # logging
    logging.basicConfig(
        format = '%(asctime)s %(levelname)-8s%(funcName)-20s %(message)s',
        level = logging.INFO
    )

    # args
    state_table_name = args[1]
    command_queue_name = args[2]
    unit_id = args[3]

    processes = Processes()
    # spawn off command process
    control_queue = mp.Queue()
    processes.spawn(
        target = command_proc_func,
        args = (command_queue_name, control_queue, processes.termination_flag)
    )
    # spawn off temperature process
    temperature_queue = mp.Queue()
    processes.spawn(
        target = temperature_proc_func,
        args = (temperature_queue, processes.termination_flag)
    )

    # operational table
    info('Initializing DDB backed operational state...')
    state = State(state_table_name, unit_id)

    # GPIO setup
    try :
        io.setmode(io.BCM)
        io.setup(_PIR_PIN, io.IN)

        error_led = led_toggle(_RED_LED_PIN)
        pir_led = led_toggle(_GREEN_LED_PIN)
        lamp = ac_toggle(_AC_RELAY_PIN)

        # command jump table via the class/lexical closure
        class CommandProcessor(object) :
            def cmd_lamp(self, val) :
                if val == 'on' :
                    if lamp.enable(True) :
                        info('Turning on lamp via command')
                        state.update_lamp(True)
                elif val == 'off' :
                    if lamp.enable(False) :
                        info('Turning off lamp via command')
                        state.update_lamp(False)
                else :
                    warn('Invalid lamp command value: %s' % val)
        command_processor = CommandProcessor()

        # event loop
        event_count = 0
        motion_count = 0
        info('Temperature window size: %d' % _TEMPERATURE_WINDOW_SIZE)
        temperatures = collections.deque(maxlen = _TEMPERATURE_WINDOW_SIZE)
        info('Starting main event loop...')
        while True :
            try :
                event_count += 1

                if io.input(_PIR_PIN) :
                    motion_count += 1
                    pir_led.enable(True)
                    # turn on lamp
                    if not lamp.is_enabled :
                        if lamp.enable(True) :
                            info('Turning on lamp via motion')
                            state.update_lamp(True)
                elif event_count == 1 :
                    pir_led.enable(False)

                temperatures.extend(drain_queue(temperature_queue))

                # get commands
                command_strs = drain_queue(control_queue)
                for command_str in command_strs :
                    info('Received command: %s' % command_str)
                    command = json.loads(command_str)
                    command_unit_id = command.get('id', None)
                    if command_unit_id == unit_id :
                        command_type = command.get('type', None)
                        if command_type == None :
                            warn('No command type specified')
                        else :
                            command_func = getattr(command_processor, 'cmd_%s' % command_type, None)
                            if command_func is None :
                                warn('Unknown command: %s' % command_type)
                            else :
                                command_func(command['value'])
                    else :
                        info('Ignoring command for unit: %s' % command_unit_id)

                # flush event
                if event_count >= _LOG_FREQ :
                    info('Detected %d/%d events in %0.2f seconds' \
                            % (motion_count, event_count, _LOG_TIME))
                    state.update_motion(motion_count > 0)
                    # turn off lamp
                    if state.pir_state == 'IDLE' \
                            and state.pir_last_updated_secs > _IDLE_AC_TURNOFF_TIME \
                            and state.lamp_last_updated_secs > _IDLE_AC_TURNOFF_TIME \
                            and lamp.is_enabled:
                        info('Idle for %0.2fs, shutting off lamp' % state.pir_last_updated_secs)
                        lamp.enable(False)
                        state.update_lamp(False)

                    event_count = 0
                    motion_count = 0

                    # record temperature
                    if len(temperatures) > 0 :
                        temperature = sum(temperatures) / len(temperatures)
                        info('Detected temperature: %0.2f F' % temperature)
                        if state.temperature is None \
                                or abs(state.temperature - temperature) > _TEMPERATURE_TOLERANCE :
                            info('Updating temperature state')
                            state.update_temperature(temperature)

                if error_led.is_enabled :
                    info('Event loop succeeded, switching error LED off')
                    error_led.enable(False)
                time.sleep(_SLEEP_TIME)
            except :
                error('Event loop error', exc_info = True)
                error_led.enable(True)
                if sys.exc_info()[0] is KeyboardInterrupt :
                    raise sys.exc_info()[1]
    finally :
        processes.finish()
        io.cleanup()

if __name__ == '__main__' :
    main(sys.argv)
<|endoftext|>


<commit_before>import json
from pprint import pprint

radical_dict = {
    0:"no radical",
    1:"singlet",
    2:"doublet",
    3:"triplet"
}

charge_dict = {
    0:"outside limits",
    1:"+3",
    2:"+2",
    3:"+1",
    4:"doublet radical",
    5:"-1",
    6:"-2",
    7:"-3"
}

stereo_parity_dict = {
    0:"not stereo",
    1:"odd",
    2:"even",
    3:"unmarked"
}

h_count_dict = {
    0:"H0",
    1:"H0",
    2:"H1",
    3:"H2",
    4:"H3",
    5:"H4"
}

bond_type_dict = {
    1:"Single",
    2:"Double",
    3:"Triple",
    4:"Aromatic",
    5:"Single or Double",
    6:"Single or Aromatic",
    7:"Double or Aromatic",
    8:"Any"
}

single_bond_stereo_dict = {
    0:"Not stereo",
    1:"Up",
    4:"Either",
    6:"Down"
}

double_bond_stereo_dict = {
    0:"Use coordinates",
    3:"Cis or trans"
}

def parse_counts_line(line):
    #aaabbblllfffcccsssxxxrrrpppiiimmmvvvvvv
    #aaa = number of atoms (current max 255)*
    #bbb = number of bonds (current max 255)*
    #lll = number of atom lists (max 30)*
    #fff = (obsolete)
    #ccc = chiral flag: 0=not chiral, 1=chiral
    #sss = number of stext entries
    #xxx = (obsolete)
    #rrr = (obsolete)
    #ppp = (obsolete)
    #iii = (obsolete)
    #mmm = number of lines of additional properties,
    #vvvvv = version for the format
    ret = {}
    ret["aaa"] = int(float(line[0:3]))
    ret["bbb"] = int(float(line[3:6]))
    ret["lll"] = int(float(line[6:9]))
    ret["ccc"] = int(float(line[12:15]))
    ret["sss"] = int(float(line[15:18]))
    ret["mmm"] = int(float(line[18:21]))
    ret["vvvvv"] = line[-5:]
    return ret

def parse_atom_line(line):
    #xxxxx.xxxxyyyyy.yyyyzzzzz.zzzz aaaddcccssshhhbbbvvvHHHrrriiimmmnnneee
    #[0:10] xxxxx.xxxx = x-coordinate
    #[10:20] yyyyy.yyyy = y-coordinate
    #[20:30] zzzzz.zzzz = z-coordinate
    #[31:34] aaa = atomic symbol
    #[34:36] dd = mass difference, i.e. difference from standard mass
    #[36:39] ccc = charge 0 = uncharged or value other than these, 1 = +3, 2 = +2, 3 = +1, 4 = doublet radical, 5 = -1, 6 = -2, 7 = -3
    #[39:42] sss = atom stereo parity 0 = not stereo, 1 = odd, 2 = even, 3 = either or unmarked stereo center
    #[42:45] hhh = INGORED hydrogen count +1
    #[45:48] bbb = IGNORED stereo care box
    #[48:51] vvv = valence
    #[51:54] HHH = IGNORED H0 designator
    #[54:57] rrr = Not used
    #[57:60] iii = Not used
    #[60:63] mmm = IGNORED atom-atom mapping number 1 - number of atoms
    #[63:66] nnn = IGNORED inversion/retention flag g 0 = property not applied 1 = configuration is inverted,2 = configuration is retained
    #[66:69] eee = IGNORED 0 = property not applied, 1 = change on atom must be exactly as shown
    ret = {}
    ret["xxx"] = float(line[0:10])
    ret["yyy"] = float(line[10:20])
    ret["zzz"] = float(line[20:30])
    ret["aaa"] = line[31:34].strip()
    ret["dd"] =  int(float(line[34:36]))
    ret["ccc"] = int(float(line[36:39]))
    ret["sss"] = int(float(line[39:42]))
    ret["hhh"] = int(float(line[42:45]))
    ret["bbb"] = int(float(line[45:48]))
    ret["vvv"] = int(float(line[48:51]))
    ret["HHH"] = int(float(line[51:54]))
    ret["mmm"] = int(float(line[60:63]))
    ret["nnn"] = int(float(line[63:66]))
    ret["eee"] = int(float(line[66:69]))
    return ret

def parse_bond_line(line):
    #111222tttsssxxxrrrccc
    #111 = number of atom 1
    #222 = number of atom 2
    #ttt = bond type
    #sss = bond stereo
    #xxx = not used
    #rrr = bond topology
    #ccc = reacting center status
    ret = {}
    ret["111"] = int(float(line[0:3]))
    ret["222"] = int(float(line[3:6]))
    ret["ttt"] = int(float(line[6:9]))
    ret["sss"] = int(float(line[9:12]))
    ret["xxx"] = int(float(line[12:15]))
    ret["rrr"] = int(float(line[15:18]))
    ret["ccc"] = int(float(line[18:21]))
    return ret

def parse_prop_name(line):
    first = line.index("<")+1
    last = line.rindex(">")
    return line[first:last]

def apply_m_chg(line,mol):
    #print('apply_m_chg')
    for k in mol:
        if k.startswith('?atom'):
        #if k[0:5] == "?atom":
            mol[k]["charge"] = "0"
    #print(line)
    #print(line[9:])
    for i in range(9,len(line),8):
        aaa = line[i:i+4].strip()
        vvv = line[i+4:i+8].strip()
        #print(str(i),aaa,vvv)
        #pprint(mol)
        if vvv[0] == "-":
            mol["?atom"+aaa]["charge"] = vvv
        else:
            mol["?atom"+aaa]["charge"] = "+"+vvv
#123456789A123456789B123456789
#M  CHG  2   2  -1   5   1
def apply_m_rad(line,mol):
    for k in mol:
        if k.startswith('?atom'):
        #if k[0:4] == "atom":
            mol[k]["charge"] = "0"
            mol[k]["radical"] = "no radical"

    for i in range(11,len(line),6):
        aaa = line[i:i+3].strip()
        vvv = int(float(line[i+3:i+6].strip()))
        mol["?atom"+aaa]["radical"] = radical_dict[vvv]

def apply_m_iso(line,mol):
    for k in mol:
        if k.startswith('?atom'):
        #if k[0:4] == "atom":
            mol[k]["mass_diff"] = "0"

    for i in range(11,len(line),6):
        aaa = line[i:i+3].strip()
        vvv = line[i+3:i+6].strip()
        mol["?atom"+aaa]["mass_diff"] = "+"+radical_dict[vvv]

def parse_mol(lines, verbose = False, properties=False):
    """

    Parse the provided molfile and return a structured object representation
    that can be read by TRESTLE.

    """
    mol = {}
    num_atoms = 0
    num_bonds = 0
    num_lists = 0

    mol["name"] = lines[0]
    mol["software"] = lines[1].strip()
    if lines[2]:
        mol["comment"] = lines[2].strip()

    c_line = parse_counts_line(lines[3])
    num_atoms = c_line["aaa"]
    num_bonds = c_line["bbb"]
    num_lists = c_line["lll"]

    atom_dex = 4 + num_atoms
    bond_dex = atom_dex + num_bonds

    if verbose:
        if c_line["ccc"] == 1:
            mol["chiral"] = True
        elif c_line["ccc"] == 0:
            mol["chiral"] = False

    mol["version"] = c_line["vvvvv"]

    for l,line in enumerate(lines[4:atom_dex]):
        atom = {}
        a_line = parse_atom_line(line)
        atom["x"] = a_line["xxx"]
        atom["y"] = a_line["yyy"]
        atom["z"] = a_line["zzz"]

        atom["symbol"] = a_line["aaa"]
        #stringify mass difference or leave numeric?
        atom["mass_diff"] = str(a_line["dd"])
        atom["charge"] = charge_dict[a_line["ccc"]]
        atom["stereo_parity"] = stereo_parity_dict[a_line["sss"]]

        if a_line["vvv"] == 0:
            atom["valence"] = "no marking"
        if a_line["vvv"] > 0 and a_line["vvv"] < 15:
            atom["valence"] = str(a_line["vvv"])
        if a_line["vvv"] == 15:
            atom["valence"] = "zero valence"

        if verbose:
            atom["hydrogen_count"] = h_count_dict[a_line["hhh"]]

        mol["?atom"+str(l+1)] = atom

    for l,line in enumerate(lines[atom_dex:bond_dex]):
        bond = ['bond']
        b_line = parse_bond_line(line)

        bond.append(bond_type_dict[b_line["ttt"]])
        if bond_type_dict[b_line["ttt"]] == "Single":
            bond.append(single_bond_stereo_dict[b_line["sss"]].replace(' ','_'))
        elif bond_type_dict[b_line["ttt"]] == "Double":
            bond.append(double_bond_stereo_dict[b_line["sss"]].replace(' ','_'))
        bond.append("?atom"+str(b_line["111"]))
        bond.append("?atom"+str(b_line["222"]))

        mol['('+' '.join(bond)+')'] = True

    Ms = []
    for line in lines[bond_dex:]:
        if line[0] == "M":
            Ms.append(line)
        else:
            break

    if len(Ms) > 1:
        if len(Ms) > 2:
            for m in Ms[1:-1]:
                Ms[0] += m[11:]

        if Ms[0][3:6] == "CHG":
            apply_m_chg(Ms[0],mol)

        elif Ms[0][3:6] == "RAD":
            apply_m_rad(Ms[0],mol)

        elif Ms[0][3:6] == "ISO":
            apply_m_iso(Ms[0],mol)

    if properties:
        prop_dex = bond_dex + len(Ms)
        opened = False
        prop_header = ""
        prop_list = []
        for line in lines[prop_dex:]:
            if line:
                if not opened and line[0] == ">":
                    opened = True
                    prop_header = parse_prop_name(line)
                    prop_list = []
                    continue
                elif opened:
                    prop_list.append(line)
            elif opened:
                mol[prop_header] = '\n'.join(prop_list)
                opened = False

    return mol

def parse_molefile(filename,verbose = False,properties=False,n=-1):
    ret = []
    curr = []
    count = 0
    with open(filename,"r") as molefile:
        for line in molefile:
            line = line.rstrip('\r\n')
            if not line == "$$$$":
                curr.append(line)
            else:
                if len(curr) > 0:
                    ret.append(parse_mol(curr,verbose,properties))
                    count += 1
                    if n > 0 and count > n:
                        break
                curr = []
    return ret

if __name__ == '__main__':
    mols = parse_molefile("./example.mol",True,True,n=5)
    #mols = parse_molefile("./testSDFs/Compound_000000001_000025000.sdf",True,True,n=1)
    pprint(mols)<commit_msg>changed up how property 'M lines' get handled so that it should be more general, it now parses a whole 25000 sdf file from pubchem without error<commit_after>import json
from concept_formation.continuous_value import ContinuousValue
from pprint import pprint

radical_dict = {
    0:"no_radical",
    1:"singlet",
    2:"doublet",
    3:"triplet"
}

charge_dict = {
    0:"outside_limits",
    1:"+3",
    2:"+2",
    3:"+1",
    4:"doublet_radical",
    5:"-1",
    6:"-2",
    7:"-3"
}

stereo_parity_dict = {
    0:"not_stereo",
    1:"odd",
    2:"even",
    3:"unmarked"
}

h_count_dict = {
    0:"H0",
    1:"H0",
    2:"H1",
    3:"H2",
    4:"H3",
    5:"H4"
}

bond_type_dict = {
    1:"Single",
    2:"Double",
    3:"Triple",
    4:"Aromatic",
    5:"Single_or_Double",
    6:"Single_or_Aromatic",
    7:"Double_or_Aromatic",
    8:"Any"
}

single_bond_stereo_dict = {
    0:"Not_stereo",
    1:"Up",
    4:"Either",
    6:"Down"
}

double_bond_stereo_dict = {
    0:"Use_coordinates",
    3:"Cis_or_trans"
}

def parse_counts_line(line):
    """
    Parses the counts line of a molecule and returns it asd a dictionary

    aaabbblllfffcccsssxxxrrrpppiiimmmvvvvvv
    aaa = number of atoms (current max 255)*
    bbb = number of bonds (current max 255)*
    lll = number of atom lists (max 30)*
    fff = (obsolete)
    ccc = chiral flag: 0=not chiral, 1=chiral
    sss = number of stext entries
    xxx = (obsolete)
    rrr = (obsolete)
    ppp = (obsolete)
    iii = (obsolete)
    mmm = number of lines of additional properties,
    vvvvv = version for the format
    """
    ret = {}
    ret["aaa"] = int(float(line[0:3]))
    ret["bbb"] = int(float(line[3:6]))
    ret["lll"] = int(float(line[6:9]))
    ret["ccc"] = int(float(line[12:15]))
    ret["sss"] = int(float(line[15:18]))
    ret["mmm"] = int(float(line[18:21]))
    ret["vvvvv"] = line[-5:]
    return ret

def parse_atom_line(line):
    """
    Parses a line from the atom block and returns it as a dictionary

    xxxxx.xxxxyyyyy.yyyyzzzzz.zzzz aaaddcccssshhhbbbvvvHHHrrriiimmmnnneee
    [0:10] xxxxx.xxxx = x-coordinate
    [10:20] yyyyy.yyyy = y-coordinate
    [20:30] zzzzz.zzzz = z-coordinate
    [31:34] aaa = atomic symbol
    [34:36] dd = mass difference, i.e. difference from standard mass
    [36:39] ccc = charge 0 = uncharged or value other than these, 1 = +3, 2 = +2, 3 = +1, 4 = doublet radical, 5 = -1, 6 = -2, 7 = -3
    [39:42] sss = atom stereo parity 0 = not stereo, 1 = odd, 2 = even, 3 = either or unmarked stereo center
    [42:45] hhh = INGORED hydrogen count +1
    [45:48] bbb = IGNORED stereo care box
    [48:51] vvv = valence
    [51:54] HHH = IGNORED H0 designator
    [54:57] rrr = Not used
    [57:60] iii = Not used
    [60:63] mmm = IGNORED atom-atom mapping number 1 - number of atoms
    [63:66] nnn = IGNORED inversion/retention flag g 0 = property not applied 1 = configuration is inverted,2 = configuration is retained
    [66:69] eee = IGNORED 0 = property not applied, 1 = change on atom must be exactly as shown
    """
    ret = {}
    ret["xxx"] = float(line[0:10])
    ret["yyy"] = float(line[10:20])
    ret["zzz"] = float(line[20:30])
    ret["aaa"] = line[31:34].strip()
    ret["dd"] =  int(float(line[34:36]))
    ret["ccc"] = int(float(line[36:39]))
    ret["sss"] = int(float(line[39:42]))
    ret["hhh"] = int(float(line[42:45]))
    ret["bbb"] = int(float(line[45:48]))
    ret["vvv"] = int(float(line[48:51]))
    ret["HHH"] = int(float(line[51:54]))
    ret["mmm"] = int(float(line[60:63]))
    ret["nnn"] = int(float(line[63:66]))
    ret["eee"] = int(float(line[66:69]))
    return ret

def parse_bond_line(line):
    """
    Parses a line from a bondblock and turns it into a dict

    111222tttsssxxxrrrccc
    111 = number of atom 1
    222 = number of atom 2
    ttt = bond type
    sss = bond stereo
    xxx = not used
    rrr = bond topology
    ccc = reacting center status
    """
    ret = {}
    ret["111"] = int(float(line[0:3]))
    ret["222"] = int(float(line[3:6]))
    ret["ttt"] = int(float(line[6:9]))
    ret["sss"] = int(float(line[9:12]))
    ret["xxx"] = int(float(line[12:15]))
    ret["rrr"] = int(float(line[15:18]))
    ret["ccc"] = int(float(line[18:21]))
    return ret

def parse_data_name(line):
    """
    Parses the name of a data item line, which will be used as an attribute name
    """
    first = line.index("<")+1
    last = line.rindex(">")
    return line[first:last]

def apply_m_chg(line,mol):
    """
    Parses a CHG line from the property block.

    This will 0-out the charge on any atom that is not listed.
    0123456789
    M CHGnn8 aaa vvv ...
    aaa An atom number to alter the charge for
    vvv The ammount of charge on the target atom
    """
    if len(line) == 0:
        return mol
    for k in mol:
        if k.startswith('?atom'):
            mol[k]["charge"] = "0"
    line = line.rsplit(r'\s+')[3:]
    for i in range(0,len(line),2):
        aaa = int(line[i])
        vvv = line[i+1]
        if vvv[0] == "-":
            mol["?atom%04d"%aaa]["charge"] = vvv
        else:
            mol["?atom%04d"%aaa]["charge"] = "+"+vvv
    return mol

def apply_m_rad(line,mol):
    """
    Parses a RAD line from the property block

    M RADnn8 aaa vvv ...
    aaa An atom number to alter the radical value of
    vvv A key into the radical_dict to set the radical value to
    """
    if len(line) == 0:
        return mol
    for k in mol:
        if k.startswith('?atom'):
            mol[k]["charge"] = "0"
            mol[k]["radical"] = "no radical"

    line = line.rsplit(r'\s+')[3:]
    for i in range(0,len(line),2):
        aaa = int(line[i])
        vvv = int(line[i+1])
        mol["?atom%04d"%aaa]["radical"] = radical_dict[vvv]
    return mol

def apply_m_iso(line,mol):
    """
    Parses and applies an ISO line from the property block

    M ISOnn8 aaa vvv ...
    aaa An atom numer to alter the mass_dif of
    vvv The mass_diff value to set
    """
    if len(line) == 0:
        return mol
    for k in mol:
        if k.startswith('?atom'):
            mol[k]["mass_diff"] = "0"

    line = line.rsplit(r'\s+')[3:]
    for i in range(0,len(line),2):
        aaa = int(line[i])
        vvv = int(line[i]+1)
        mol["?atom%04d"%aaa]["mass_diff"] = "+"+radical_dict[vvv]
    return mol

def merge_m_lines(lines):
    if len(lines) == 0:
        return ''
    line0 = lines[0]
    for l in lines[1:]:
        line0 += l[8:]
    return line0

def parse_mol(lines, verbose = False, data_items=False, tuple_realtions=False):
    """
    Parse the provided molfile and return a structured object representation
    that can be read by TRESTLE.
    """
    mol = {}
    num_atoms = 0
    num_bonds = 0
    num_lists = 0

    mol["name"] = lines[0]
    mol["software"] = lines[1].strip()
    if lines[2]:
        mol["comment"] = lines[2].strip()

    ### COUNTS LINE ###
    c_line = parse_counts_line(lines[3])
    num_atoms = c_line["aaa"]
    num_bonds = c_line["bbb"]
    num_lists = c_line["lll"]

    atom_dex = 4 + num_atoms
    bond_dex = atom_dex + num_bonds

    if verbose:
        if c_line["ccc"] == 1:
            mol["chiral"] = True
        elif c_line["ccc"] == 0:
            mol["chiral"] = False

    mol["version"] = c_line["vvvvv"]

    ### ATOM BLOCK ###
    for l,line in enumerate(lines[4:atom_dex]):
        atom = {}
        a_line = parse_atom_line(line)
        atom["x"] = a_line["xxx"]
        atom["y"] = a_line["yyy"]
        atom["z"] = a_line["zzz"]

        atom["symbol"] = a_line["aaa"]
        #stringify mass difference or leave numeric?
        atom["mass_diff"] = str(a_line["dd"])
        atom["charge"] = charge_dict[a_line["ccc"]]
        atom["stereo_parity"] = stereo_parity_dict[a_line["sss"]]

        if a_line["vvv"] == 0:
            atom["valence"] = "no marking"
        if a_line["vvv"] > 0 and a_line["vvv"] < 15:
            atom["valence"] = str(a_line["vvv"])
        if a_line["vvv"] == 15:
            atom["valence"] = "zero valence"

        if verbose:
            atom["hydrogen_count"] = h_count_dict[a_line["hhh"]]

        mol["?atom%04d"%(l+1)] = atom

    ### BOND BLOCK ###
    for l,line in enumerate(lines[atom_dex:bond_dex]):
        bond = ['bond']
        b_line = parse_bond_line(line)

        bond.append(bond_type_dict[b_line["ttt"]])
        if bond_type_dict[b_line["ttt"]] == "Single":
            bond.append(single_bond_stereo_dict[b_line["sss"]])
        elif bond_type_dict[b_line["ttt"]] == "Double":
            bond.append(double_bond_stereo_dict[b_line["sss"]])
        bond.append("?atom%04d"%b_line["111"])
        bond.append("?atom%04d"%b_line["222"])

        if tuple_realtions:
            mol[tuple(bond)] = True
        else:
            mol['('+' '.join(bond)+')'] = True

    ### PROPERTIES BLOCK ###
    Ms = []
    for line in lines[bond_dex:]:
        if line == 'M  END':
            break
        if line[0] == "M":
            Ms.append(line)
        else:
            break

    if len(Ms) > 0:
        apply_m_chg(merge_m_lines([l for l in Ms if l.startswith('M  CHG')]),mol)
        apply_m_rad(merge_m_lines([l for l in Ms if l.startswith('M  RAD')]),mol)
        apply_m_iso(merge_m_lines([l for l in Ms if l.startswith('M  ISO')]),mol)

    ### DATA ITEMS ###
    if data_items:
        data_dex = bond_dex + len(Ms)
        opened = False
        data_header = ""
        data_list = []
        for line in lines[data_dex:]:
            if line:
                if not opened and line[0] == ">":
                    opened = True
                    data_header = parse_data_name(line)
                    data_list = []
                    continue
                elif opened:
                    data_list.append(line)
            elif opened:
                mol[data_header] = '\n'.join(data_list)
                opened = False

    return mol

def parse_sdf_file(filename,verbose = False,data_items=False,tuple_realtions=False,n=-1):
    ret = []
    curr = []
    count = 0
    with open(filename,"r") as molefile:
        for line in molefile:
            line = line.rstrip('\r\n')
            if not line == "$$$$":
                curr.append(line)
            else:
                if len(curr) > 0:
                    ret.append(parse_mol(curr,verbose,data_items,tuple_realtions))
                    count += 1
                    if n > 0 and count > n:
                        break
                curr = []
    return ret

if __name__ == '__main__':
    mols = parse_sdf_file("./example.mol",True,True,n=5)
    pprint(mols[0])

    # mols = parse_sdf_file("./testSDFs/Compound_000000001_000025000.sdf",True,True)
    # bond_counts = ContinuousValue()
    # atom_counts = ContinuousValue()

    # bmax = -10000000
    # bmin = 100000000
    # amax = -10000000
    # amin = 100000000

    # a_dist = {}
    # b_dist = {}

    # for mol in mols:
    #     atoms = [v for v in mol if isinstance(v,str) and v.startswith('?atom')]
    #     bons = [v for v in mol if isinstance(v,str) and v.startswith('(bond')]

    #     b_count = len(bons)
    #     a_count = len(atoms)

    #     if a_count < amin:
    #         amin = a_count
    #     if a_count > amax:
    #         amax = a_count

    #     if b_count < bmin:
    #         bmin = b_count
    #     if b_count > bmax:
    #         bmax = b_count

    #     bond_counts.update(b_count)
    #     atom_counts.update(a_count)

    #     if a_count not in a_dist:
    #         a_dist[a_count] = 0
    #     if b_count not in b_dist:
    #         b_dist[b_count] = 0

    #     a_dist[a_count] += 1
    #     b_dist[b_count] += 1

    # print('Bonds',bond_counts)
    # print('Min:',bmin,'Max:',bmax)
    # print('Atoms',atom_counts)
    # print('Min:',amin,'Max:',amax)

    # with open('atom_dist.txt','w') as out:
    #     out.write('AtomCount\tFrequency\n')
    #     keys = list(a_dist.keys())
    #     for k in sorted(keys):
    #         out.write(str(k)+'\t'+str(a_dist[k])+'\n')
<|endoftext|>


<commit_before>
def _c(s):
  return s.replace(" ", "").lower()

class Sites(object):
    """
    Cre/Lox sites
    """

    loxP     = _c("ATAACTTCGTATA GCATACAT TATACGAAGTTAT")
    lox66    = _c("ATAACTTCGTATA GCATACAT TATACGAACGGTA")
    lox71    = _c("TACCGTTCGTATA GCATACAT TATACGAAGTTAT")
    lox72    = _c("TACCGTTCGTATA GCATACAT TATACGAACGGTA")

    lox5171  = _c("ATAACTTCGTATA GtAcACAT TATACGAAGTTAT")
    lox2272  = _c("ATAACTTCGTATA GgATACtT TATACGAAGTTAT")

    loxm2    = _c("ATAACTTCGTATA TGGTTTCT TATACGAAGTTAT")
    loxm2_66 = _c("ATAACTTCGTATA TGGTTTCT TATACGAACGGTA")
    loxm2_71 = _c("TACCGTTCGTATA TGGTTTCT TATACGAAGTTAT")
    loxm2_72 = _c("TACCGTTCGTATA TGGTTTCT TATACGAACGGTA")

    """
    Flp/FRT sites
    """

    Frt  = _c("GAAGTTCCTATTC tctagaaa GTATAGGAACTTC")
    Frt1 = _c("GAAGTTCCTATTC tctagata GTATAGGAACTTC")
    Frt2 = _c("GAAGTTCCTATTC tctactta GTATAGGAACTTC")
    Frt3 = _c("GAAGTTCCTATTC ttcaaata GTATAGGAACTTC")
    Frt4 = _c("GAAGTTCCTATTC tctagaag GTATAGGAACTTC")
    Frt5 = _c("GAAGTTCCTATTC ttcaaaag GTATAGGAACTTC")

    """
    PhiC31 sites
    """

    PhiC31_attB_tt = _c("TGCGGGTGCCAGGGCGTGCCC tt GGGCTCCCCGGGCGCGTACTCC")
    PhiC31_attP_tt = _c("GTGCCCCAACTGGGGTAACCT tt GAGTTCTCTCAGTTGGGGG")

    """
    Bxb1 sites
    """

    Bxb1_attB =     _c("TCGGCCGGCTTGTCGACGACG gcggtctc CGTCGTCAGGATCATCCGGGC")
    Bxb1_attP = _c("GTCGTGGTTTGTCTGGTCAACCACC gcggtctc AGTGGTGTACGGTACAAACCCCGAC")

class SiteLocation(object):

    def __init__(self, site, fragment, insert, start, end, direction):
        self.site = site
        self.fragment = fragment
        self.insert = insert
        self.start = start
        self.end = end
        self.direction = direction

        # after conversion from bp coordinates to chunks
        self.prev_chunk = None
        self.first_chunk = None
        self.last_chunk = None
        self.next_chunk = None

    @property
    def fragment_id(self):
        return self.fragment.id if self.fragment is not None else None

    @property
    def on_insert(self):
        return self.insert is not None

    def chunkify(self):
        XXX

class Recombination(object):
    """
    Generic class to model a recombination event.
    """

    def possible_locations(self, all_locations):
        XXX also disambiguity

    def generate_events(self, locations, event_cls):
        location_sets = self.possible_locations(locations)
        events = []
        for locs in location_sets:
            events.append(event_cls(self, locs))
        return events

class Integration(Recombination):

    def __init__(self, site_insert, site_genome, recombined_site_genome_left, recombined_site_genome_right):
        self.site_insert = site_insert
        self.site_genome = site_genome
        self.recombined_site_genome_left = recombined_site_genome_left
        self.recombined_site_genome_right = recombined_site_genome_right

    def required_genome_sites(self):
        return [self.site_genome]

    def required_insert_sites(self):
        return [self.site_insert]

    def events(self, locations):
        return self.generate_events(locations, IntegrationEvent)

class Excision(Recombination):
    def __init__(self, site_left, site_right, recombined_site):
        self.site_left = site_left
        self.site_right = site_right
        self.recombined_site = recombined_site

    def required_genome_sites(self):
        return [self.site_left, self.site_right]

    def required_insert_sites(self):
        return []

    def events(self, locations):
        return self.generate_events(locations, ExcisionEvent)

class Inversion(Recombination):
    def __init__(self, site_left, site_right, recombined_site_left, recombined_site_right):
        self.site_left = site_left
        self.site_right = site_right
        self.recombined_site_left = recombined_site_left
        self.recombined_site_right = recombined_site_right

    def required_genome_sites(self):
        return [self.site_left, self.site_right]

    def required_insert_sites(self):
        return []

    def events(self, locations):
        return self.generate_events(locations, InversionEvent)

class RMCE(Recombination):
    def __init__(self, site_left_insert, site_right_insert, site_left_genome, site_right_genome,
                 recombined_site_left_genome, recombined_site_right_genome):
        self.site_left_insert = site_left_insert
        self.site_right_insert = site_right_insert
        self.site_left_genome = site_left_genome
        self.site_right_genome = site_right_genome
        self.recombined_site_left_genome = recombined_site_left_genome
        self.recombined_site_right_genome = recombined_site_right_genome

    def required_genome_sites(self):
        return [self.site_left_genome, self.site_right_genome]

    def required_insert_sites(self):
        return [self.site_left_insert, self.site_right_insert]

    def events(self, locations):
        return self.generate_events(locations, RMCEEvent)

class Event(object):

    def __init__(self, recombination, locations):
        self.recombination = recombination
        self.locations = locations

    @property
    def fragment_id(self):
        for loc in self.locations:
            if loc.fragment_id:
                return loc.fragment_id
        assert False

    def prepare(self):
        for loc in self.locations:
            loc.chunkify()

class IntegrationEvent(Event):

    def run(self, new_fragment, insert, is_insert_circular):
        insert = self.rotate_insert()
        assert insert.index(recombination.site_insert) == 0

        XXX
        remove old site?
        create new chunk with insert
        flank with two sites

class ExcisionEvent(Event):

    def run(self, new_fragment, insert, is_insert_circular):
        # TODO
        pass

class InversionEvent(Event):

    def run(self, new_fragment, insert, is_insert_circular):
        # TODO
        pass

class RMCEEvent(Event):

    def run(self, new_fragment, insert, is_insert_circular):
        # TODO
        pass

class Reaction(object):

    @staticmethod
    def allowed():
        return []

    def __init__(self, parent_genome, insert, is_insert_circular):
        self.parent_genome = parent_genome
        self.insert = insert
        self.is_insert_circular = is_insert_circular
        self.locations = {}
        self.events = None
        self.errors = []

    @classmethod
    def __sites_on_genome(cls):
        sites = []
        for recombination in cls.allowed():
            sites.extend(recombination.required_genome_sites())
        return set(sites)

    @classmethod
    def __sites_on_insert(cls):
        sites = []
        for recombination in cls.allowed():
            sites.extend(recombination.required_insert_sites())
        return set(sites)

    @staticmethod
    def __add_reverse_sites(sites):
        new_sites = []
        for s in sites:
            new_sites.append(s)
            new_sites.append(rc(s))
        return set(new_sites)

    def determine_site_locations(self):
	# FIXME this is a very slow implementation, we can make this faster by
	# using blast (i think)

        if self.insert:
            sites = self.__add_reverse_sites(self.__sites_on_insert())
            template = self.insert
            if self.is_insert_circular:
                template = self.insert*2
            template = template.lower()
            XXX

        XXX

    def group_into_events(self):
	# TODO if we attempt an integration with loxP on loxP, how do we return
	# that as an error not just silently ignore?

        self.determine_site_locations(self)
        self.events = []

        for recombination in self.allowed():
            self.events.extend(recombination.events(self.locations))

    def run_reaction(self):
        self.group_into_events()
        if self.errors:
            return
        if len(self.events) == 0:
            return

        new_genome = self.parent_genome.update()
        new_genome.name = genome_name
        new_genome.notes = notes
        new_genome.save()

        old_to_new_fragment_dict = {}

	# first prepare all events for execution, then execute; this allows
	# events to convert bp coordinates to chunk objects, so we are not
	# depending on bp coordinates from before making any changes

        for event in self.events:
            if event.fragment_id not in old_to_new_fragment_dict:
                with new_genome.update_fragment_by_fragment_id(
                    event.fragment_id, new_fragment=True
                ) as f:
                    old_to_new_fragment_dict[event.fragment_id] = f
            new_fragment = old_to_new_fragment_dict[event.fragment_id]
            event.prepare(new_fragment)

        for event in self.events:
            new_fragment = old_to_new_fragment_dict[event.fragment_id]
            event.run(new_fragment)

        return new_genome

class CreLoxReaction(Reaction):

    @staticmethod
    def allowed():
        return [
            Integration(Sites.lox66, Sites.lox71, Sites.lox72, Sites.loxP),
            Integration(Sites.lox71, Sites.lox66, Sites.loxP, Sites.lox72),

            Excision(Sites.loxP, Sites.loxP, Sites.loxP),
            Excision(Sites.lox66, Sites.loxP, Sites.loxP),
            Excision(Sites.loxP, Sites.lox66, Sites.lox66),
            Excision(Sites.lox71, Sites.loxP, Sites.lox71),
            Excision(Sites.loxP, Sites.lox71, Sites.loxP),

            Inversion(Sites.loxP, rc(Sites.loxP), Sites.loxP, rc(Sites.loxP)),
            Inversion(Sites.lox66, rc(Sites.loxP), Sites.loxP, rc(Sites.lox66)),
            Inversion(Sites.loxP, rc(Sites.lox66), Sites.lox66, rc(Sites.loxP)),
            Inversion(Sites.lox71, rc(Sites.loxP), Sites.lox71, rc(Sites.loxP)),
            Inversion(Sites.loxP, rc(Sites.lox71), Sites.loxP, rc(Sites.lox71)),

            RMCE(Sites.loxP, Sites.lox2272, Sites.loxP, Sites.lox2272, Sites.loxP, Sites.lox2272),
            RMCE(Sites.lox66, Sites.lox2272, Sites.lox71, Sites.lox2272, Sites.lox72, Sites.lox2272),
        ]
<commit_msg>more progress<commit_after>
def _c(s):
  return s.replace(" ", "").lower()

class Sites(object):
    """
    Cre/Lox sites
    """

    loxP     = _c("ATAACTTCGTATA GCATACAT TATACGAAGTTAT")
    lox66    = _c("ATAACTTCGTATA GCATACAT TATACGAACGGTA")
    lox71    = _c("TACCGTTCGTATA GCATACAT TATACGAAGTTAT")
    lox72    = _c("TACCGTTCGTATA GCATACAT TATACGAACGGTA")

    lox5171  = _c("ATAACTTCGTATA GtAcACAT TATACGAAGTTAT")
    lox2272  = _c("ATAACTTCGTATA GgATACtT TATACGAAGTTAT")

    loxm2    = _c("ATAACTTCGTATA TGGTTTCT TATACGAAGTTAT")
    loxm2_66 = _c("ATAACTTCGTATA TGGTTTCT TATACGAACGGTA")
    loxm2_71 = _c("TACCGTTCGTATA TGGTTTCT TATACGAAGTTAT")
    loxm2_72 = _c("TACCGTTCGTATA TGGTTTCT TATACGAACGGTA")

    """
    Flp/FRT sites
    """

    Frt  = _c("GAAGTTCCTATTC tctagaaa GTATAGGAACTTC")
    Frt1 = _c("GAAGTTCCTATTC tctagata GTATAGGAACTTC")
    Frt2 = _c("GAAGTTCCTATTC tctactta GTATAGGAACTTC")
    Frt3 = _c("GAAGTTCCTATTC ttcaaata GTATAGGAACTTC")
    Frt4 = _c("GAAGTTCCTATTC tctagaag GTATAGGAACTTC")
    Frt5 = _c("GAAGTTCCTATTC ttcaaaag GTATAGGAACTTC")

    """
    PhiC31 sites
    """

    PhiC31_attB_tt = _c("TGCGGGTGCCAGGGCGTGCCC tt GGGCTCCCCGGGCGCGTACTCC")
    PhiC31_attP_tt = _c("GTGCCCCAACTGGGGTAACCT tt GAGTTCTCTCAGTTGGGGG")

    """
    Bxb1 sites
    """

    Bxb1_attB =     _c("TCGGCCGGCTTGTCGACGACG gcggtctc CGTCGTCAGGATCATCCGGGC")
    Bxb1_attP = _c("GTCGTGGTTTGTCTGGTCAACCACC gcggtctc AGTGGTGTACGGTACAAACCCCGAC")

class SiteLocation(object):

    def __init__(self, site, fragment, insert, start, end, direction):
        self.site = site
        self.fragment = fragment
        self.insert = insert
        self.start = start
        self.end = end
        self.direction = direction

        # after conversion from bp coordinates to chunks
        self.prev_chunk = None
        self.first_chunk = None
        self.last_chunk = None
        self.next_chunk = None

    @property
    def fragment_id(self):
        return self.fragment.id if self.fragment is not None else None

    @property
    def on_insert(self):
        return self.insert is not None

    def chunkify(self):
        XXX

class Recombination(object):
    """
    Generic class to model a recombination event.
    """

    def possible_locations(self, all_locations):
        XXX also disambiguity

    def generate_events(self, locations, event_cls):
        location_sets = self.possible_locations(locations)
        events = []
        for locs in location_sets:
            events.append(event_cls(self, locs))
        return events

class Integration(Recombination):

    def __init__(self, site_insert, site_genome, recombined_site_genome_left, recombined_site_genome_right):
        self.site_insert = site_insert
        self.site_genome = site_genome
        self.recombined_site_genome_left = recombined_site_genome_left
        self.recombined_site_genome_right = recombined_site_genome_right

    def required_genome_sites(self):
        return [self.site_genome]

    def required_insert_sites(self):
        return [self.site_insert]

    def events(self, locations):
        return self.generate_events(locations, IntegrationEvent)

class Excision(Recombination):
    def __init__(self, site_left, site_right, recombined_site):
        self.site_left = site_left
        self.site_right = site_right
        self.recombined_site = recombined_site

    def required_genome_sites(self):
        return [self.site_left, self.site_right]

    def required_insert_sites(self):
        return []

    def events(self, locations):
        return self.generate_events(locations, ExcisionEvent)

class Inversion(Recombination):
    def __init__(self, site_left, site_right, recombined_site_left, recombined_site_right):
        self.site_left = site_left
        self.site_right = site_right
        self.recombined_site_left = recombined_site_left
        self.recombined_site_right = recombined_site_right

    def required_genome_sites(self):
        return [self.site_left, self.site_right]

    def required_insert_sites(self):
        return []

    def events(self, locations):
        return self.generate_events(locations, InversionEvent)

class RMCE(Recombination):
    def __init__(self, site_left_insert, site_right_insert, site_left_genome, site_right_genome,
                 recombined_site_left_genome, recombined_site_right_genome):
        self.site_left_insert = site_left_insert
        self.site_right_insert = site_right_insert
        self.site_left_genome = site_left_genome
        self.site_right_genome = site_right_genome
        self.recombined_site_left_genome = recombined_site_left_genome
        self.recombined_site_right_genome = recombined_site_right_genome

    def required_genome_sites(self):
        return [self.site_left_genome, self.site_right_genome]

    def required_insert_sites(self):
        return [self.site_left_insert, self.site_right_insert]

    def events(self, locations):
        return self.generate_events(locations, RMCEEvent)

class Event(object):

    def __init__(self, recombination, locations):
        self.recombination = recombination
        self.locations = locations

    @property
    def fragment_id(self):
        for loc in self.locations:
            if loc.fragment_id:
                return loc.fragment_id
        assert False

    def prepare(self):
        for loc in self.locations:
            loc.chunkify()

class IntegrationEvent(Event):

    def run(self, new_fragment, insert, is_insert_circular):
        insert = self.rotate_insert()
        assert insert.index(recombination.site_insert) == 0

        XXX
        remove old site?
        create new chunk with insert
        flank with two sites

class ExcisionEvent(Event):

    def run(self, new_fragment, insert, is_insert_circular):
        # TODO
        pass

class InversionEvent(Event):

    def run(self, new_fragment, insert, is_insert_circular):
        # TODO
        pass

class RMCEEvent(Event):

    def run(self, new_fragment, insert, is_insert_circular):
        # TODO
        pass

def add_reverse_sites(sites):
    new_sites = []
    for s in sites:
        new_sites.append(s)
        new_sites.append(rc(s))
    return set(new_sites)

def find_site_locations_on_sequence(sequence, is_circular, sites, fragment_obj=None):
    sites = add_reverse_sites(sites)
    template = sequence
    if is_circular:
        template = sequence*2
    template = template.lower()

    locations = []
    for site in sites:
        locations.extend(
            find_query_locations_on_duplicated_template(
                template, len(sequence), site, fragment_obj=fragment_obj
            )
        )
    return locations

def find_query_locations_on_duplicated_template(template, sequence_len, site, fragment_obj=fragment_obj):
    XXX

class Reaction(object):

    @staticmethod
    def allowed():
        return []

    def __init__(self, parent_genome, insert, is_insert_circular):
        self.parent_genome = parent_genome
        self.insert = insert
        self.is_insert_circular = is_insert_circular
        self.locations = {}
        self.events = None
        self.errors = []

    @classmethod
    def __sites_on_genome(cls):
        sites = []
        for recombination in cls.allowed():
            sites.extend(recombination.required_genome_sites())
        return set(sites)

    @classmethod
    def __sites_on_insert(cls):
        sites = []
        for recombination in cls.allowed():
            sites.extend(recombination.required_insert_sites())
        return set(sites)

    def determine_site_locations(self):
	# FIXME this is a very slow implementation, we can make this faster by
	# using cached results

        locations = []

        if self.insert:
            locations.extend(
                find_site_locations_on_sequence(
                    self.insert,
                    self.is_insert_circular,
                    self.__sites_on_insert()
                )
            )

        sites_on_genome = self.__sites_on_genome()
        for fragment in self.parent_genome.fragments.all():
            fragment = fragment.indexed_fragment()
            locations.extend(
                find_site_locations_on_sequence(
                    fragment.sequence,
                    fragment.circular,
                    sites_on_genome,
                    fragment_obj=fragment
                )
            )

        return locations

    def group_into_events(self):
	# TODO if we attempt an integration with loxP on loxP, how do we return
	# that as an error not just silently ignore?

        self.determine_site_locations(self)
        self.events = []

        for recombination in self.allowed():
            self.events.extend(recombination.events(self.locations))

    def run_reaction(self):
        self.group_into_events()
        if self.errors:
            return
        if len(self.events) == 0:
            return

        new_genome = self.parent_genome.update()
        new_genome.name = genome_name
        new_genome.notes = notes
        new_genome.save()

        old_to_new_fragment_dict = {}

	# first prepare all events for execution, then execute; this allows
	# events to convert bp coordinates to chunk objects, so we are not
	# depending on bp coordinates from before making any changes

        for event in self.events:
            if event.fragment_id not in old_to_new_fragment_dict:
                with new_genome.update_fragment_by_fragment_id(
                    event.fragment_id, new_fragment=True
                ) as f:
                    old_to_new_fragment_dict[event.fragment_id] = f
            new_fragment = old_to_new_fragment_dict[event.fragment_id]
            event.prepare(new_fragment)

        for event in self.events:
            new_fragment = old_to_new_fragment_dict[event.fragment_id]
            event.run(new_fragment)

        return new_genome

class CreLoxReaction(Reaction):

    @staticmethod
    def allowed():
        return [
            Integration(Sites.lox66, Sites.lox71, Sites.lox72, Sites.loxP),
            Integration(Sites.lox71, Sites.lox66, Sites.loxP, Sites.lox72),

            Excision(Sites.loxP, Sites.loxP, Sites.loxP),
            Excision(Sites.lox66, Sites.loxP, Sites.loxP),
            Excision(Sites.loxP, Sites.lox66, Sites.lox66),
            Excision(Sites.lox71, Sites.loxP, Sites.lox71),
            Excision(Sites.loxP, Sites.lox71, Sites.loxP),

            Inversion(Sites.loxP, rc(Sites.loxP), Sites.loxP, rc(Sites.loxP)),
            Inversion(Sites.lox66, rc(Sites.loxP), Sites.loxP, rc(Sites.lox66)),
            Inversion(Sites.loxP, rc(Sites.lox66), Sites.lox66, rc(Sites.loxP)),
            Inversion(Sites.lox71, rc(Sites.loxP), Sites.lox71, rc(Sites.loxP)),
            Inversion(Sites.loxP, rc(Sites.lox71), Sites.loxP, rc(Sites.lox71)),

            RMCE(Sites.loxP, Sites.lox2272, Sites.loxP, Sites.lox2272, Sites.loxP, Sites.lox2272),
            RMCE(Sites.lox66, Sites.lox2272, Sites.lox71, Sites.lox2272, Sites.lox72, Sites.lox2272),
        ]
<|endoftext|>