<commit_before>#!/usr/bin/env python
# -*- coding: utf-8 -*-

# AsynCode Framework - XML framework allowing developing internet
# applications without using programming languages.
# Copyright (C) 2008-2010  Adrian Kalbarczyk, Marcin Radecki

# This program is free software: you can redistribute it and/or modify
# it under the terms of the version 3 of GNU General Public License as published by
# the Free Software Foundation.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

#@marcin: getView, checkRefresh

from ACF.utils.xmlextras import *
from ACF.core.view import View
from ACF.errors import *
from ACF import components,serializers
from ACF.components import *
from ACF.globals import MIMEmapper
import time,os

try:
	import simplejson
except:
	pass

D=False

class Application(object):
	appName=""
	debug=None
	prefix="ACF_"
	storage=None
	appDir=""
	lang="en"
	immutable=False
	def __init__(self,appDir):
		#cache for component objects
		self.immutable=False
		self.COMPONENTS_CACHE={}
		self.DEFAULT_DB=None
		self.views={}
		self.lang="en"
		self.langs=[]
		#if D: log.debug("Creating instance with appDir=%s",appDir)
		try:
			self.configPath = os.path.join(appDir, "config.xml")
			self.timestamp=os.stat(self.configPath).st_mtime
			config=xml2tree(self.configPath)
		except IOError:
			#if D: log.critical("Application config not found!")
			raise Exception("Application config not found at %s!"%(appDir+"\config.xml"))
		self.appDir=appDir
		self.config=config
		try:
			self.appName=config.get("/name/text()")[0]
		except AttributeError,e:
			#if D: log.critical("Application name not set in configuration file!")
			raise Exception("Application name not set in configuration file!")
		#for optimization we get some data from config and add it as object properties
		self.computeLangs()
		self.domain="".join(config.get("/domain/text()") or ["localhost"])
		debug=config.get("/debug")
		if debug:
			self.debug={
				"enabled":tpath(debug,"/enable/*[0]") or False,
				"level":tpath(debug,"/level/*[0]") or "error",
				"dbtimer":0,
				"dbcounter":0
			}
		self.output={
			"engine":config.get("/output[@engine]") or None,
			"xsltfile":config.get("/output[@xsltfile]") or None,
			"format":config.get("/output[@format]") or "objectml"
		}
		#engineConf=config.get("/engine")[0]
		#self.engine=te.get(engineConf[1]["name"]).engine(engineConf)
		self.prefix=(config.get("/prefix") or "ACF")+"_"
		for component in config.get("/component"):
			#if D: log.debug("setting default configuration to %s component",component[1]["name"])
			self.getComponent(component[1]["name"],component[2])
		#do it or not?
		#self.config=None
		self.immutable=True

	# checks if an application instance chould be reloaded
	def checkRefresh(self):
		if self.timestamp < os.stat(self.configPath).st_mtime:
			return True
		return False

	def getDBConn(self):
		if not self.DEFAULT_DB:
			self.DEFAULT_DB=self.getComponent("database").CONNECTIONS["default"]
		return self.DEFAULT_DB

	def getComponent(self,name,conf=None):
		#print "getComponent"
		#print name
		if self.COMPONENTS_CACHE.has_key(name):
			#print "cache"
			return self.COMPONENTS_CACHE[name]
		#print "Component not in cache"
		#print components.get(name)
		o=components.get(name).getObject(conf)
		#print o
		#print "end"
		self.COMPONENTS_CACHE[name]=o
		return o

	#lazy view objects creation
	def getView(self,acenv):

		# TODO: cache
		#print "view not in cache"
		path=acenv.URLpath
		lenpath=len(path)
		if lenpath==0:
			acenv.viewPath = ["default"]
		else:
			viewsPath = os.path.join(self.appDir, "views")
			tempPath = viewsPath
			for i in range(0, len(path)):
				s = path[i]
				tempPath = os.path.join(tempPath, s)
				# first, check if exists view name s
				if os.path.exists(tempPath + '.xml'):
					# there is a view of that name
					acenv.viewPath, acenv.inputs = path[:i+1], path[i+1:]
					break
				# if not, check if it is a directory
				elif os.path.isdir(tempPath):
					viewsPath = tempPath
					if i == lenpath - 1:
						# the last loop
						# at that moment, we passed through directory tree and
						# havn't found view file. So we assume, its default view in
						# that tree
						if os.path.exists(os.path.join(viewsPath, 'default.xml')):
							path.append('default')
							acenv.viewPath, acenv.inputs = path, []
						else:
							acenv.viewPath, acenv.inputs = ['notFound'], []
				else:
					# there is no directory neither view of that name, search for default view
					if os.path.exists(os.path.join(viewsPath, 'default.xml')):
						v = path[:i]
						v.append('default')
						acenv.viewPath, acenv.inputs =  v, path[i:]
					else:
						acenv.viewPath, acenv.inputs =  ['notFound'], []
					break
		try:
			v=View(acenv.viewPath,self)
		except ViewNotFound, e:
			if path!=["notFound"]:
				v = View(["notFound"], self)
			else:
					raise e
		#self.views[path[0]]=v
		return v

	#will be generator
	def generate(self,acenv):
		if D: t=time.time()
		prefix=acenv.prefix+"SESS"
		if acenv.cookies.has_key(prefix):
			log.info("Session cookie found")
			sessID=acenv.cookies[prefix]
			try:
				acenv.sessionStorage=FileSession(sessID)
			except:
				sessID=None
		view=self.getView(acenv)
		view.generate(acenv)
		#this is little faster than Object
		acenv.generations["lang"]=("object",{"name":"acf:lang","current":acenv.lang,"supported":",".join(acenv.langs)},None)
		acenv.generations["domain"]=("object",{"name":"acf:appDetails","domain":acenv.domain},None)
		#temporary error handling
		try:
			s=serializers.get(globals.MIMEmapper.get(acenv.outputFormat))
		except Error, e:
			acenv.outputFormat="text/html"
			return ""+str(e)+"</body"
		if D:
			all=round((time.time()-t)*1000,5)
			dbms=round(acenv.debug["dbtimer"]*1000,5)
			print("Generated in %s"%(all))
			print("DBMS took %s"%(dbms))
			print("Python took %s"%(all-dbms))
		return s.serialize(acenv)

	def transform(self,acenv):
		self.getXML(acenv)

	def computeLangs(self):
		attrs=self.config.get("/lang")[0][1]
		try:
			defaultLang=attrs["default"].strip()
		except KeyError:
			#make it log.warning and fall back to en
			raise Exception("Misconfiguration of 'langs' setting in app configuration file.")
		#["","aaa"," dd   "]->["aaa","ddd"]
		self.langs=filter(len, map(str.strip, [defaultLang]+attrs.get("supported", "").split(",")))
		self.lang=self.langs[0]

	def __str__(self):
		return str(self.__dict__)

	def __setattr__(self, name, val):
		if self.immutable and not name=="DEFAULT_DB":
			if D: log.error("%s is read only",name)
			raise Exception("PropertyImmutable")
		self.__dict__[name]=val
<commit_msg>Reorganized code on view directories. Added view cache.<commit_after>#!/usr/bin/env python
# -*- coding: utf-8 -*-

# AsynCode Framework - XML framework allowing developing internet
# applications without using programming languages.
# Copyright (C) 2008-2010  Adrian Kalbarczyk, Marcin Radecki

# This program is free software: you can redistribute it and/or modify
# it under the terms of the version 3 of GNU General Public License as published by
# the Free Software Foundation.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

#@marcin: getView, checkRefresh, cacheView, findCachedView, findViewFile

from ACF.utils.xmlextras import *
from ACF.core.view import View
from ACF.errors import *
from ACF import components,serializers
from ACF.components import *
from ACF.globals import MIMEmapper
import time,os

try:
	import simplejson
except:
	pass

D=False

class Application(object):
	appName=""
	debug=None
	prefix="ACF_"
	storage=None
	appDir=""
	lang="en"
	immutable=False
	def __init__(self,appDir):
		#cache for component objects
		self.immutable=False
		self.COMPONENTS_CACHE={}
		self.DEFAULT_DB=None
		self.views={}
		self.lang="en"
		self.langs=[]
		#if D: log.debug("Creating instance with appDir=%s",appDir)
		try:
			self.configPath = os.path.join(appDir, "config.xml")
			self.timestamp=os.stat(self.configPath).st_mtime
			config=xml2tree(self.configPath)
		except IOError:
			#if D: log.critical("Application config not found!")
			raise Exception("Application config not found at %s!"%(appDir+"\config.xml"))
		self.appDir=appDir
		self.config=config
		try:
			self.appName=config.get("/name/text()")[0]
		except AttributeError,e:
			#if D: log.critical("Application name not set in configuration file!")
			raise Exception("Application name not set in configuration file!")
		#for optimization we get some data from config and add it as object properties
		self.computeLangs()
		self.domain="".join(config.get("/domain/text()") or ["localhost"])
		debug=config.get("/debug")
		if debug:
			self.debug={
				"enabled":tpath(debug,"/enable/*[0]") or False,
				"level":tpath(debug,"/level/*[0]") or "error",
				"dbtimer":0,
				"dbcounter":0
			}
		self.output={
			"engine":config.get("/output[@engine]") or None,
			"xsltfile":config.get("/output[@xsltfile]") or None,
			"format":config.get("/output[@format]") or "objectml"
		}
		#engineConf=config.get("/engine")[0]
		#self.engine=te.get(engineConf[1]["name"]).engine(engineConf)
		self.prefix=(config.get("/prefix") or "ACF")+"_"
		for component in config.get("/component"):
			#if D: log.debug("setting default configuration to %s component",component[1]["name"])
			self.getComponent(component[1]["name"],component[2])
		#do it or not?
		#self.config=None
		self.immutable=True

	# checks if an application instance should be reloaded
	def checkRefresh(self):
		if self.timestamp < os.stat(self.configPath).st_mtime:
			return True
		return False

	def getDBConn(self):
		if not self.DEFAULT_DB:
			self.DEFAULT_DB=self.getComponent("database").CONNECTIONS["default"]
		return self.DEFAULT_DB

	def getComponent(self,name,conf=None):
		#print "getComponent"
		#print name
		if self.COMPONENTS_CACHE.has_key(name):
			#print "cache"
			return self.COMPONENTS_CACHE[name]
		#print "Component not in cache"
		#print components.get(name)
		o=components.get(name).getObject(conf)
		#print o
		#print "end"
		self.COMPONENTS_CACHE[name]=o
		return o

	#caches a view
	def cacheView(self, path, view):
		d = self.views
		for s in path[:-1]:  # without last element
			if not d.has_key(s):
				d[s] = {}
			d = d[s]
		d[path[-1]] = view # path[-1] means last element of the list

	# finds view in cache by url; return View or None, if not found
	def findCachedView(self, URLpath):
		tempPath = []
		d = self.views
		v = None
		for s in URLpath[:]:
			tempPath.append(URLpath.pop(0))
			if d.has_key(s):
				if type(d[s]) != dict:
					v = d[s]
					break
				d = d[s]
				if not URLpath: # last loop
					if d.has_key('default'):
						tempPath.append('default')
						v = d['default']
			else:
				break
		if v:
			timestamp = os.stat(v.path).st_mtime
			if timestamp <= v.timestamp:
				return (v, tempPath, URLpath)
		return None

	# finds view file stored on hdd. Seperate view path from inputs.
	# Algorithm is greedy, so it finds first view which suits url.
	def findViewFile(self, URLpath):
		viewsPath = os.path.join(self.appDir, "views")
		tempPath = []
		for s in URLpath[:]:
			viewsPath = os.path.join(viewsPath, s)
			tempPath.append(URLpath.pop(0))
			if os.path.exists(viewsPath + '.xml'):
				break
			elif os.path.isdir(viewsPath):
				if not URLpath: # last loop
					if os.path.exists(os.path.join(viewsPath, 'default.xml')):
						tempPath.append('default')
					else:
						tempPath = ['notFound']
			else:
				tempPath, URLpath = ['notFound'], []
				break
		return (tempPath, URLpath)

	#lazy view objects creation
	def getView(self,acenv):
		path = acenv.URLpath
		if not path:
			path = ['default']
		t = self.findCachedView(path[:]) # we have to work on a copy, because of next call with path in arguments
		if t:
			(v, acenv.viewPath, acenv.inputs) = t
		else:
			(acenv.viewPath, acenv.inputs) = self.findViewFile(path)
			if acenv.viewPath == ['notFound']:
				t = self.findCachedView(['notFound'])
				if t:
					return t[0]
			#print 'View not in cache'
			v = View(acenv.viewPath, self)
			self.cacheView(acenv.viewPath, v)
		return v

	#will be generator
	def generate(self,acenv):
		if D: t=time.time()
		prefix=acenv.prefix+"SESS"
		if acenv.cookies.has_key(prefix):
			log.info("Session cookie found")
			sessID=acenv.cookies[prefix]
			try:
				acenv.sessionStorage=FileSession(sessID)
			except:
				sessID=None
		view=self.getView(acenv)
		view.generate(acenv)
		#this is little faster than Object
		acenv.generations["lang"]=("object",{"name":"acf:lang","current":acenv.lang,"supported":",".join(acenv.langs)},None)
		acenv.generations["domain"]=("object",{"name":"acf:appDetails","domain":acenv.domain},None)
		#temporary error handling
		try:
			s=serializers.get(globals.MIMEmapper.get(acenv.outputFormat))
		except Error, e:
			acenv.outputFormat="text/html"
			return ""+str(e)+"</body"
		if D:
			all=round((time.time()-t)*1000,5)
			dbms=round(acenv.debug["dbtimer"]*1000,5)
			print("Generated in %s"%(all))
			print("DBMS took %s"%(dbms))
			print("Python took %s"%(all-dbms))
		return s.serialize(acenv)

	def transform(self,acenv):
		self.getXML(acenv)

	def computeLangs(self):
		attrs=self.config.get("/lang")[0][1]
		try:
			defaultLang=attrs["default"].strip()
		except KeyError:
			#make it log.warning and fall back to en
			raise Exception("Misconfiguration of 'langs' setting in app configuration file.")
		#["","aaa"," dd   "]->["aaa","ddd"]
		self.langs=filter(len, map(str.strip, [defaultLang]+attrs.get("supported", "").split(",")))
		self.lang=self.langs[0]

	def __str__(self):
		return str(self.__dict__)

	def __setattr__(self, name, val):
		if self.immutable and not name=="DEFAULT_DB":
			if D: log.error("%s is read only",name)
			raise Exception("PropertyImmutable")
		self.__dict__[name]=val
<|endoftext|>


<commit_before># Copyright (C) 2007 Alexandre Conrad, aconrad(dot.)tlv(at@)magic(dot.)fr
#
# This module is part of FormAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

import logging
logger = logging.getLogger('formalchemy.' + __name__)

from sqlalchemy.orm.attributes import InstrumentedAttribute
import webhelpers as h
import base, fields, utils

#__all__ = ["FieldSet", "MultiFields", "Field"]
__all__ = ["FieldSet", "Field"]

class FieldSet(base.BaseModelRender):
    """The `FieldSet` class.

    This class is responsible for generating HTML fields from a given
    `model`.

    The one method to use is `render`. This is the method that returns
    generated HTML code from the `model` object.

    FormAlchemy has some default behaviour set. It is configured to generate
    the most HTML possible that will reflect the `model` object. Although,
    you can configure FormAlchemy to behave differently, thus altering the
    generated HTML output by many ways:

      * By passing keyword options to the `render` method:

        render(pk=False, fk=False, exclude=["private_column"])

      These options are NOT persistant. You'll need to pass these options
      everytime you call `render` or FormAlchemy will fallback to its
      default behaviour. Passing keyword options is usually meant to alter
      the HTML output on the fly.

      * At the SQLAlchemy mapped class level, by creating a `FormAlchemy`
      subclass, it is possible to setup attributes which names and values
      correspond to the keyword options passed to `render`:

        class MyClass(object):
            class FormAlchemy:
                pk = False
                fk = False
                exclude = ["private_column"]

      These attributes are persistant and will be used as FormAlchemy's
      default behaviour.

      * By passing the keyword options to FormAlchemy's `configure` method.
      These options are persistant. and will be used as FormAlchemy's default
      behaviour.

        configure(pk=False, fk=False, exclude=["private_column"])

    Note: In any case, options set at the SQLAlchemy mapped class level or
    via the `configure` method will be overridden if these same keyword
    options are passed to `render`.

    """

    def __init__(self, *args, **kwargs):
        super(FieldSet, self).__init__(*args, **kwargs)
        self.__dict__.update([(attrname, fields.AttributeWrapper((attr, self.model, self.session)))
                              for attrname, attr in self.model.__class__.__dict__.iteritems()
                              if isinstance(attr, InstrumentedAttribute)])

    def render(self, **options):
        # Merge class level options with given options.
        opts = self.new_options(**options)
        logger.debug(opts)

        # Filter out unnecessary columns.
        attrs = self.get_attrs(**opts)

        # Should we keep track of rendered columns ?
        track_cols = opts.get('track_cols', False)

        html = []
        # Generate fields.
        field_render = Field(self.model, self.session)
        field_render.reconfigure(**opts)
        for attr in attrs:
            field_render.set_attr(attr)
            field = field_render.render()
            html.append(field)

        return "\n".join(html)

class Field(base.BaseColumnRender):
    """The `Field` class.

    Return generated HTML  and <input> tags for one single column.

    """

    def __init__(self, model, session=None, attr=None, make_label=True):
        super(Field, self).__init__(model, session, attr=attr)

        self.set_make_label(make_label)
        self._focus_rendered = False

    def set_make_label(self, value):
        self._make_label = bool(value)

    def get_make_label(self):
        return self._make_label

    def render(self, **options):
        # Merge class level options with given options.
        opts = self.new_options(**options)

        # DateTime, Date, Time string formaters
        date_f = opts.get('date', "%Y-%m-%d")
        time_f = opts.get('time', "%H:%M:%S")
        datetime_f = opts.get('datetime', "%s %s" % (date_f, time_f))

        make_label = opts.get('make_label', self.get_make_label())

        pretty_func = opts.get('prettify')
        alias = opts.get('alias', {})
        focus = opts.get('focus', True)

        errors = opts.get('error', {})
        docs = opts.get('doc', {})

        # Setup HTML classes
        cls_fld_req = opts.get('cls_req', 'field_req')
        cls_fld_opt = opts.get('cls_opt', 'field_opt')
        cls_fld_err = opts.get('cls_err', 'field_err')

        cls_span_doc = opts.get('span_doc', 'span_doc')
        cls_span_err = opts.get('span_err', 'span_err')

        # Process hidden fields first as they don't need a `Label`.
        if self.wrapper.render_as == fields.HiddenField:
            return self.wrapper.render()

        # Make the label
        field = ""

        if make_label:
            label = fields.Label(self.wrapper.column, alias=alias.get(self.wrapper.name, self.wrapper.name))
            label.set_prettify(pretty_func)
            if self.wrapper.nullable:
                label.cls = cls_fld_opt
            else:
                label.cls = cls_fld_req
            field += label.render()

        # Make the input

        field += '\n' + self.wrapper.render()

        # Make the error
        if self.wrapper.column in errors:
            field += "\n" + h.content_tag("span", errors[self.wrapper.column], class_=cls_span_err)
        # Make the documentation
        if self.wrapper.column in docs:
            field += "\n" + h.content_tag("span", docs[self.wrapper.column], class_=cls_span_doc)

        if field.startswith("\n"):
            field = field[1:]

        # Wrap the whole thing into a div
        if self.get_make_label():
            field = utils.wrap("
", field, "
")

        # Do the field focusing
        if (focus == self.wrapper.column or focus is True) and not self._focus_rendered:
            field += "\n" + h.javascript_tag('document.getElementById("%s").focus();' % self.wrapper.name)
            self._focus_rendered = True

        return field
<commit_msg>r/m no-op track_cols<commit_after># Copyright (C) 2007 Alexandre Conrad, aconrad(dot.)tlv(at@)magic(dot.)fr
#
# This module is part of FormAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php

import logging
logger = logging.getLogger('formalchemy.' + __name__)

from sqlalchemy.orm.attributes import InstrumentedAttribute
import webhelpers as h
import base, fields, utils

#__all__ = ["FieldSet", "MultiFields", "Field"]
__all__ = ["FieldSet", "Field"]

class FieldSet(base.BaseModelRender):
    """The `FieldSet` class.

    This class is responsible for generating HTML fields from a given
    `model`.

    The one method to use is `render`. This is the method that returns
    generated HTML code from the `model` object.

    FormAlchemy has some default behaviour set. It is configured to generate
    the most HTML possible that will reflect the `model` object. Although,
    you can configure FormAlchemy to behave differently, thus altering the
    generated HTML output by many ways:

      * By passing keyword options to the `render` method:

        render(pk=False, fk=False, exclude=["private_column"])

      These options are NOT persistant. You'll need to pass these options
      everytime you call `render` or FormAlchemy will fallback to its
      default behaviour. Passing keyword options is usually meant to alter
      the HTML output on the fly.

      * At the SQLAlchemy mapped class level, by creating a `FormAlchemy`
      subclass, it is possible to setup attributes which names and values
      correspond to the keyword options passed to `render`:

        class MyClass(object):
            class FormAlchemy:
                pk = False
                fk = False
                exclude = ["private_column"]

      These attributes are persistant and will be used as FormAlchemy's
      default behaviour.

      * By passing the keyword options to FormAlchemy's `configure` method.
      These options are persistant. and will be used as FormAlchemy's default
      behaviour.

        configure(pk=False, fk=False, exclude=["private_column"])

    Note: In any case, options set at the SQLAlchemy mapped class level or
    via the `configure` method will be overridden if these same keyword
    options are passed to `render`.

    """

    def __init__(self, *args, **kwargs):
        super(FieldSet, self).__init__(*args, **kwargs)
        self.__dict__.update([(attrname, fields.AttributeWrapper((attr, self.model, self.session)))
                              for attrname, attr in self.model.__class__.__dict__.iteritems()
                              if isinstance(attr, InstrumentedAttribute)])

    def render(self, **options):
        # Merge class level options with given options.
        opts = self.new_options(**options)
        logger.debug(opts)

        # Filter out unnecessary columns.
        attrs = self.get_attrs(**opts)

        html = []
        # Generate fields.
        field_render = Field(self.model, self.session)
        field_render.reconfigure(**opts)
        for attr in attrs:
            field_render.set_attr(attr)
            field = field_render.render()
            html.append(field)

        return "\n".join(html)

class Field(base.BaseColumnRender):
    """The `Field` class.

    Return generated HTML  and <input> tags for one single column.

    """

    def __init__(self, model, session=None, attr=None, make_label=True):
        super(Field, self).__init__(model, session, attr=attr)

        self.set_make_label(make_label)
        self._focus_rendered = False

    def set_make_label(self, value):
        self._make_label = bool(value)

    def get_make_label(self):
        return self._make_label

    def render(self, **options):
        # Merge class level options with given options.
        opts = self.new_options(**options)

        # DateTime, Date, Time string formaters
        date_f = opts.get('date', "%Y-%m-%d")
        time_f = opts.get('time', "%H:%M:%S")
        datetime_f = opts.get('datetime', "%s %s" % (date_f, time_f))

        make_label = opts.get('make_label', self.get_make_label())

        pretty_func = opts.get('prettify')
        alias = opts.get('alias', {})
        focus = opts.get('focus', True)

        errors = opts.get('error', {})
        docs = opts.get('doc', {})

        # Setup HTML classes
        cls_fld_req = opts.get('cls_req', 'field_req')
        cls_fld_opt = opts.get('cls_opt', 'field_opt')
        cls_fld_err = opts.get('cls_err', 'field_err')

        cls_span_doc = opts.get('span_doc', 'span_doc')
        cls_span_err = opts.get('span_err', 'span_err')

        # Process hidden fields first as they don't need a `Label`.
        if self.wrapper.render_as == fields.HiddenField:
            return self.wrapper.render()

        # Make the label
        field = ""

        if make_label:
            label = fields.Label(self.wrapper.column, alias=alias.get(self.wrapper.name, self.wrapper.name))
            label.set_prettify(pretty_func)
            if self.wrapper.nullable:
                label.cls = cls_fld_opt
            else:
                label.cls = cls_fld_req
            field += label.render()

        # Make the input

        field += '\n' + self.wrapper.render()

        # Make the error
        if self.wrapper.column in errors:
            field += "\n" + h.content_tag("span", errors[self.wrapper.column], class_=cls_span_err)
        # Make the documentation
        if self.wrapper.column in docs:
            field += "\n" + h.content_tag("span", docs[self.wrapper.column], class_=cls_span_doc)

        if field.startswith("\n"):
            field = field[1:]

        # Wrap the whole thing into a div
        if self.get_make_label():
            field = utils.wrap("
", field, "
")

        # Do the field focusing
        if (focus == self.wrapper.column or focus is True) and not self._focus_rendered:
            field += "\n" + h.javascript_tag('document.getElementById("%s").focus();' % self.wrapper.name)
            self._focus_rendered = True

        return field
<|endoftext|>


<commit_before>#!/usr/bin/env python3
"""
    Client library generator for SciGraph REST api.
"""

import requests

class State:
    def __init__(self, api_url):
        self.shebang = "#!/usr/bin/env python3\n"
        self.imports = "import builtins\nimport requests\nfrom json import dumps\n\n"
        self.api_url = api_url
        self.current_path = self.api_url
        self.exten_mapping = {}
        self.paths = {}
        self.globs = {}
        self.tab = '    '
        self.gencode()

    @property
    def code(self):
        return self.make_main()

    def make_main(self):
        code = ""
        code += self.shebang
        code += self.make_doc()
        code += self.imports
        code += "exten_mapping = {%s}\n\n" % ', '.join(["'" + '\': \''.join(_) + "'" for _ in sorted(self.exten_mapping.items())])
        code += self.make_baseclass()
        code += self._code
        code += '\n'

        return code

    def make_doc(self):
        code = ('""" WARNING: DO NOT MODIFY THIS FILE\n'
                '{t}IT IS AUTOMATICALLY GENERATED BY scigraph.py\n'
                '{t}AND WILL BE OVERWRITTEN\n'
                '{t}Swagger Version: {swaggerVersion}, API Version: {apiVersion}\n'
                '{t}generated for {api_url}\n{t}by scigraph.py\n"""\n')
        swaggerVersion = self.globs['swaggerVersion']
        apiVersion = self.globs['apiVersion']
        return code.format(swaggerVersion=swaggerVersion, apiVersion=apiVersion, api_url=self.api_url, t=self.tab)

    def make_baseclass(self):
        # useful print(inspect.getsource(restService).replace(' '*4,'{t}').replace("'","\\'").replace('\n',"\\n'\n'"))
        code = (
            'class restService:\n'
            '{t}""" Base class for SciGraph rest services. """\n\n'
            '{t}def __init__(self, cache=False):\n'
            '{t}{t}self._session = requests.Session()\n'
            '{t}{t}adapter = requests.adapters.HTTPAdapter(pool_connections=1000, pool_maxsize=1000)\n'
            '{t}{t}self._session.mount(\'http://\', adapter)\n\n'
            '{t}{t}if cache:\n'
            '{t}{t}{t}self._cache = dict()\n'
            '{t}{t}{t}self._get = self._cache_get\n'
            '{t}{t}else:\n'
            '{t}{t}{t}self._get = self._normal_get\n'
            '\n'
            '{t}def _normal_get(self, method, url, params=None, output=None):\n'
            '{t}{t}s = self._session\n'
            '{t}{t}if method == \'POST\':\n'
            '{t}{t}{t}req = requests.Request(method=method, url=url, data=params)\n'
            '{t}{t}else:\n'
            '{t}{t}{t}req = requests.Request(method=method, url=url, params=params)\n'
            '{t}{t}if output:\n'
            '{t}{t}{t}req.headers[\'Accept\'] = output\n'
            '{t}{t}prep = req.prepare()\n'
            '{t}{t}if self._verbose: print(prep.url)\n'
            '{t}{t}resp = s.send(prep)\n'
            '{t}{t}if not resp.ok:\n'
            '{t}{t}{t}return None\n'
            '{t}{t}elif resp.headers[\'content-type\'] == \'application/json\':\n'
            '{t}{t}{t}return resp.json()\n'
            '{t}{t}elif resp.headers[\'content-type\'].startswith(\'text/plain\'):\n'
            '{t}{t}{t}return resp.text\n'
            '{t}{t}else:\n'
            '{t}{t}{t}return resp\n'
            '\n'
            '{t}def _cache_get(self, method, url, params=None, output=None):\n'
            '{t}{t}if params:\n'
            '{t}{t}{t}pkey = \'?\' + \'&\'.join([\'%s=%s\' % (k,v) for k,v in sorted(params.items()) if v is not None])\n'
            '{t}{t}else:\n'
            '{t}{t}{t}pkey = \'\'\n'
            '{t}{t}key = url + pkey + \' \' + method + \' \' + str(output)\n'
            '{t}{t}if  key in self._cache:\n'
            '{t}{t}{t}if self._verbose:\n'
            '{t}{t}{t}{t}print(\'cache hit\', key)\n'
            '{t}{t}{t}return self._cache[key]\n'
            '{t}{t}else:\n'
            '{t}{t}{t}resp = self._normal_get(method, url, params, output)\n'
            '{t}{t}{t}self._cache[key] = resp\n'
            '{t}{t}{t}return resp\n'
            '\n'
            '{t}def _make_rest(self, default=None, **kwargs):\n'
            '{t}{t}kwargs = {dict_comp}\n'
            '{t}{t}param_rest = \'&\'.join([\'%s={STUPID}\' % (arg, arg) for arg in kwargs if arg != default])\n'
            '{t}{t}param_rest = param_rest if param_rest else \'\'\n'
            '{t}{t}return param_rest\n'
            '\n'

        )
        dict_comp = '{k:v for k, v in kwargs.items() if v}'
        STUPID = '{%s}'
        return code.format(t=self.tab, dict_comp=dict_comp, STUPID=STUPID)

    def make_class(self, dict_):
        code = (
            '\n'
            'class {classname}(restService):\n'
            '{t}""" {docstring} """\n\n'
            '{t}def __init__(self, basePath=\'{basePath}\', verbose=False, cache=False):\n'
            '{t}{t}self._basePath = basePath\n'
            '{t}{t}self._verbose = verbose\n'
            '{t}{t}super().__init__(cache)\n\n'
        )
        classname = dict_['resourcePath'].strip('/').capitalize()
        docstring = dict_['docstring']
        _, basePath = self.basePath_(dict_['basePath'])
        return code.format(classname=classname, docstring=docstring, basePath=basePath, t=self.tab)

    def make_param_parts(self, dict_):
        if dict_['required']:
            param_args = '{name}'
            param_args = param_args.format(name=dict_['name'])
            required = param_args
        else:
            param_args = "{name}={defaultValue}"
            dv = dict_.get('defaultValue', None)
            if dv:
                try:
                    dv = int(dv)
                except ValueError:
                    if dv == 'true':
                        dv = 'True'
                    elif dv == 'false':
                        dv = 'False'
                    else:
                        dv = "'%s'" % dv
            param_args = param_args.format(name=dict_['name'], defaultValue=dv)
            required = None

        param_rest = '{name}'
        param_rest = param_rest.format(name=dict_['name'])

        param_doc = '{t}{t}{t}{name}: {description}'

        desc = dict_.get('description','')
        if len(desc) > 60:
            tmp = desc.split(' ')
            part = len(desc) // 60
            size = len(tmp) // part
            lines = []
            for i in range(part + 1):
                lines.append(' '.join(tmp[i*size:(i+1) * size]))
            desc = '\n{t}{t}{t}'.format(t=self.tab).join([l for l in lines if l])
        param_doc = param_doc.format(name=dict_['name'], description=desc, t=self.tab)

        return param_args, param_rest, param_doc, required

    def make_params(self, list_):
        pargs_list, prests, pdocs = [], [], []
        required = None
        for param in list_:
            parg, prest, pdoc, put_required = self.make_param_parts(param)
            if put_required:
                required = "'%s'" % put_required  # XXX fail for multi required?
            pargs_list.append(parg)
            prests.append(prest)
            pdocs.append(pdoc)

        if pargs_list:
            pargs = ', ' + ', '.join(pargs_list)
        else:
            pargs = ''

        if prests:
            prests = '{' + ', '.join(["'%s':%s"%(pr, pr) for pr in prests]) + '}'
        else:
            prests = '{}'

        pdocs = '\n'.join(pdocs)
        return pargs, prests, pdocs, required

    def make_return(self, api_dict):
        if 'type' in api_dict:
            return_type = api_dict['type']  # array or other (Graph, etc)
            print(return_type)
        else:
            return_type = None
            print('NO TYPE')

        type_return_dict = {
            'array': '[]',
            'string': None,
            'Annotations': '[]',  # bug in docs
            'Graph': "{'nodes':[], 'edges':[]}",  # risky
            'ConceptDTO': None,  # better None than empty dict
            'RefineResult': None,  # TODO
            'AnalyzerResult' :None,  # TODO
            None:None,
        }

        return type_return_dict[return_type]

    def apiVersion(self, value):
        self.globs['apiVersion'] = value
        return None, ''

    def swaggerVersion(self, value):
        self.globs['swaggerVersion'] = value
        return None, ''

    def operation(self, api_dict):
        code = (
            '{t}def {nickname}(self{params}{default_output}):\n'
            '{t}{t}""" {docstring}\n{t}{t}"""\n\n'
            '{params_conditional}'
            '{t}{t}kwargs = {param_rest}\n'
            '{t}{t}kwargs = {dict_comp}\n'
            '{t}{t}param_rest = self._make_rest({required}, **kwargs)\n'
            '{t}{t}url = self._basePath + (\'{path}\').format(**kwargs)\n'
            '{t}{t}requests_params = {dict_comp2}\n'
            '{t}{t}output = self._get(\'{method}\', url, requests_params{output})\n'
            '{t}{t}return output if output else {empty_return_type}\n'
        )

        dict_comp = '{k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}'  # json needs " not '
        params, param_rest, param_docs, required = self.make_params(api_dict['parameters'])
        empty_return_type = self.make_return(api_dict)
        nickname = api_dict['nickname']
        path = self.paths[nickname]
        docstring = api_dict['summary'] + ' from: ' + path + '\n\n{t}{t}{t}Arguments:\n'.format(t=self.tab) + param_docs
        # handle whether required is in the url
        if required:
            if '{' + required.strip("'") + '}' not in path:
                required = None
        if required:
            dict_comp2 = '{k:v for k, v in kwargs.items() if k != %s}' % required
        else:
            dict_comp2 = 'kwargs'

        params_conditional = ''
        for cond in 'id', 'url', 'type':
            if cond in param_rest:
                params_conditional += (
                    "{t}{t}if {cond} and {cond}.startswith('http:'):\n"
                    "{t}{t}{t}{cond} = {cond}.replace('/','%2F').replace('#','%23')\n").format(cond=cond, t=self.tab)

        if 'produces' in api_dict:  # ICK but the alt is nastier
            outputs, default_output = self.make_produces(api_dict['produces'])
            docstring += outputs
            output = ', output'
        else:
            default_output = ''
            output = ''

        method = api_dict['method']

        formatted = code.format(path=path, nickname=nickname, params=params, param_rest=param_rest,
                            dict_comp=dict_comp, dict_comp2=dict_comp2, method=method,
                            docstring=docstring, required=required, default_output=default_output,
                            params_conditional=params_conditional, output=output, t=self.tab,
                            empty_return_type=empty_return_type)
        self.dodict(api_dict)  # catch any stateful things we need, but we arent generating code from it
        return formatted

    def description(self, value):
        return None, ''

    def resourcePath(self, value):
        return None, ''

    def top_path(self, extension):
        newpath = self.api_url + extension
        json = requests.get(newpath).json()
        return json

    def path(self, value):
        # if anything do substitution here
        # need something extra here?
        return None, ''

    def apis(self, list_):
        try:
            for api in list_:
                if 'operations' in api:
                    for operation in api['operations']:
                        self.paths[operation['nickname']] = api['path']
        except:
            raise BaseException
        return None, self.dolist(list_)

    def models(self, dict_):
        return None, self.dodict(dict_)

    def Features(self, dict_):
        self.dodict(dict_)
        return None, ''

    def Graph(self, dict_):
        self.dodict(dict_)
        return None, ''

    def properties(self, dict_):
        return None, self.dodict(dict_)

    def operations(self, list_):
        self.context = 'operations'
        code = '\n'.join(self.operation(l) for l in list_)
        return None, code

    def produces(self, list_):
        return None, ''

    def make_produces(self, list_):
        # we make return option here including the docstring
        for mimetype in list_:
            self.exten_mapping[mimetype] = mimetype.split('/')[-1]

        outputs = '\n{t}{t}{t}outputs:\n{t}{t}{t}{t}'
        outputs += '\n{t}{t}{t}{t}'.join(list_)

        default_output = ', output=\'{output}\''.format(output=list_[0])
        return outputs.format(t=self.tab), default_output   # FIXME there MUST be a better way to deal with the bloody {t} all at once

    def basePath_(self, value):
        dirs = value.split('/')
        curs = self.api_url.split('/')
        for d in dirs:
            if d == '..':
                curs = curs[:-1]
            else:
                curs.append(d)

        return None, '/'.join(curs)

    def dolist(self, list_):
        blocks = []
        for dict_ in list_:
            code = self.dodict(dict_)
            blocks.append(code)

        return '\n'.join([b for b in blocks if b])

    def dodict(self, dict_):
        blocks = []
        for key, value in dict_.items():
            #print('trying with key:', key)
            if key in self.__class__.__dict__:
                name, code = self.__class__.__dict__[key](self, value)
                blocks.append(code)
            else:
                #print('METHOD', key, 'NOT FOUND')
                pass

        return '\n'.join([b for b in blocks if b])

    def class_json(self, dict_):
        code = self.make_class(dict_)
        code += self.dodict(dict_)
        return None, code

    def dotopdict(self, dict_):
        for api in dict_['apis']:
            json = self.top_path(api['path'])
            json['docstring'] = api['description']
            api['class_json'] = json
        return dict_

    def gencode(self):
        """ Run this to generate the code """
        ledict = requests.get(self.api_url).json()
        ledict = self.dotopdict(ledict)
        out = self.dodict(ledict)
        self._code = out

def main():
    target = '/tmp/scigraph_client.py'
    s = State('http://matrix.neuinfo.org:9000/scigraph/api-docs')
    code = s.code
    with open(target, 'wt') as f:
        f.write(code)

if __name__ == '__main__':
    main()

<commit_msg>ah inspect.getsource, you make me look stupid<commit_after>#!/usr/bin/env python3
"""
    Client library generator for SciGraph REST api.
"""

import inspect
import requests

class restService:
    """ Base class for SciGraph rest services. """

    def __init__(self, cache=False):
        self._session = requests.Session()
        adapter = requests.adapters.HTTPAdapter(pool_connections=1000, pool_maxsize=1000)
        self._session.mount('http://', adapter)

        if cache:
            self._cache = dict()
            self._get = self._cache_get
        else:
            self._get = self._normal_get

    def _normal_get(self, method, url, params=None, output=None):
        s = self._session
        if method == 'POST':
            req = requests.Request(method=method, url=url, data=params)
        else:
            req = requests.Request(method=method, url=url, params=params)
        if output:
            req.headers['Accept'] = output
        prep = req.prepare()
        if self._verbose: print(prep.url)
        resp = s.send(prep)
        if not resp.ok:
            return None
        elif resp.headers['content-type'] == 'application/json':
            return resp.json()
        elif resp.headers['content-type'].startswith('text/plain'):
            return resp.text
        else:
            return resp

    def _cache_get(self, method, url, params=None, output=None):
        if params:
            pkey = '?' + '&'.join(['%s=%s' % (k,v) for k,v in sorted(params.items()) if v is not None])
        else:
            pkey = ''
        key = url + pkey + ' ' + method + ' ' + str(output)
        if  key in self._cache:
            if self._verbose:
                print('cache hit', key)
            return self._cache[key]
        else:
            resp = self._normal_get(method, url, params, output)
            self._cache[key] = resp
            return resp

    def _make_rest(self, default=None, **kwargs):
        kwargs = {k:v for k, v in kwargs.items() if v}
        param_rest = '&'.join(['%s={%s}' % (arg, arg) for arg in kwargs if arg != default])
        param_rest = param_rest if param_rest else ''
        return param_rest

class State:
    def __init__(self, api_url):
        self.shebang = "#!/usr/bin/env python3\n"
        self.imports = "import builtins\nimport requests\nfrom json import dumps\n\n"
        self.api_url = api_url
        self.current_path = self.api_url
        self.exten_mapping = {}
        self.paths = {}
        self.globs = {}
        self.tab = '    '
        self.gencode()

    @property
    def code(self):
        return self.make_main()

    def make_main(self):
        code = ""
        code += self.shebang
        code += self.make_doc()
        code += self.imports
        code += "exten_mapping = {%s}\n\n" % ', '.join(["'" + '\': \''.join(_) + "'" for _ in sorted(self.exten_mapping.items())])
        code += self.make_baseclass()
        code += self._code
        code += '\n'

        return code

    def make_doc(self):
        code = ('""" WARNING: DO NOT MODIFY THIS FILE\n'
                '{t}IT IS AUTOMATICALLY GENERATED BY scigraph.py\n'
                '{t}AND WILL BE OVERWRITTEN\n'
                '{t}Swagger Version: {swaggerVersion}, API Version: {apiVersion}\n'
                '{t}generated for {api_url}\n{t}by scigraph.py\n"""\n')
        swaggerVersion = self.globs['swaggerVersion']
        apiVersion = self.globs['apiVersion']
        return code.format(swaggerVersion=swaggerVersion, apiVersion=apiVersion, api_url=self.api_url, t=self.tab)

    def make_baseclass(self):
        return inspect.getsource(restService) + '\n'

    def make_class(self, dict_):
        code = (
            '\n'
            'class {classname}(restService):\n'
            '{t}""" {docstring} """\n\n'
            '{t}def __init__(self, basePath=\'{basePath}\', verbose=False, cache=False):\n'
            '{t}{t}self._basePath = basePath\n'
            '{t}{t}self._verbose = verbose\n'
            '{t}{t}super().__init__(cache)\n\n'
        )
        classname = dict_['resourcePath'].strip('/').capitalize()
        docstring = dict_['docstring']
        _, basePath = self.basePath_(dict_['basePath'])
        return code.format(classname=classname, docstring=docstring, basePath=basePath, t=self.tab)

    def make_param_parts(self, dict_):
        if dict_['required']:
            param_args = '{name}'
            param_args = param_args.format(name=dict_['name'])
            required = param_args
        else:
            param_args = "{name}={defaultValue}"
            dv = dict_.get('defaultValue', None)
            if dv:
                try:
                    dv = int(dv)
                except ValueError:
                    if dv == 'true':
                        dv = 'True'
                    elif dv == 'false':
                        dv = 'False'
                    else:
                        dv = "'%s'" % dv
            param_args = param_args.format(name=dict_['name'], defaultValue=dv)
            required = None

        param_rest = '{name}'
        param_rest = param_rest.format(name=dict_['name'])

        param_doc = '{t}{t}{t}{name}: {description}'

        desc = dict_.get('description','')
        if len(desc) > 60:
            tmp = desc.split(' ')
            part = len(desc) // 60
            size = len(tmp) // part
            lines = []
            for i in range(part + 1):
                lines.append(' '.join(tmp[i*size:(i+1) * size]))
            desc = '\n{t}{t}{t}'.format(t=self.tab).join([l for l in lines if l])
        param_doc = param_doc.format(name=dict_['name'], description=desc, t=self.tab)

        return param_args, param_rest, param_doc, required

    def make_params(self, list_):
        pargs_list, prests, pdocs = [], [], []
        required = None
        for param in list_:
            parg, prest, pdoc, put_required = self.make_param_parts(param)
            if put_required:
                required = "'%s'" % put_required  # XXX fail for multi required?
            pargs_list.append(parg)
            prests.append(prest)
            pdocs.append(pdoc)

        if pargs_list:
            pargs = ', ' + ', '.join(pargs_list)
        else:
            pargs = ''

        if prests:
            prests = '{' + ', '.join(["'%s':%s"%(pr, pr) for pr in prests]) + '}'
        else:
            prests = '{}'

        pdocs = '\n'.join(pdocs)
        return pargs, prests, pdocs, required

    def make_return(self, api_dict):
        if 'type' in api_dict:
            return_type = api_dict['type']  # array or other (Graph, etc)
            print(return_type)
        else:
            return_type = None
            print('NO TYPE')

        type_return_dict = {
            'array': '[]',
            'string': None,
            'Annotations': '[]',  # bug in docs
            'Graph': "{'nodes':[], 'edges':[]}",  # risky
            'ConceptDTO': None,  # better None than empty dict
            'RefineResult': None,  # TODO
            'AnalyzerResult' :None,  # TODO
            None:None,
        }

        return type_return_dict[return_type]

    def apiVersion(self, value):
        self.globs['apiVersion'] = value
        return None, ''

    def swaggerVersion(self, value):
        self.globs['swaggerVersion'] = value
        return None, ''

    def operation(self, api_dict):
        code = (
            '{t}def {nickname}(self{params}{default_output}):\n'
            '{t}{t}""" {docstring}\n{t}{t}"""\n\n'
            '{params_conditional}'
            '{t}{t}kwargs = {param_rest}\n'
            '{t}{t}kwargs = {dict_comp}\n'
            '{t}{t}param_rest = self._make_rest({required}, **kwargs)\n'
            '{t}{t}url = self._basePath + (\'{path}\').format(**kwargs)\n'
            '{t}{t}requests_params = {dict_comp2}\n'
            '{t}{t}output = self._get(\'{method}\', url, requests_params{output})\n'
            '{t}{t}return output if output else {empty_return_type}\n'
        )

        dict_comp = '{k:dumps(v) if builtins.type(v) is dict else v for k, v in kwargs.items()}'  # json needs " not '
        params, param_rest, param_docs, required = self.make_params(api_dict['parameters'])
        empty_return_type = self.make_return(api_dict)
        nickname = api_dict['nickname']
        path = self.paths[nickname]
        docstring = api_dict['summary'] + ' from: ' + path + '\n\n{t}{t}{t}Arguments:\n'.format(t=self.tab) + param_docs
        # handle whether required is in the url
        if required:
            if '{' + required.strip("'") + '}' not in path:
                required = None
        if required:
            dict_comp2 = '{k:v for k, v in kwargs.items() if k != %s}' % required
        else:
            dict_comp2 = 'kwargs'

        params_conditional = ''
        for cond in 'id', 'url', 'type':
            if cond in param_rest:
                params_conditional += (
                    "{t}{t}if {cond} and {cond}.startswith('http:'):\n"
                    "{t}{t}{t}{cond} = {cond}.replace('/','%2F').replace('#','%23')\n").format(cond=cond, t=self.tab)

        if 'produces' in api_dict:  # ICK but the alt is nastier
            outputs, default_output = self.make_produces(api_dict['produces'])
            docstring += outputs
            output = ', output'
        else:
            default_output = ''
            output = ''

        method = api_dict['method']

        formatted = code.format(path=path, nickname=nickname, params=params, param_rest=param_rest,
                            dict_comp=dict_comp, dict_comp2=dict_comp2, method=method,
                            docstring=docstring, required=required, default_output=default_output,
                            params_conditional=params_conditional, output=output, t=self.tab,
                            empty_return_type=empty_return_type)
        self.dodict(api_dict)  # catch any stateful things we need, but we arent generating code from it
        return formatted

    def description(self, value):
        return None, ''

    def resourcePath(self, value):
        return None, ''

    def top_path(self, extension):
        newpath = self.api_url + extension
        json = requests.get(newpath).json()
        return json

    def path(self, value):
        # if anything do substitution here
        # need something extra here?
        return None, ''

    def apis(self, list_):
        try:
            for api in list_:
                if 'operations' in api:
                    for operation in api['operations']:
                        self.paths[operation['nickname']] = api['path']
        except:
            raise BaseException
        return None, self.dolist(list_)

    def models(self, dict_):
        return None, self.dodict(dict_)

    def Features(self, dict_):
        self.dodict(dict_)
        return None, ''

    def Graph(self, dict_):
        self.dodict(dict_)
        return None, ''

    def properties(self, dict_):
        return None, self.dodict(dict_)

    def operations(self, list_):
        self.context = 'operations'
        code = '\n'.join(self.operation(l) for l in list_)
        return None, code

    def produces(self, list_):
        return None, ''

    def make_produces(self, list_):
        # we make return option here including the docstring
        for mimetype in list_:
            self.exten_mapping[mimetype] = mimetype.split('/')[-1]

        outputs = '\n{t}{t}{t}outputs:\n{t}{t}{t}{t}'
        outputs += '\n{t}{t}{t}{t}'.join(list_)

        default_output = ', output=\'{output}\''.format(output=list_[0])
        return outputs.format(t=self.tab), default_output   # FIXME there MUST be a better way to deal with the bloody {t} all at once

    def basePath_(self, value):
        dirs = value.split('/')
        curs = self.api_url.split('/')
        for d in dirs:
            if d == '..':
                curs = curs[:-1]
            else:
                curs.append(d)

        return None, '/'.join(curs)

    def dolist(self, list_):
        blocks = []
        for dict_ in list_:
            code = self.dodict(dict_)
            blocks.append(code)

        return '\n'.join([b for b in blocks if b])

    def dodict(self, dict_):
        blocks = []
        for key, value in dict_.items():
            #print('trying with key:', key)
            if key in self.__class__.__dict__:
                name, code = self.__class__.__dict__[key](self, value)
                blocks.append(code)
            else:
                #print('METHOD', key, 'NOT FOUND')
                pass

        return '\n'.join([b for b in blocks if b])

    def class_json(self, dict_):
        code = self.make_class(dict_)
        code += self.dodict(dict_)
        return None, code

    def dotopdict(self, dict_):
        for api in dict_['apis']:
            json = self.top_path(api['path'])
            json['docstring'] = api['description']
            api['class_json'] = json
        return dict_

    def gencode(self):
        """ Run this to generate the code """
        ledict = requests.get(self.api_url).json()
        ledict = self.dotopdict(ledict)
        out = self.dodict(ledict)
        self._code = out

def main():
    target = '/tmp/scigraph_client.py'
    s = State('http://matrix.neuinfo.org:9000/scigraph/api-docs')
    code = s.code
    with open(target, 'wt') as f:
        f.write(code)

if __name__ == '__main__':
    main()

<|endoftext|>


<commit_before>from __future__ import division
import pandas as pd
pd.set_option('display.mpl_style', 'default') # Make the graphs a bit prettier
from collections import OrderedDict
from scipy.stats import norm
Z = norm.ppf

# counts number of trials corresponding to inputted conditions
def itemCounter(df, responseType, changeType, t, d):
    count = float(len(df[responseType & changeType & t & d]))
    return count

# calculates K pased on Pashler equation: K = S * ((H - F) / (1 - F))
def kCalculation(s, h, f):
    k = s * ((h - f) / (1 - f))
    return k

# calculates pashler K for each condition from csv data file
def pashlerK(csv):
    # read in csv file
    df = pd.read_csv(csv)

    # create conditions
    resps = df['Response'] == 1
    noResps = df['Response'] == 0
    changes = df['ChangeTrial'] == 1
    noChanges = df['ChangeTrial'] == 0
    targ2 = df['nTargets'] == 2
    targ4 = df['nTargets'] == 4
    dist0 = df['nDistractors'] == 0
    dist2 = df['nDistractors'] == 2
    dist4 = df['nDistractors'] == 4

    # initialize dictionaries
    hitRates = OrderedDict()
    fAlarmRates = OrderedDict()
    Kvals = OrderedDict()

    conds = OrderedDict([('t2d0', (targ2, dist0)), ('t2d2', (targ2, dist2)), ('t2d4', (targ2, dist4)), ('t4d0', (targ4, dist0)), ('t4d2', (targ4, dist2)), ('t4d4', (targ4, dist4))])

    # store rate values
    for key in conds:
        hits = itemCounter(df, resps, changes, conds[key][0], conds[key][1])
        misses = itemCounter(df, noResps, changes, conds[key][0], conds[key][1])
        falarms = itemCounter(df, resps, noChanges, conds[key][0], conds[key][1])
        crejects = itemCounter(df, noResps, noChanges, conds[key][0], conds[key][1])
        hitRates[key] = hits/(hits+misses)
        fAlarmRates[key] = falarms/(falarms+crejects)
        s = 0
        if key == 't2d0' or key == 't2d2' or key == 't2d4':
            s = 2
        else:
            s = 4
        Kvals[key] = kCalculation(s, hitRates[key], fAlarmRates[key])
    return {'Kvals': Kvals, 'hitRates': hitRates, 'fAlarmRates': fAlarmRates}

# calculates mean RT for each condition from csv data file
def rtCalc(csv):
    # read in csv file
    df = pd.read_csv(csv)

    # create conditions
    resps = df['Response'] == 1
    targ2 = df['nTargets'] == 2
    targ4 = df['nTargets'] == 4
    dist0 = df['nDistractors'] == 0
    dist2 = df['nDistractors'] == 2
    dist4 = df['nDistractors'] == 4

    # initialize dictionary
    rtRates = OrderedDict()
    conds = OrderedDict([('t2d0', (targ2, dist0)), ('t2d2', (targ2, dist2)), ('t2d4', (targ2, dist4)), ('t4d0', (targ4, dist0)), ('t4d2', (targ4, dist2)), ('t4d4', (targ4, dist4))])

    # calculate average RT value
    for key in conds:
        rows = df[conds[key][0] & conds[key][1] & resps]
        rts = rows['RT']
        meanRT = rts.mean()
        rtRates[key] = meanRT
    return rtRates

# calculates d's for each condition from csv data file
def dprime(csv):
     # read in csv file
    df = pd.read_csv(csv)

    # create conditions
    resps = df['Response'] == 1
    noResps = df['Response'] == 0
    changes = df['ChangeTrial'] == 1
    noChanges = df['ChangeTrial'] == 0
    targ2 = df['nTargets'] == 2
    targ4 = df['nTargets'] == 4
    dist0 = df['nDistractors'] == 0
    dist2 = df['nDistractors'] == 2
    dist4 = df['nDistractors'] == 4

    # initialize dict
    hits = OrderedDict()
    misses = OrderedDict()
    falarms = OrderedDict()
    crejects = OrderedDict()
    dprimes = OrderedDict()

    conds = OrderedDict([('t2d0', (targ2, dist0)), ('t2d2', (targ2, dist2)), ('t2d4', (targ2, dist4)), ('t4d0', (targ4, dist0)), ('t4d2', (targ4, dist2)), ('t4d4', (targ4, dist4))])

    # calculate d-primes for each condition
    for key in conds:
        hits = itemCounter(df, resps, changes, conds[key][0], conds[key][1])
        misses = itemCounter(df, noResps, changes, conds[key][0], conds[key][1])
        falarms = itemCounter(df, resps, noChanges, conds[key][0], conds[key][1])
        crejects = itemCounter(df, noResps, noChanges, conds[key][0], conds[key][1])

        # Floors an ceilings are replaced by half hits and half FA's
        halfHit = 0.5/(hits+misses)
        halfFa = 0.5/(falarms+crejects)

        # Calculate hitrate and avoid d' infinity
        hitRate = hits/(hits+misses)
        if hitRate == 1: hitRate = 1-halfHit
        if hitRate == 0: hitRate = halfHit

        # Calculate false alarm rate and avoid d' infinity
        faRate = falarms/(falarms+crejects)
        if faRate == 1: faRate = 1-halfFa
        if faRate == 0: faRate = halfFa

        # calculate dprime using Z (ppf function in scipy.norm)
        dprimes[key]= Z(hitRate) - Z(faRate)

    return dprimes

# calculates hit rate for each condition for (parameter) hem change trials
def hemHitRates(csv, hem):
    # read in csv file
    df = pd.read_csv(csv)

    # create boolean condition vectors of length nTrials
    resps = df['Response'] == 1
    noResps = df['Response'] == 0
    hemChanges = df['changeHemi'] == hem
    nTarg1 = df[hem + 'Targs'] == 1
    nTarg2 = df[hem + 'Targs'] == 2
    nDist0 = df[hem + 'Dists'] == 0
    nDist1 = df[hem + 'Dists'] == 1
    nDist2 = df[hem + 'Dists'] == 2

    # condition dictionary
    hemConds = OrderedDict([('t1d0', (nTarg1, nDist0)), ('t1d1', (nTarg1, nDist1)), ('t1d2', (nTarg1, nDist2)), ('t2d0', (nTarg2, nDist0)), ('t2d1', (nTarg2, nDist1)), ('t2d2', (nTarg2, nDist2))])
    hemHitRates = OrderedDict()

    # calculate hit rate
    for key in hemConds:
        hits = itemCounter(df, resps, hemChanges, hemConds[key][0], hemConds[key][1])
        misses = itemCounter(df, noResps, hemChanges, hemConds[key][0], hemConds[key][1])
        hitRate = hits/(hits+misses)
        hemHitRates[key] = hitRate
    return hemHitRates

# calculates dPrimes for each condition for (parameter) hem change trials
def hemdPrime(csv, hem):
    # read in csv file
    df = pd.read_csv(csv)

    # create boolean condition vectors of length nTrials
    oppositeHem = ''
    if hem == 'left':
        oppositeHem = 'right'
    else:
        oppositeHem == 'left'
    resps = df['Response'] == 1
    noResps = df['Response'] == 0
    hemChanges = df['changeHemi'] == hem
    noChanges = df['ChangeTrial'] == 0
    nTarg1 = df[hem + 'Targs'] == 1
    nTarg2 = df[hem + 'Targs'] == 2
    nDist0 = df[hem + 'Dists'] == 0
    nDist1 = df[hem + 'Dists'] == 1
    nDist2 = df[hem + 'Dists'] == 2

    # condition dictionary
    hemConds = OrderedDict([('t1d0', (nTarg1, nDist0)), ('t1d1', (nTarg1, nDist1)), ('t1d2', (nTarg1, nDist2)), ('t2d0', (nTarg2, nDist0)), ('t2d1', (nTarg2, nDist1)), ('t2d2', (nTarg2, nDist2))])
    hemdPrimes = OrderedDict()

    # calculate hit rate
    for key in hemConds:
        hits = itemCounter(df, resps, hemChanges, hemConds[key][0], hemConds[key][1])
        misses = itemCounter(df, noResps, hemChanges, hemConds[key][0], hemConds[key][1])
        falarms = itemCounter(df, resps, noChanges, hemConds[key][0], hemConds[key][1])
        crejects = itemCounter(df, noResps, noChanges, hemConds[key][0], hemConds[key][1])

        # Floors an ceilings are replaced by half hits and half FA's
        halfHit = 0.5/(hits+misses)
        halfFa = 0.5/(falarms+crejects)

        # Calculate hitrate and avoid d' infinity
        hitRate = hits/(hits+misses)
        if hitRate == 1: hitRate = 1-halfHit
        if hitRate == 0: hitRate = halfHit

        # Calculate false alarm rate and avoid d' infinity
        faRate = falarms/(falarms+crejects)
        if faRate == 1: faRate = 1-halfFa
        if faRate == 0: faRate = halfFa

        # calculate dprime using Z (ppf function in scipy.norm)
        hemdPrimes[key]= Z(hitRate) - Z(faRate)

    return hemdPrimes
<commit_msg>Reverted dataMethods to extraction technique code<commit_after>from __future__ import division
import pandas as pd
from collections import OrderedDict
from scipy.stats import norm
Z = norm.ppf
import os
from os.path import expanduser
import re

# contanst number of trials per change cond
nTrialsPerChangeCond = 16

# establish file data directory
homeDirectory = expanduser("~")
dataDirectory = homeDirectory + os.sep + 'Google Drive/tACS_VWM_ALPHA/data/stim1/'

# counts number of trials corresponding to inputted conditions
def condCounter(df, response, changeType, cond):
    count = float(len(df[response & changeType & cond]))
    return count

# calculates K pased on Pashler equation: K = S * ((H - F) / (1 - F))
def pashlerK(hits, misses, falarms, crejects, cond):
    S = cond[1]
    hitRate = hits/(hits+misses)
    faRate = falarms/(falarms+crejects)
    K = int(S) * ((hitRate - faRate) / (1 - faRate))
    return {'K': K, 'hitRate': hitRate, 'faRate': faRate}

# calculates d's using Z from scipy.stats.norm.ppf()
def dprime(hits, misses, falarms, crejects):
    # TODO fix correction

    # Floors an ceilings are replaced by half hits and half FA's
    halfHit = 0.5/(hits+misses)
    halfFa = 0.5/(falarms+crejects)

    # Calculate hitrate and avoid d' infinity
    hitRate = hits/(hits+misses)
    if hitRate == 1: hitRate = 1-halfHit
    if hitRate == 0: hitRate = halfHit

    # Calculate false alarm rate and avoid d' infinity
    faRate = falarms/(falarms+crejects)
    if faRate == 1: faRate = 1-halfFa
    if faRate == 0: faRate = halfFa

    # calculate dprime using Z (ppf function in scipy.norm)
    dprime = Z(hitRate) - Z(faRate)

    # calculate critereon using Z
    critereon = -.5 * (Z(hitRate) + Z(faRate))
    return {'dprime': dprime, 'critereon': critereon}

def rt(df, resps, change, cond):
    row = df[resps & change & cond]
    rt = row['RT'].median()
    return rt

def extractPerformance(subj, run):
    # read in csv file
    saveDirectory = dataDirectory + 's' + str(subj) + os.sep + 'runData/'
    directoryFiles = os.listdir(saveDirectory)
    csv = ''
    for file in directoryFiles:
        match = re.match('run' + str(run) + '.*', file, re.M|re.I)
        if match != None:
            csv = saveDirectory + match.group()
    df = pd.read_csv(csv)

    # create conditions
    resps = df['Response'] == 1
    noResps = df['Response'] == 0
    changes = df['ChangeTrial'] == 1
    noChanges = df['ChangeTrial'] == 0
    lChanges = df['ChangeCond'] == 1
    lNoChanges = df['ChangeCond'] != 1
    rChanges = df['ChangeCond'] == 2
    rNoChanges = df['ChangeCond'] != 2
    t1d0 = df['WFCond'] == 1
    t1d1 = df['WFCond'] == 2
    t1d2 = df['WFCond'] == 3
    t2d0 = df['WFCond'] == 4
    t2d1 = df['WFCond'] == 5
    t2d2 = df['WFCond'] == 6

    # initialize dictionaries
    lHits = OrderedDict()
    lMisses = OrderedDict()
    lFAs = OrderedDict()
    lCRs = OrderedDict()
    rHits = OrderedDict()
    rMisses = OrderedDict()
    rFAs = OrderedDict()
    rCRs = OrderedDict()
    wHits = OrderedDict()
    wMisses = OrderedDict()
    wFAs = OrderedDict()
    wCRs = OrderedDict()
    lHitRTs = OrderedDict()
    rHitRTs = OrderedDict()
    wHitRTs = OrderedDict()
    lFaRTs = OrderedDict()
    rFaRTs = OrderedDict()
    wFaRTs = OrderedDict()
    lKs = OrderedDict()
    rKs = OrderedDict()
    wKs = OrderedDict()
    lHRs = OrderedDict()
    rHRs = OrderedDict()
    wHRs = OrderedDict()
    lfaRates = OrderedDict()
    rfaRates = OrderedDict()
    wfaRates = OrderedDict()
    ldPs = OrderedDict()
    rdPs = OrderedDict()
    wdPs = OrderedDict()
    lCrits = OrderedDict()
    rCrits = OrderedDict()
    wCrits = OrderedDict()
    HFconds = OrderedDict([('t1d0', t1d0), ('t1d1', t1d1), ('t1d2', t1d2),
    ('t2d0', t2d0), ('t2d1', t2d1), ('t2d2', t2d2)])

    # store rate values
    for key in HFconds:
        lHits[key] = condCounter(df, resps, lChanges, HFconds[key])
        lMisses[key] = condCounter(df, noResps, lChanges, HFconds[key])
        #assert (lHits[key] + lMisses[key] == nTrialsPerChangeCond), 'lChange counterbalancing failed: ' + str(lHits[key] + lMisses[key])
        lFAs[key] = condCounter(df, resps, lNoChanges, HFconds[key])
        lCRs[key] = condCounter(df, noResps, lNoChanges, HFconds[key])
        #assert (lFAs[key] + lCRs[key] == nTrialsPerChangeCond * 3), 'lNoChange counterbalancing failed: ' + str(lFAs[key] + lCRs[key])

        rHits[key] = condCounter(df, resps, rChanges, HFconds[key])
        rMisses[key] = condCounter(df, noResps, rChanges, HFconds[key])
        #assert (rHits[key] + rMisses[key] == nTrialsPerChangeCond), 'rChange counterbalancing failed: ' + str(rHits[key] + rMisses[key])
        rFAs[key] = condCounter(df, resps, rNoChanges, HFconds[key])
        rCRs[key] = condCounter(df, noResps, rNoChanges, HFconds[key])
        #assert (rFAs[key] + rCRs[key] == nTrialsPerChangeCond * 3), 'rNoChange counterbalancing failed: ' + str(rFAs[key] + rCRs[key])

        # HFconds used b/c ChangeCond #s are equivalent to WFconds
        wHits[key] = condCounter(df, resps, changes, HFconds[key])
        wMisses[key] = condCounter(df, noResps, changes, HFconds[key])
        #assert (wHits[key] + wMisses[key] == nTrialsPerChangeCond * 2), 'wChange counterbalancing failed: ' + str(wHits[key] + wMisses[key])
        wFAs[key] = condCounter(df, resps, noChanges, HFconds[key])
        wCRs[key] = condCounter(df, noResps, noChanges, HFconds[key])
        #assert (wFAs[key] + wCRs[key] == nTrialsPerChangeCond * 2), 'wNoChange counterbalancing failed: ' + str(wFAs[key] + wCRs[key])

        lHitRTs[key] = rt(df, resps, lChanges, HFconds[key])
        rHitRTs[key] = rt(df, resps, rChanges, HFconds[key])
        wHitRTs[key] = rt(df, resps, changes, HFconds[key])
        lFaRTs[key] = rt(df, resps, lNoChanges, HFconds[key])
        rFaRTs[key] = rt(df, resps, rNoChanges, HFconds[key])
        wFaRTs[key] = rt(df, resps, noChanges, HFconds[key])

        lPashler = pashlerK(lHits[key],lMisses[key],lFAs[key], lCRs[key], key)
        rPashler = pashlerK(rHits[key],rMisses[key],rFAs[key], rCRs[key], key)
        wPashler = pashlerK(wHits[key],wMisses[key],wFAs[key], wCRs[key], key)
        lHRs[key] = lPashler['hitRate']
        rHRs[key] = rPashler['hitRate']
        wHRs[key] = wPashler['hitRate']
        lKs[key] = lPashler['K']
        rKs[key] = rPashler['K']
        wKs[key] = wPashler['K']
        lfaRates[key] = lPashler['faRate']
        rfaRates[key] = rPashler['faRate']
        wfaRates[key] = wPashler['faRate']
        ldPrime = dprime(lHits[key],lMisses[key],lFAs[key], lCRs[key])
        rdPrime = dprime(rHits[key],rMisses[key],rFAs[key], rCRs[key])
        wdPrime = dprime(wHits[key],wMisses[key],wFAs[key], wCRs[key])
        ldPs[key] = ldPrime['dprime']
        rdPs[key] = rdPrime['dprime']
        wdPs[key] = wdPrime['dprime']
        lCrits[key] = ldPrime['critereon']
        rCrits[key] = rdPrime['critereon']
        wCrits[key] = wdPrime['critereon']

    return {'lHits': lHits, 'rHits': rHits, 'lMisses': lMisses, 'rMisses': rMisses,
            'lFAs': lFAs, 'rFAs': rFAs, 'lCRs': lCRs, 'rCRs': rCRs, 'lHitRTs': lHitRTs,
            'rHitRTs': rHitRTs, 'lFaRTs': lFaRTs, 'rFaRTs': rFaRTs, 'lHRs': lHRs,
            'rHRs': rHRs, 'lKs': lKs, 'rKs': rKs, 'ldPs': ldPs, 'rdPs': rdPs,
            'wHRs': wHRs, 'wKs': wKs, 'wHitRTs': wHitRTs, 'wFaRTs': wFaRTs, 'wdPs': wdPs,
            'lfaRates': lfaRates, 'rfaRates': rfaRates, 'wfaRates': wfaRates,
            'lCrits': lCrits, 'rCrits': rCrits, 'wCrits': wCrits}
<|endoftext|>


<commit_before>import datetime
from django.db.models import Min, Q
from multigtfs.models import Feed, Trip
from .ni import Grouping, Timetable, Row

def handle_trips(trips, day):
    i = 0
    head = None
    rows_map = {}

    if not day:
        day = datetime.date.today()
    midnight = datetime.datetime.combine(day, datetime.time())

    for trip in trips:
        previous = None
        visited_stops = set()

        for stop in trip.stoptime_set.order_by('departure_time').select_related('stop'):
            stop_id = stop.stop.stop_id
            if stop_id in rows_map:
                if stop_id in visited_stops:
                    if (
                        previous and previous.next and previous.next.atco_code == stop_id
                        and len(previous.next.times) == i
                    ):
                        row = previous.next
                    else:
                        row = Row(stop_id, ['     '] * i)
                        row.part.stop.name = stop.stop.name
                        previous.append(row)
                else:
                    row = rows_map[stop_id]
            else:
                row = Row(stop_id, ['     '] * i)
                row.part.stop.name = stop.stop.name
                rows_map[stop_id] = row
                if previous:
                    previous.append(row)
                else:
                    if head:
                        head.prepend(row)
                    head = row
            time = datetime.timedelta(seconds=(stop.departure_time or stop.arrival_time).seconds)
            time = (midnight + time).time()
            row.times.append(time)
            row.part.timingstatus = None
            previous = row
            visited_stops.add(stop_id)

        if i:
            p = head
            while p:
                if len(p.times) == i:
                    p.times.append('     ')
                p = p.next
        i += 1
    p = head
    g = Grouping()

    while p:
        g.rows.append(p)
        p = p.next
    return g

def get_timetable(routes, day):
    if not routes:
        return

    trips_dict = {}

    trips = Trip.objects.filter(route__in=routes)
    trips = trips.annotate(departure_time=Min('stoptime__departure_time')).order_by('departure_time')
    if day:
        trips = trips.filter(Q(service__servicedate__date=day, service__servicedate__exception_type=1) |
                             Q(service__start_date__lte=day, service__end_date__gte=day,
                               **{'service__' + day.strftime('%A').lower(): True}))
        trips = trips.exclude(service__servicedate__date=day, service__servicedate__exception_type=2)
    for trip in trips.select_related('service'):
        if trip.direction in trips_dict:
            trips_dict[trip.direction].append(trip)
        else:
            trips_dict[trip.direction] = [trip]

    t = Timetable()
    t.groupings = [handle_trips(trips_dict[direction], day) for direction in trips_dict]
    t.date = day
    for grouping in t.groupings:
        grouping.name = grouping.rows[0].part.stop.name + ' - ' + grouping.rows[-1].part.stop.name
    return t

def get_timetables(service_code, day):
    parts = service_code.split('-', 1)
    collection = parts[0]
    route_id = parts[1] + '-'
    if len(collection) == 10:
        feed = Feed.objects.filter(name__startswith=collection)
    else:
        feed = Feed.objects.filter(name=collection)
    try:
        feed = feed.latest('created')
    except Feed.DoesNotExist:
        return
    timetable = get_timetable(feed.route_set.filter(route_id__startswith=route_id), day)
    if timetable:
        return [timetable]
<commit_msg>Use prefetch_related to quicken GTFS timetables<commit_after>import datetime
from django.db.models import Min, Q, Prefetch
from multigtfs.models import Feed, Trip, StopTime
from .ni import Grouping, Timetable, Row

def handle_trips(trips, day):
    i = 0
    head = None
    rows_map = {}

    if not day:
        day = datetime.date.today()
    midnight = datetime.datetime.combine(day, datetime.time())

    for trip in trips:
        previous = None
        visited_stops = set()

        for stop in trip.stoptime_set.all():
            stop_id = stop.stop.stop_id
            if stop_id in rows_map:
                if stop_id in visited_stops:
                    if (
                        previous and previous.next and previous.next.atco_code == stop_id
                        and len(previous.next.times) == i
                    ):
                        row = previous.next
                    else:
                        row = Row(stop_id, ['     '] * i)
                        row.part.stop.name = stop.stop.name
                        previous.append(row)
                else:
                    row = rows_map[stop_id]
            else:
                row = Row(stop_id, ['     '] * i)
                row.part.stop.name = stop.stop.name
                rows_map[stop_id] = row
                if previous:
                    previous.append(row)
                else:
                    if head:
                        head.prepend(row)
                    head = row
            time = datetime.timedelta(seconds=(stop.departure_time or stop.arrival_time).seconds)
            time = (midnight + time).time()
            row.times.append(time)
            row.part.timingstatus = None
            previous = row
            visited_stops.add(stop_id)

        if i:
            p = head
            while p:
                if len(p.times) == i:
                    p.times.append('     ')
                p = p.next
        i += 1
    p = head
    g = Grouping()

    while p:
        g.rows.append(p)
        p = p.next
    return g

def get_timetable(routes, day):
    if not routes:
        return

    trips_dict = {}

    trips = Trip.objects.filter(route__in=routes)
    trips = trips.annotate(departure_time=Min('stoptime__trip_id')).order_by('departure_time')
    if day:
        trips = trips.filter(Q(service__servicedate__date=day, service__servicedate__exception_type=1) |
                             Q(service__start_date__lte=day, service__end_date__gte=day,
                               **{'service__' + day.strftime('%A').lower(): True}))
        trips = trips.exclude(service__servicedate__date=day, service__servicedate__exception_type=2)
    prefetch = Prefetch('stoptime_set', queryset=StopTime.objects.select_related('stop').order_by('stop_sequence'))
    trips = trips.prefetch_related(prefetch).select_related('service')
    for trip in trips:
        if trip.direction in trips_dict:
            trips_dict[trip.direction].append(trip)
        else:
            trips_dict[trip.direction] = [trip]

    t = Timetable()
    t.groupings = [handle_trips(trips_dict[direction], day) for direction in trips_dict]
    t.date = day
    for grouping in t.groupings:
        grouping.name = grouping.rows[0].part.stop.name + ' - ' + grouping.rows[-1].part.stop.name
    return t

def get_timetables(service_code, day):
    parts = service_code.split('-', 1)
    collection = parts[0]
    route_id = parts[1] + '-'
    if len(collection) == 10:
        feed = Feed.objects.filter(name__startswith=collection)
    else:
        feed = Feed.objects.filter(name=collection)
    try:
        feed = feed.latest('created')
    except Feed.DoesNotExist:
        return
    timetable = get_timetable(feed.route_set.filter(route_id__startswith=route_id), day)
    if timetable:
        return [timetable]
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""AppiumEnhanceLibrary is the enhancement of robotframework-appiumlibrary.

It will bring back these missing keywords from robotframework selenium2library.

Detail imformation could be found on github.com:
    https://github.com/ScenK/robotframework-AppiumEnhanceLibrary
"""
from robot.libraries.BuiltIn import BuiltIn

class AppiumEnhanceLibrary(object):
    """AppiumEnhanceLibrary for supporting actions that not included in RF.

    Support more keywords for AppiumLibrary.

    Detail imformation about AppiumLibrary could be found on github.com:
        https://github.com/jollychang/robotframework-appiumlibrary
    """

    def __init__(self):
        """Init function.

        Load and store configs in to variables.
        """
        super(AppiumEnhanceLibrary, self).__init__()
        self.apu = BuiltIn().get_library_instance('AppiumLibrary')

    def execute_javascript(self, code):
        """Execute the given JavaScript code.

        `code` may contain multiple lines of code and may be divided into
        multiple cells in the test data. In that case, the parts are
        catenated together without adding spaces.

        The JavaScript executes in the context of the currently selected
        frame or window as the body of an anonymous function. Use _window_ to
        refer to the window of your application and _document_ to refer to the
        document object of the current frame or window, e.g.
        _document.getElementById('foo')_.

        This keyword returns None unless there is a return statement in the
        JavaScript. Return values are converted to the appropriate type in
        Python, including WebElements.

        Examples:
        | Execute JavaScript | window.my_js('arg1', 'arg2') |               |
        | ${sum}=            | Execute JavaScript           | return 1 + 1; |
        | Should Be Equal    | ${sum}                       | ${2}          |
        """
        driver = self.apu._current_application()
        return driver.execute_script(code)

    def wait_until_element_is_visible(self, locator, timeout=None, error=None):
        """Wait until element specified with `locator` is visible.

        Fails if `timeout` expires before the element is visible. See
        `introduction` for more information about `timeout` and its
        default value.

        `error` can be used to override the default error message.

        See also `Wait Until Page Contains`, `Wait Until Page Contains
        Element`, `Wait For Condition`.
        """
        def check_visibility():
            visible = self._is_visible(locator)
            if visible:
                return
            elif visible is None:
                return error or "Element locator '%s' did not match any " \
                                "elements after %s" % \
                                (locator, self.apu._format_timeout(timeout))
            else:
                return error or "Element '%s' was not visible in %s" % \
                                (locator, self.apu._format_timeout(timeout))
        self.apu._wait_until_no_error(timeout, check_visibility)

    def wait_until_element_is_not_visible(self, locator, timeout=None,
                                          error=None):
        """Wait until element specified with `locator` is not visible.

        Fails if `timeout` expires before the element is not visible. See
        `introduction` for more information about `timeout` and its
        default value.

        `error` can be used to override the default error message.

        See also `Wait Until Page Contains`, `Wait Until Page Contains
        Element`, `Wait For Condition`.
        """
        def check_hidden():
            visible = self._is_visible(locator)
            if not visible:
                return
            elif visible is None:
                return error or "Element locator '%s' did not match any " \
                                "elements after %s" % \
                                (locator, self.apu._format_timeout(timeout))
            else:
                return error or "Element '%s' was still visible in %s" % \
                                (locator, self.apu._format_timeout(timeout))
        self.apu._wait_until_no_error(timeout, check_hidden)

    def element_should_be_visible(self, locator, message=''):
        """Verifies that the element identified by `locator` is visible.

        Herein, visible means that the element is logically visible,
        not optically visible in the current browser viewport. For example,
        an element that carries display:none is not logically visible,
        so using this keyword on that element would fail.

        `message` can be used to override the default error message.

        Key attributes for arbitrary elements are `id` and `name`. See
        `introduction` for details about locating elements.
        """
        visible = self._is_visible(locator)
        if not visible:
            if not message:
                message = "The element '%s' should be visible, but it " \
                          "is not." % locator
            raise AssertionError(message)

    def element_should_not_be_visible(self, locator, message=''):
        """Verifies that the element identified by `locator` is NOT visible.

        This is the opposite of `Element Should Be Visible`.

        `message` can be used to override the default error message.

        Key attributes for arbitrary elements are `id` and `name`. See
        `introduction` for details about locating elements.
        """
        visible = self._is_visible(locator)
        if visible:
            if not message:
                message = "The element '%s' should not be visible, " \
                          "but it is." % locator
            raise AssertionError(message)

    # Private

    def _is_visible(self, locator):
        element = self.apu._element_find(locator, True, False)
        if element is not None:
            return element.is_displayed()
        return None
<commit_msg>Wait Until Element (Does Not) Contain<commit_after># -*- coding: utf-8 -*-
"""AppiumEnhanceLibrary is the enhancement of robotframework-appiumlibrary.

It will bring back these missing keywords from robotframework selenium2library.

Detail imformation could be found on github.com:
    https://github.com/ScenK/robotframework-AppiumEnhanceLibrary
"""
from robot.libraries.BuiltIn import BuiltIn

class AppiumEnhanceLibrary(object):
    """AppiumEnhanceLibrary for supporting actions that not included in RF.

    Support more keywords for AppiumLibrary.

    Detail imformation about AppiumLibrary could be found on github.com:
        https://github.com/jollychang/robotframework-appiumlibrary
    """

    def __init__(self):
        """Init function.

        Load and store configs in to variables.
        """
        super(AppiumEnhanceLibrary, self).__init__()
        self.apu = BuiltIn().get_library_instance('AppiumLibrary')

    def execute_javascript(self, code):
        """Execute the given JavaScript code.

        `code` may contain multiple lines of code and may be divided into
        multiple cells in the test data. In that case, the parts are
        catenated together without adding spaces.

        The JavaScript executes in the context of the currently selected
        frame or window as the body of an anonymous function. Use _window_ to
        refer to the window of your application and _document_ to refer to the
        document object of the current frame or window, e.g.
        _document.getElementById('foo')_.

        This keyword returns None unless there is a return statement in the
        JavaScript. Return values are converted to the appropriate type in
        Python, including WebElements.

        Examples:
        | Execute JavaScript | window.my_js('arg1', 'arg2') |               |
        | ${sum}=            | Execute JavaScript           | return 1 + 1; |
        | Should Be Equal    | ${sum}                       | ${2}          |
        """
        driver = self.apu._current_application()
        return driver.execute_script(code)

    def wait_until_element_is_visible(self, locator, timeout=None, error=None):
        """Wait until element specified with `locator` is visible.

        Fails if `timeout` expires before the element is visible. See
        `introduction` for more information about `timeout` and its
        default value.

        `error` can be used to override the default error message.

        See also `Wait Until Page Contains`, `Wait Until Page Contains
        Element`, `Wait For Condition`.
        """

        def check_visibility():
            visible = self._is_visible(locator)
            if visible:
                return
            elif visible is None:
                return error or "Element locator '%s' did not match any " \
                                "elements after %s" % \
                                (locator, self.apu._format_timeout(timeout))
            else:
                return error or "Element '%s' was not visible in %s" % \
                                (locator, self.apu._format_timeout(timeout))

        self.apu._wait_until_no_error(timeout, check_visibility)

    def wait_until_element_is_not_visible(self, locator, timeout=None,
                                          error=None):
        """Wait until element specified with `locator` is not visible.

        Fails if `timeout` expires before the element is not visible. See
        `introduction` for more information about `timeout` and its
        default value.

        `error` can be used to override the default error message.

        See also `Wait Until Page Contains`, `Wait Until Page Contains
        Element`, `Wait For Condition`.
        """

        def check_hidden():
            visible = self._is_visible(locator)
            if not visible:
                return
            elif visible is None:
                return error or "Element locator '%s' did not match any " \
                                "elements after %s" % \
                                (locator, self.apu._format_timeout(timeout))
            else:
                return error or "Element '%s' was still visible in %s" % \
                                (locator, self.apu._format_timeout(timeout))

        self.apu._wait_until_no_error(timeout, check_hidden)

    def element_should_be_visible(self, locator, message=''):
        """Verifies that the element identified by `locator` is visible.

        Herein, visible means that the element is logically visible,
        not optically visible in the current browser viewport. For example,
        an element that carries display:none is not logically visible,
        so using this keyword on that element would fail.

        `message` can be used to override the default error message.

        Key attributes for arbitrary elements are `id` and `name`. See
        `introduction` for details about locating elements.
        """
        visible = self._is_visible(locator)
        if not visible:
            if not message:
                message = "The element '%s' should be visible, but it " \
                          "is not." % locator
            raise AssertionError(message)

    def element_should_not_be_visible(self, locator, message=''):
        """Verifies that the element identified by `locator` is NOT visible.

        This is the opposite of `Element Should Be Visible`.

        `message` can be used to override the default error message.

        Key attributes for arbitrary elements are `id` and `name`. See
        `introduction` for details about locating elements.
        """
        visible = self._is_visible(locator)
        if visible:
            if not message:
                message = "The element '%s' should not be visible, " \
                          "but it is." % locator
            raise AssertionError(message)

    def wait_until_element_contains(self, locator, text, timeout=None,
                                    error=None):
        """Waits until given element contains `text`.

        Fails if `timeout` expires before the text appears on given element.
        See `introduction` for more information about `timeout` and its
        default value.

        `error` can be used to override the default error message.

        See also `Wait Until Page Contains`, `Wait Until Page Contains Element`
        , `Wait For Condition`, `Wait Until Element Is Visible`.
        """
        element = self.apu._element_find(locator, True, True)

        def check_text():
            actual = element.text
            if text in actual:
                return
            else:
                return error or "Text '%s' did not appear in %s to element " \
                                "'%s'. Its text was '%s'." \
                                % (text, self.apu._format_timeout(timeout),
                                   locator, actual)

        self.apu._wait_until_no_error(timeout, check_text)

    def wait_until_element_does_not_contain(self, locator, text,
                                            timeout=None, error=None):
        """Waits until given element does not contain `text`.

        Fails if `timeout` expires before the text disappears from given
        element. See `introduction` for more information about `timeout` and
        its default value.

        `error` can be used to override the default error message.

        See also `Wait Until Page Contains`, `Wait Until Page Contains Element`
        , `Wait For Condition`, `Wait Until Element Is Visible`.
        """
        element = self.apu._element_find(locator, True, True)

        def check_text():
            actual = element.text
            if text not in actual:
                return
            else:
                return error or "Text '%s' did not disappear in %s from " \
                                "element '%s'." % (text,
                                                   self.apu._format_timeout(
                                                       timeout), locator)

        self.apu._wait_until_no_error(timeout, check_text)

    # Private

    def _is_visible(self, locator):
        element = self.apu._element_find(locator, True, False)
        if element is not None:
            return element.is_displayed()
        return None
<|endoftext|>