<commit_before># Author Michele Mattioni
# Fri Jan 30 15:57:01 GMT 2009

import ecell.Session as Session
#import mySession as Session
import ecell.ecs
import ecell.config
import ecell.emc
import os
import numpy
from sumatra.external.NeuroTools import parameters

class EcellManager():
    """Control and instatiate the ecell simulator embedding it in an handy python object"""

    def __init__(self, filename=None):
        ecell.ecs.setDMSearchPath( os.pathsep.join( ecell.config.dm_path ) )
        self.sim = ecell.emc.Simulator()
        if ecell.config.version < '3.2.0':
            self.ses = Session.Session(self.sim, changeDirectory=False)
        else:
            self.ses = Session.Session(self.sim)

        # Load the model
        self.ses.loadModel(filename)
        self.molToTrack = ('ca',
                           'moles_bound_ca_per_moles_cam',
                           'Rbar',
                           'PP2Bbar',
                           'CaMKIIbar',
                           'PP1abar', # Active PP1/Total PP1
                           'AMPAR', #
                           'AMPAR_P',
                           'CBPfastca',
                           'CBPmediaca',
                           'CBPslowca',
                           'CBPvslowca'
                           )
        # Tracking the calcium
        self.ca =  self.ses.createEntityStub( 'Variable:/Spine:ca' )
        self.CaMKIIbar = self.ses.createEntityStub( 'Variable:/Spine:CaMKIIbar' )
        self.ampar_P = self.ses.createEntityStub('Variable:/Spine:AMPAR_P')
        self.ca_in = self.ses.createEntityStub('Process:/Spine:ca_in')
        self.ca_leak = self.ses.createEntityStub('Process:/Spine:ca_leak')
        self.ca_pump = self.ses.createEntityStub('Process:/Spine:ca_pump')

    def createLoggers(self):
        """Create the logger to track the species"""
        loggers = {}
        #log = ecell.LoggerStub()

        for mol in self.molToTrack:

            loggers[mol]  = self.ses.createLoggerStub( "Variable:/Spine:" + mol
                                                       + ":Value" )
            loggers[mol].create() # This creat the Logger Object in the backend
            if mol == 'ca':
                loggers['ca_conc']  = self.ses.createLoggerStub( "Variable:/Spine:" + mol
                                                       + ":MolarConc" )
                loggers['ca_conc'].create() # This creat the Logger Object in the backend

        self.loggers = loggers

    def calcWeight(CaMKIIbar, PP2Bbar, alpha, beta, n=3, k=0.5):
        """Calc the weight of the synapses according to the CaMKII and Pospahtases
        PP2B and PP1"""

        # CaMKII term
        CaMKII_factor = math.pow(CaMKIIbar, n) / (math.pow(k, n) +
                                                  math.pow(CaMKIIbar, n))
        Phosphatase_factor = math.pow(PP2Bbar, n) / (math.pow(k, n) +
                                                     math.pow(PP2Bbar, n))
        scaled_CaMKII_factor = alpha * CaMKII_factor
        scaled_Phospatese_factor = beta * Phosphatase_factor
        weight = 1 + scaled_CaMKII_factor - scaled_Phospatese_factor
        s = "Weight: %s CaMKII factor %s, Phosphatase factor %s" %(weight,
                                                                   scaled_CaMKII_factor,
                                                                   scaled_Phospatese_factor)
        return weight

    def calcium_peak(self, k_value, duration):
        """
        Mimic the calcium peak

        :Parameters
            k_value: the rate of calcium to enter
            duration: Duration of the spike
        """

        basal = self.ca_in['k']
        self.ca_in['k'] = k_value
        self.ses.run(duration)
        self.ca_in['k'] = basal

    def calciumTrain(self, spikes=30, interval=0.1):
        """Create a train of calcium with the specified number of spikes and interval

        :Parameter
            spikes: number of spikes
            interval: Interval between spikes
        """
        for i in range(spikes):
            self.calcium_peak(4.0e8, # Magic number from Lu
                         0.00001 #Really fast spike to avoid the overlap
                         )
            self.ses.run(interval)

    def converToTimeCourses(self):
        timeCourses = {}
        for key in self.loggers:
            timeCourses[key] = self.loggers[key].getData()

        self.timeCourses = timeCourses

##############################################
# Testing method

def testCalciumTrain(spikes_number, interval, filename):
    """Run a test simulation wit a train of calcium input"""

    print "Test the results of a train of calcium"""
    ecellManager = EcellManager(filename)
    ecellManager.createLoggers()
    #ecellManager.ca_in = ecellManager.ses.createEntityStub('Process:/Spine:ca_in')
    print "Model loaded, loggers created. Integration start."
    ecellManager.ses.run(300)
    print "Calcium Train"
    ecellManager.calciumTrain(spikes=spikes_number, interval=interval)
    ecellManager.ses.run(400)
    ecellManager.converToTimeCourses()
    print "CalciumTrain Test Concluded\n##################"
    return ecellManager

def testChangeCalciumValue(interval, caValue, filename="../biochemical_circuits/biomd183_noCalcium.eml"):
    """Run a test simulation changing the calcium value on the fly"""
    print "Show case of the possibilities to change the level of calcium on the fly"
    ecellManager = EcellManager(filename)
    ecellManager.createLoggers()
    print "Loggers created"
    print "Running with the updating interval of : %f" %interval

    tstop = 150
    while(ecellManager.ses.getCurrentTime() < tstop):
        ecellManager.ca['Value'] = caValue
        ecellManager.ses.run(interval)
        #ecellManager.ses.run(1)
        #print ecellManager.ses.getCurrentTime()

    print "immision of Calcium"
    print "Value of Calcium %f" %ecellManager.ca.getProperty('Value')
    spikes = 4
    for i in range(spikes):
        ecellManager.ca['Value'] = 7200
        ecellManager.ses.run(0.020)
        ecellManager.ca['Value'] = caValue
        ecellManager.ses.run(0.010)

    tstop = tstop+500
    while(ecellManager.ses.getCurrentTime() < tstop):
        ecellManager.ca['Value'] = caValue
        ecellManager.ses.run(interval)
        #ecellManager.ses.run(1)
        #print ecellManager.ses.getCurrentTime()

    ecellManager.converToTimeCourses()
    print "ChangeCalciumValue Test Concluded"
    return ecellManager

def plotTimeCourses(timeCourses, save=False, dir=None, name=None, x_lims=None):
     """Plot the default timecourses"""
     import matplotlib.pyplot as plt
     ca_tc = timeCourses['ca']
     plt.figure()
     plt.plot(ca_tc[:,0], ca_tc[:,1], label="Calcium")
     plt.xlabel("Time [s]")
     if x_lims:
         plt.xlim(x_lims)
     plt.legend(loc=0)

     if save :
         filename = name +"_caInput.png"
         plt.savefig(os.path.join(dir, filename))
         print "figure saved in: %s" % os.path.join(dir, filename)

     vars = ['PP2Bbar', 'CaMKIIbar', 'PP1abar', 'AMPAR', 'AMPAR_P']

     for var in vars:
         plt.figure()
         var_tc = timeCourses[var]
         plt.plot(var_tc[:,0], var_tc[:,1], label=var)
         plt.xlabel("Time [s]")
         if x_lims:
             plt.xlim(x_lims)
         plt.legend(loc=0)

         if save :
             filename = name + "_" + var + ".png"
             plt.savefig(os.path.join(dir, filename))
             print "figure saved in: %s" % os.path.join(dir, filename)

def plotWeight(timecourses, dir=None):
    import matplotlib.pyplot as plt
    scaled_CaMKII = []
    time = timecourses['AMPAR'][:,0] # time equal for everything

    plt.figure()
    plt.plot(time, timecourses['AMPAR_P'][:,1], label='AMPAR_P')
    #plt.plot(time, weight_baseline, label='w_b')
    title = "AMPA weight, calculated from E-Cell"
    plt.title(title)
    plt.legend(loc=0)
    if dir is not None:
        plt.savefig(os.path.join(dir, "weight.png"))
    plt.plot(time, timecourses['AMPAR'][:,1], label='AMPAR')
    plt.legend(loc=0)
    if dir is not None:
        plt.savefig(os.path.join(dir, "weight_both.png"))

if __name__ == "__main__":

    import sys
    if len(sys.argv) != 2:
        print("No parameter file supplied. Abort.")
        usage = 'python ecellManager.py ecellControl.param'
        print usage
        sys.exit()

    parameter_file = sys.argv[1]
    param = parameters.ParameterSet(parameter_file)

    ## Setting the mat plotlib backend
    import matplotlib
    if param['interactive'] == False:
        matplotlib.use('Agg')
        print "Switching backend to Agg. Batch execution"
    import matplotlib.pyplot as plt
    import helpers
    loader = helpers.Loader()

#    ecellManager = testChangeCalciumValue(interval, caValue)
    if param['running_type'] == 'train':
        ecellManager = testCalciumTrain(param['num_spikes'],
                                        param['delay'],
                                        param['biochemical_filename'])

    if param['interactive'] == False:
        dir = loader.create_new_dir(prefix=os.getcwd())
        loader.save(ecellManager.timeCourses,  dir, "timeCourses")
        plotTimeCourses(ecellManager.timeCourses, save=True, dir=dir)
        plotWeight(ecellManager.timeCourses, dir=dir)
    else:
        plotTimeCourses(ecellManager.timeCourses)
        plotWeight(ecellManager.timeCourses)
        plt.show()
<commit_msg>Inserting the spine number in the legend.<commit_after># Author Michele Mattioni
# Fri Jan 30 15:57:01 GMT 2009

import ecell.Session as Session
#import mySession as Session
import ecell.ecs
import ecell.config
import ecell.emc
import os
import numpy
from sumatra.external.NeuroTools import parameters

class EcellManager():
    """Control and instatiate the ecell simulator embedding it in an handy python object"""

    def __init__(self, filename=None):
        ecell.ecs.setDMSearchPath( os.pathsep.join( ecell.config.dm_path ) )
        self.sim = ecell.emc.Simulator()
        if ecell.config.version < '3.2.0':
            self.ses = Session.Session(self.sim, changeDirectory=False)
        else:
            self.ses = Session.Session(self.sim)

        # Load the model
        self.ses.loadModel(filename)
        self.molToTrack = ('ca',
                           'moles_bound_ca_per_moles_cam',
                           'Rbar',
                           'PP2Bbar',
                           'CaMKIIbar',
                           'PP1abar', # Active PP1/Total PP1
                           'AMPAR', #
                           'AMPAR_P',
                           'CBPfastca',
                           'CBPmediaca',
                           'CBPslowca',
                           'CBPvslowca'
                           )
        # Tracking the calcium
        self.ca =  self.ses.createEntityStub( 'Variable:/Spine:ca' )
        self.CaMKIIbar = self.ses.createEntityStub( 'Variable:/Spine:CaMKIIbar' )
        self.ampar_P = self.ses.createEntityStub('Variable:/Spine:AMPAR_P')
        self.ca_in = self.ses.createEntityStub('Process:/Spine:ca_in')
        self.ca_leak = self.ses.createEntityStub('Process:/Spine:ca_leak')
        self.ca_pump = self.ses.createEntityStub('Process:/Spine:ca_pump')

    def createLoggers(self):
        """Create the logger to track the species"""
        loggers = {}
        #log = ecell.LoggerStub()

        for mol in self.molToTrack:

            loggers[mol]  = self.ses.createLoggerStub( "Variable:/Spine:" + mol
                                                       + ":Value" )
            loggers[mol].create() # This creat the Logger Object in the backend
            if mol == 'ca':
                loggers['ca_conc']  = self.ses.createLoggerStub( "Variable:/Spine:" + mol
                                                       + ":MolarConc" )
                loggers['ca_conc'].create() # This creat the Logger Object in the backend

        self.loggers = loggers

    def calcWeight(CaMKIIbar, PP2Bbar, alpha, beta, n=3, k=0.5):
        """Calc the weight of the synapses according to the CaMKII and Pospahtases
        PP2B and PP1"""

        # CaMKII term
        CaMKII_factor = math.pow(CaMKIIbar, n) / (math.pow(k, n) +
                                                  math.pow(CaMKIIbar, n))
        Phosphatase_factor = math.pow(PP2Bbar, n) / (math.pow(k, n) +
                                                     math.pow(PP2Bbar, n))
        scaled_CaMKII_factor = alpha * CaMKII_factor
        scaled_Phospatese_factor = beta * Phosphatase_factor
        weight = 1 + scaled_CaMKII_factor - scaled_Phospatese_factor
        s = "Weight: %s CaMKII factor %s, Phosphatase factor %s" %(weight,
                                                                   scaled_CaMKII_factor,
                                                                   scaled_Phospatese_factor)
        return weight

    def calcium_peak(self, k_value, duration):
        """
        Mimic the calcium peak

        :Parameters
            k_value: the rate of calcium to enter
            duration: Duration of the spike
        """

        basal = self.ca_in['k']
        self.ca_in['k'] = k_value
        self.ses.run(duration)
        self.ca_in['k'] = basal

    def calciumTrain(self, spikes=30, interval=0.1):
        """Create a train of calcium with the specified number of spikes and interval

        :Parameter
            spikes: number of spikes
            interval: Interval between spikes
        """
        for i in range(spikes):
            self.calcium_peak(4.0e8, # Magic number from Lu
                         0.00001 #Really fast spike to avoid the overlap
                         )
            self.ses.run(interval)

    def converToTimeCourses(self):
        timeCourses = {}
        for key in self.loggers:
            timeCourses[key] = self.loggers[key].getData()

        self.timeCourses = timeCourses

##############################################
# Testing method

def testCalciumTrain(spikes_number, interval, filename):
    """Run a test simulation wit a train of calcium input"""

    print "Test the results of a train of calcium"""
    ecellManager = EcellManager(filename)
    ecellManager.createLoggers()
    #ecellManager.ca_in = ecellManager.ses.createEntityStub('Process:/Spine:ca_in')
    print "Model loaded, loggers created. Integration start."
    ecellManager.ses.run(300)
    print "Calcium Train"
    ecellManager.calciumTrain(spikes=spikes_number, interval=interval)
    ecellManager.ses.run(400)
    ecellManager.converToTimeCourses()
    print "CalciumTrain Test Concluded\n##################"
    return ecellManager

def testChangeCalciumValue(interval, caValue, filename="../biochemical_circuits/biomd183_noCalcium.eml"):
    """Run a test simulation changing the calcium value on the fly"""
    print "Show case of the possibilities to change the level of calcium on the fly"
    ecellManager = EcellManager(filename)
    ecellManager.createLoggers()
    print "Loggers created"
    print "Running with the updating interval of : %f" %interval

    tstop = 150
    while(ecellManager.ses.getCurrentTime() < tstop):
        ecellManager.ca['Value'] = caValue
        ecellManager.ses.run(interval)
        #ecellManager.ses.run(1)
        #print ecellManager.ses.getCurrentTime()

    print "immision of Calcium"
    print "Value of Calcium %f" %ecellManager.ca.getProperty('Value')
    spikes = 4
    for i in range(spikes):
        ecellManager.ca['Value'] = 7200
        ecellManager.ses.run(0.020)
        ecellManager.ca['Value'] = caValue
        ecellManager.ses.run(0.010)

    tstop = tstop+500
    while(ecellManager.ses.getCurrentTime() < tstop):
        ecellManager.ca['Value'] = caValue
        ecellManager.ses.run(interval)
        #ecellManager.ses.run(1)
        #print ecellManager.ses.getCurrentTime()

    ecellManager.converToTimeCourses()
    print "ChangeCalciumValue Test Concluded"
    return ecellManager

def plotTimeCourses(timeCourses, save=False, dir=None, name=None, x_lims=None):
     """Plot the default timecourses"""
     import matplotlib.pyplot as plt
     ca_tc = timeCourses['ca']
     plt.figure()
     plt.plot(ca_tc[:,0], ca_tc[:,1], label="Calcium")
     plt.xlabel("Time [s]")
     if x_lims:
         plt.xlim(x_lims)
     plt.legend(loc=0)

     if save :
         filename = name +"_caInput.png"
         plt.savefig(os.path.join(dir, filename))
         print "figure saved in: %s" % os.path.join(dir, filename)

     vars = ['PP2Bbar', 'CaMKIIbar', 'PP1abar', 'AMPAR', 'AMPAR_P']

     for var in vars:
         plt.figure()
         var_tc = timeCourses[var]
         lbl = name + '_' + var
         plt.plot(var_tc[:,0], var_tc[:,1], label=lbl)
         plt.xlabel("Time [s]")
         if x_lims:
             plt.xlim(x_lims)
         plt.legend(loc=0)

         if save :
             filename = name + "_" + var + ".png"
             plt.savefig(os.path.join(dir, filename))
             print "figure saved in: %s" % os.path.join(dir, filename)

def plotWeight(timecourses, dir=None):
    import matplotlib.pyplot as plt
    scaled_CaMKII = []
    time = timecourses['AMPAR'][:,0] # time equal for everything

    plt.figure()
    plt.plot(time, timecourses['AMPAR_P'][:,1], label='AMPAR_P')
    #plt.plot(time, weight_baseline, label='w_b')
    title = "AMPA weight, calculated from E-Cell"
    plt.title(title)
    plt.legend(loc=0)
    if dir is not None:
        plt.savefig(os.path.join(dir, "weight.png"))
    plt.plot(time, timecourses['AMPAR'][:,1], label='AMPAR')
    plt.legend(loc=0)
    if dir is not None:
        plt.savefig(os.path.join(dir, "weight_both.png"))

if __name__ == "__main__":

    import sys
    if len(sys.argv) != 2:
        print("No parameter file supplied. Abort.")
        usage = 'python ecellManager.py ecellControl.param'
        print usage
        sys.exit()

    parameter_file = sys.argv[1]
    param = parameters.ParameterSet(parameter_file)

    ## Setting the mat plotlib backend
    import matplotlib
    if param['interactive'] == False:
        matplotlib.use('Agg')
        print "Switching backend to Agg. Batch execution"
    import matplotlib.pyplot as plt
    import helpers
    loader = helpers.Loader()

#    ecellManager = testChangeCalciumValue(interval, caValue)
    if param['running_type'] == 'train':
        ecellManager = testCalciumTrain(param['num_spikes'],
                                        param['delay'],
                                        param['biochemical_filename'])

    if param['interactive'] == False:
        dir = loader.create_new_dir(prefix=os.getcwd())
        loader.save(ecellManager.timeCourses,  dir, "timeCourses")
        plotTimeCourses(ecellManager.timeCourses, save=True, dir=dir)
        plotWeight(ecellManager.timeCourses, dir=dir)
    else:
        plotTimeCourses(ecellManager.timeCourses)
        plotWeight(ecellManager.timeCourses)
        plt.show()
<|endoftext|>


<commit_before># Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import BaseHTTPServer
from collections import namedtuple
import mimetypes
import os
import SimpleHTTPServer
import SocketServer
import sys
import zlib

ByteRange = namedtuple('ByteRange', ['from_byte', 'to_byte'])
ResourceAndRange = namedtuple('ResourceAndRange', ['resource', 'byte_range'])

class MemoryCacheHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):

  def do_GET(self):
    """Serve a GET request."""
    resource_range = self.SendHead()

    if not resource_range.resource:
      return
    response = resource_range.resource['response']

    if not resource_range.byte_range:
      self.wfile.write(response)
      return

    start_index = resource_range.byte_range.from_byte
    end_index = resource_range.byte_range.to_byte
    self.wfile.write(response[start_index:end_index + 1])

  def do_HEAD(self):
    """Serve a HEAD request."""
    self.SendHead()

  def SendHead(self):
    path = self.translate_path(self.path)
    if path not in self.server.resource_map:
      self.send_error(404, 'File not found')
      return None

    resource = self.server.resource_map[path]
    total_num_of_bytes = resource['content-length']
    byte_range = self.GetByteRange(total_num_of_bytes)
    if byte_range:
      # request specified a range, so set response code to 206.
      self.send_response(206)
      self.send_header('Content-Range',
                       'bytes %d-%d/%d' % (byte_range.from_byte,
                                           byte_range.to_byte,
                                           total_num_of_bytes))
      total_num_of_bytes = byte_range.to_byte - byte_range.from_byte + 1
    else:
      self.send_response(200)

    self.send_header('Content-Length', str(total_num_of_bytes))
    self.send_header('Content-Type', resource['content-type'])
    self.send_header('Last-Modified',
                     self.date_time_string(resource['last-modified']))
    if resource['zipped']:
      self.send_header('Content-Encoding', 'deflate')
    self.end_headers()
    return ResourceAndRange(resource, byte_range)

  def GetByteRange(self, total_num_of_bytes):
    """Parse the header and get the range values specified.

    Args:
      total_num_of_bytes: Total # of bytes in requested resource,
      used to calculate upper range limit.
    Returns:
      A ByteRange namedtuple object with the requested byte-range values.
      If no Range is explicitly requested or there is a failure parsing,
      return None.
      Special case: If range specified is in the format "N-", return N-N.
      If upper range limit is greater than total # of bytes, return upper index.
    """

    range_header = self.headers.getheader('Range')
    if range_header is None:
      return None
    if not range_header.startswith('bytes='):
      return None

    # The range header is expected to be a string in this format:
    # bytes=0-1
    # Get the upper and lower limits of the specified byte-range.
    # We've already confirmed that range_header starts with 'bytes='.
    byte_range_values = range_header[len('bytes='):].split('-')
    from_byte = 0
    to_byte = 0

    if len(byte_range_values) == 2:
      from_byte = int(byte_range_values[0])
      if byte_range_values[1]:
        to_byte = int(byte_range_values[1])
    else:
      return None

    # Do some validation.
    if from_byte < 0:
      return None

    if to_byte < from_byte:
      to_byte = from_byte

    if to_byte >= total_num_of_bytes:
      # End of range requested is greater than length of requested resource.
      # Only return # of available bytes.
      to_byte = total_num_of_bytes - 1

    return ByteRange(from_byte, to_byte)

class MemoryCacheHTTPServer(SocketServer.ThreadingMixIn,
                            BaseHTTPServer.HTTPServer):
  # Increase the request queue size. The default value, 5, is set in
  # SocketServer.TCPServer (the parent of BaseHTTPServer.HTTPServer).
  # Since we're intercepting many domains through this single server,
  # it is quite possible to get more than 5 concurrent requests.
  request_queue_size = 128

  def __init__(self, host_port, handler, paths):
    BaseHTTPServer.HTTPServer.__init__(self, host_port, handler)
    self.resource_map = {}
    for path in paths:
      if os.path.isdir(path):
        self.AddDirectoryToResourceMap(path)
      else:
        self.AddFileToResourceMap(path)

  def AddDirectoryToResourceMap(self, cwd):
    """Loads all files in cwd into the in-memory resource map."""
    for root, dirs, files in os.walk(cwd):
      # Skip hidden files and folders (like .svn and .git).
      files = [f for f in files if f[0] != '.']
      dirs[:] = [d for d in dirs if d[0] != '.']

      for f in files:
        file_path = os.path.join(root, f)
        if not os.path.exists(file_path):  # Allow for '.#' files
          continue
        self.AddFileToResourceMap(file_path)

  def AddFileToResourceMap(self, file_path):
    """Loads file_path into the in-memory resource map."""
    with open(file_path, 'rb') as fd:
      response = fd.read()
      fs = os.fstat(fd.fileno())
      content_type = mimetypes.guess_type(file_path)[0]
      zipped = False
      if content_type in ['text/html', 'text/css', 'application/javascript']:
        zipped = True
        response = zlib.compress(response, 9)
      self.resource_map[file_path] = {
          'content-type': content_type,
          'content-length': len(response),
          'last-modified': fs.st_mtime,
          'response': response,
          'zipped': zipped
          }
      if file_path.endswith('/index.html'):
        self.resource_map[
            file_path[:-len('/index.html')]] = self.resource_map[file_path]

def Main():
  assert len(sys.argv) > 2, 'usage: %prog <port> [<path1>, <path2>, ...]'

  port = int(sys.argv[1])
  paths = sys.argv[2:]
  server_address = ('127.0.0.1', port)
  MemoryCacheHTTPRequestHandler.protocol_version = 'HTTP/1.1'
  httpd = MemoryCacheHTTPServer(server_address, MemoryCacheHTTPRequestHandler,
                                paths)
  httpd.serve_forever()

if __name__ == '__main__':
  Main()
<commit_msg>[Telemetry] Fix 2 bugs in memory_cache_http_server.<commit_after># Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import BaseHTTPServer
from collections import namedtuple
import mimetypes
import os
import SimpleHTTPServer
import SocketServer
import sys
import zlib

ByteRange = namedtuple('ByteRange', ['from_byte', 'to_byte'])
ResourceAndRange = namedtuple('ResourceAndRange', ['resource', 'byte_range'])

class MemoryCacheHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):

  def do_GET(self):
    """Serve a GET request."""
    resource_range = self.SendHead()

    if not resource_range or not resource_range.resource:
      return
    response = resource_range.resource['response']

    if not resource_range.byte_range:
      self.wfile.write(response)
      return

    start_index = resource_range.byte_range.from_byte
    end_index = resource_range.byte_range.to_byte
    self.wfile.write(response[start_index:end_index + 1])

  def do_HEAD(self):
    """Serve a HEAD request."""
    self.SendHead()

  def SendHead(self):
    path = self.translate_path(self.path)
    if path not in self.server.resource_map:
      self.send_error(404, 'File not found')
      return None

    resource = self.server.resource_map[path]
    total_num_of_bytes = resource['content-length']
    byte_range = self.GetByteRange(total_num_of_bytes)
    if byte_range:
      # request specified a range, so set response code to 206.
      self.send_response(206)
      self.send_header('Content-Range',
                       'bytes %d-%d/%d' % (byte_range.from_byte,
                                           byte_range.to_byte,
                                           total_num_of_bytes))
      total_num_of_bytes = byte_range.to_byte - byte_range.from_byte + 1
    else:
      self.send_response(200)

    self.send_header('Content-Length', str(total_num_of_bytes))
    self.send_header('Content-Type', resource['content-type'])
    self.send_header('Last-Modified',
                     self.date_time_string(resource['last-modified']))
    if resource['zipped']:
      self.send_header('Content-Encoding', 'deflate')
    self.end_headers()
    return ResourceAndRange(resource, byte_range)

  def GetByteRange(self, total_num_of_bytes):
    """Parse the header and get the range values specified.

    Args:
      total_num_of_bytes: Total # of bytes in requested resource,
      used to calculate upper range limit.
    Returns:
      A ByteRange namedtuple object with the requested byte-range values.
      If no Range is explicitly requested or there is a failure parsing,
      return None.
      Special case: If range specified is in the format "N-", return N-N.
      If upper range limit is greater than total # of bytes, return upper index.
    """

    range_header = self.headers.getheader('Range')
    if range_header is None:
      return None
    if not range_header.startswith('bytes='):
      return None

    # The range header is expected to be a string in this format:
    # bytes=0-1
    # Get the upper and lower limits of the specified byte-range.
    # We've already confirmed that range_header starts with 'bytes='.
    byte_range_values = range_header[len('bytes='):].split('-')
    from_byte = 0
    to_byte = 0

    if len(byte_range_values) == 2:
      from_byte = int(byte_range_values[0])
      if byte_range_values[1]:
        to_byte = int(byte_range_values[1])
    else:
      return None

    # Do some validation.
    if from_byte < 0:
      return None

    if to_byte < from_byte:
      to_byte = from_byte

    if to_byte >= total_num_of_bytes:
      # End of range requested is greater than length of requested resource.
      # Only return # of available bytes.
      to_byte = total_num_of_bytes - 1

    return ByteRange(from_byte, to_byte)

class MemoryCacheHTTPServer(SocketServer.ThreadingMixIn,
                            BaseHTTPServer.HTTPServer):
  # Increase the request queue size. The default value, 5, is set in
  # SocketServer.TCPServer (the parent of BaseHTTPServer.HTTPServer).
  # Since we're intercepting many domains through this single server,
  # it is quite possible to get more than 5 concurrent requests.
  request_queue_size = 128

  def __init__(self, host_port, handler, paths):
    BaseHTTPServer.HTTPServer.__init__(self, host_port, handler)
    self.resource_map = {}
    for path in paths:
      if os.path.isdir(path):
        self.AddDirectoryToResourceMap(path)
      else:
        self.AddFileToResourceMap(path)

  def AddDirectoryToResourceMap(self, cwd):
    """Loads all files in cwd into the in-memory resource map."""
    for root, dirs, files in os.walk(cwd):
      # Skip hidden files and folders (like .svn and .git).
      files = [f for f in files if f[0] != '.']
      dirs[:] = [d for d in dirs if d[0] != '.']

      for f in files:
        file_path = os.path.join(root, f)
        if not os.path.exists(file_path):  # Allow for '.#' files
          continue
        self.AddFileToResourceMap(file_path)

  def AddFileToResourceMap(self, file_path):
    """Loads file_path into the in-memory resource map."""
    with open(file_path, 'rb') as fd:
      response = fd.read()
      fs = os.fstat(fd.fileno())
      content_type = mimetypes.guess_type(file_path)[0]
      zipped = False
      if content_type in ['text/html', 'text/css', 'application/javascript']:
        zipped = True
        response = zlib.compress(response, 9)
      self.resource_map[file_path] = {
          'content-type': content_type,
          'content-length': len(response),
          'last-modified': fs.st_mtime,
          'response': response,
          'zipped': zipped
          }

      index = os.path.sep + 'index.html'
      if file_path.endswith(index):
        self.resource_map[
            file_path[:-len(index)]] = self.resource_map[file_path]

def Main():
  assert len(sys.argv) > 2, 'usage: %prog <port> [<path1>, <path2>, ...]'

  port = int(sys.argv[1])
  paths = sys.argv[2:]
  server_address = ('127.0.0.1', port)
  MemoryCacheHTTPRequestHandler.protocol_version = 'HTTP/1.1'
  httpd = MemoryCacheHTTPServer(server_address, MemoryCacheHTTPRequestHandler,
                                paths)
  httpd.serve_forever()

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


<commit_before># ^_^ coding: utf-8 ^_^

# Copyright 2015 Joaquín Muñoz M.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#    http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import tornado.web
import tornado.websocket

_handler_names = []
_handler_methods = {}
_handler_routes = {}

def route(route, websocket=False):
    def decorator(func):
        handler_name = func.__name__.capitalize()
        methods = func()
        if type(methods) not in [list, tuple]:
            raise Exception(u'Handler function must return a list or a tuple')

        if websocket:
            handler_name = 'WS' + handler_name

        _handler_names.append(handler_name)
        _handler_methods[handler_name] = methods
        _handler_routes[handler_name] = route

    return decorator

def app():
    handlers = []
    for name in _handler_names:
        handler_type = tornado.websocket.WebSocketHandler \
            if name.startswith('WS') else tornado.web.RequestHandler

        handler = type(
            name, (handler_type,),
            {method.__name__: method for method in _handler_methods[name]}
        )
        handlers.append((_handler_routes[name], handler))

    return tornado.web.Application(handlers)
<commit_msg>static urls weren't correctly defined (static_url was between quotes)<commit_after># ^_^ coding: utf-8 ^_^

# Copyright 2015 Joaquín Muñoz M.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#    http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import tornado.web
import tornado.websocket

_handler_names = []
_handler_methods = {}
_handler_routes = {}
_static_folders = {}

def route(route, websocket=False):
    def decorator(func):
        handler_name = func.__name__.capitalize()
        methods = func()
        if type(methods) not in [list, tuple]:
            raise Exception(u'Handler function must return a list or a tuple')

        if websocket:
            handler_name = 'WS' + handler_name

        _handler_names.append(handler_name)
        _handler_methods[handler_name] = methods
        _handler_routes[handler_name] = route

    return decorator

def add_static(static_url, static_folder):
    _static_folders[static_url] = static_folder

def app():
    handlers = []
    for name in _handler_names:
        handler_type = tornado.websocket.WebSocketHandler \
            if name.startswith('WS') else tornado.web.RequestHandler

        handler = type(
            name, (handler_type,),
            {method.__name__: method for method in _handler_methods[name]}
        )
        handlers.append((_handler_routes[name], handler))

    for url, folder in _static_folders.iteritems():
        handlers.append((url, tornado.web.StaticFileHandler, {'path': folder}))

    return tornado.web.Application(handlers)
<|endoftext|>


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

# This file is part of wger Workout Manager.
#
# wger Workout Manager is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# wger Workout Manager 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 Affero General Public License

import logging
import uuid

from django.template import RequestContext
from django.shortcuts import render_to_response
from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
from django.core.context_processors import csrf
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django.core.cache import cache
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy

from django.contrib.auth.decorators import login_required

from django.views.generic import DeleteView
from django.views.generic import UpdateView

from wger.manager.models import Workout
from wger.manager.models import WorkoutLog
from wger.manager.models import Schedule
from wger.manager.models import Day
from wger.manager.models import Setting
from wger.manager.forms import WorkoutForm
from wger.manager.forms import WorkoutCopyForm

from wger.utils.generic_views import WgerFormMixin
from wger.utils.generic_views import WgerDeleteMixin
from wger.utils.generic_views import WgerPermissionMixin
from wger.utils.cache import cache_mapper

logger = logging.getLogger('wger.custom')

# ************************
# Workout functions
# ************************
@login_required
def overview(request):
    '''
    An overview of all the user's workouts
    '''

    template_data = {}

    latest_workouts = Workout.objects.filter(user=request.user)
    (current_workout, schedule) = Schedule.objects.get_current_workout(request.user)
    template_data['workouts'] = latest_workouts
    template_data['current_workout'] = current_workout

    return render_to_response('workout/overview.html',
                              template_data,
                              context_instance=RequestContext(request))

@login_required
def view(request, id):
    '''
    Show the workout with the given ID
    '''
    template_data = {}
    workout = get_object_or_404(Workout, pk=id, user=request.user)
    canonical = workout.canonical_representation

    # Create the backgrounds that show what muscles the workout will work on
    muscles_front = []
    muscles_back = []
    for i in canonical['muscles']['front']:
        if not i in muscles_front:
            muscles_front.append('images/muscles/main/muscle-{0}.svg'.format(i))
    for i in canonical['muscles']['back']:
        if not i in muscles_back:
            muscles_back.append('images/muscles/main/muscle-{0}.svg'.format(i))

    # Append the silhouette of the human body as the last entry so the browser
    # renders it in the background
    muscles_front.append('images/muscles/muscular_system_front.svg')
    muscles_back.append('images/muscles/muscular_system_back.svg')

    template_data['workout'] = workout
    template_data['muscle_backgrounds_front'] = muscles_front
    template_data['muscle_backgrounds_back'] = muscles_back

    return render_to_response('workout/view.html',
                              template_data,
                              context_instance=RequestContext(request))

@login_required
def copy_workout(request, pk):
    '''
    Makes a copy of a workout
    '''

    workout = get_object_or_404(Workout, pk=pk, user=request.user)

    # Process request
    if request.method == 'POST':
        workout_form = WorkoutCopyForm(request.POST)

        if workout_form.is_valid():

            # Copy workout
            days = workout.day_set.all()

            workout_copy = workout
            workout_copy.pk = None
            workout_copy.comment = workout_form.cleaned_data['comment']
            workout_copy.save()

            # Copy the days
            for day in days:
                sets = day.set_set.all()

                day_copy = day
                days_of_week = [i for i in day.day.all()]
                day_copy.pk = None
                day_copy.training = workout_copy
                day_copy.save()
                for i in days_of_week:
                    day_copy.day.add(i)
                day_copy.save()

                # Copy the sets
                for current_set in sets:
                    current_set_id = current_set.id
                    exercises = current_set.exercises.all()

                    current_set_copy = current_set
                    current_set_copy.pk = None
                    current_set_copy.exerciseday = day_copy
                    current_set_copy.save()

                    # Exercises has Many2Many relationship
                    current_set_copy.exercises = exercises

                    # Go through the exercises
                    for exercise in exercises:
                        settings = exercise.setting_set.filter(set_id=current_set_id)

                        # Copy the settings
                        for setting in settings:
                            setting_copy = setting
                            setting_copy.pk = None
                            setting_copy.set = current_set_copy
                            setting_copy.save()

            return HttpResponseRedirect(reverse('workout-view',
                                                kwargs={'id': workout.id}))
    else:
        workout_form = WorkoutCopyForm({'comment': workout.comment})

        template_data = {}
        template_data.update(csrf(request))
        template_data['title'] = _('Copy workout')
        template_data['form'] = workout_form
        template_data['form_action'] = reverse('workout-copy', kwargs={'pk': workout.id})
        template_data['form_fields'] = [workout_form['comment']]
        template_data['submit_text'] = _('Copy')

        return render_to_response('form.html',
                                  template_data,
                                  context_instance=RequestContext(request))

@login_required
def add(request):
    '''
    Add a new workout and redirect to its page
    '''
    workout = Workout()
    workout.user = request.user
    workout.save()

    return HttpResponseRedirect(reverse('workout-view', kwargs={'id': workout.id}))

class WorkoutDeleteView(WgerDeleteMixin, DeleteView, WgerPermissionMixin):
    '''
    Generic view to delete a workout routine
    '''

    model = Workout
    success_url = reverse_lazy('workout-overview')
    title = ugettext_lazy('Delete workout')
    form_action_urlname = 'workout-delete'
    messages = ugettext_lazy('Workout was successfully deleted')
    login_required = True

class WorkoutEditView(WgerFormMixin, UpdateView, WgerPermissionMixin):
    '''
    Generic view to update an existing workout routine
    '''

    model = Workout
    form_class = WorkoutForm
    title = ugettext_lazy('Edit workout')
    form_action_urlname = 'workout-edit'
    login_required = True

class LastWeightHelper():
    '''
    Small helper class to retrieve the last workout log for a certain
    user, exercise and repetition combination.
    '''
    user = None
    last_weight_list = {}

    def __init__(self, user):
        self.user = user

    def get_last_weight(self, exercise, reps):
        '''
        Returns an emtpy string if no entry is found

        :param exercise:
        :param reps:
        :return: WorkoutLog or '' if none is found
        '''
        key = '{0}-{1}-{2}'.format(self.user.pk, exercise.pk, reps)

        if self.last_weight_list.get(key) is None:
            last_log = WorkoutLog.objects.filter(user=self.user,
                                                 exercise=exercise,
                                                 reps=reps).order_by('-date')
            weight = last_log[0].weight if last_log.exists() else ''
            self.last_weight_list[key] = weight

        return self.last_weight_list.get(key)

@login_required
def timer(request, day_pk):
    '''
    The timer view ("gym mode") for a workout
    '''

    day = get_object_or_404(Day, pk=day_pk, training__user=request.user)
    canonical_day = day.canonical_representation
    context = {}
    step_list = []
    last_log = LastWeightHelper(request.user)

    # Go through the workout day and create the individual 'pages'
    for set_dict in canonical_day['set_list']:

        if not set_dict['is_superset']:
            for exercise_dict in set_dict['exercise_list']:
                exercise = exercise_dict['obj']
                for reps in exercise_dict['setting_list']:
                    step_list.append({'current_step': uuid.uuid4().hex,
                                      'step_percent': 0,
                                      'step_nr': len(step_list) + 1,
                                      'exercise': exercise,
                                      'type': 'exercise',
                                      'reps': reps,
                                      'weight': last_log.get_last_weight(exercise, reps)})

                    step_list.append({'current_step': uuid.uuid4().hex,
                                      'step_percent': 0,
                                      'step_nr': len(step_list) + 1,
                                      'type': 'pause',
                                      'time': 90})

        # Supersets need extra work to group the exercises and reps together
        else:
            total_reps = len(set_dict['exercise_list'][0]['setting_list'])
            for i in range(0, total_reps):
                for exercise_dict in set_dict['exercise_list']:
                    reps = exercise_dict['setting_list'][i]
                    exercise = exercise_dict['obj']

                    step_list.append({'current_step': uuid.uuid4().hex,
                                      'step_percent': 0,
                                      'step_nr': len(step_list) + 1,
                                      'exercise': exercise,
                                      'type': 'exercise',
                                      'reps': reps,
                                      'weight': last_log.get_last_weight(exercise, reps)})

                step_list.append({'current_step': uuid.uuid4().hex,
                                  'step_percent': 0,
                                  'step_nr': len(step_list) + 1,
                                  'type': 'pause',
                                  'time': 90})

    # Remove the last pause step as it is not needed
    step_list.pop()

    # Go through the page list and calculate the correct value for step_percent
    for i, s in enumerate(step_list):
        step_list[i]['step_percent'] = (i + 1) * 100.0 / len(step_list)

    # Render template
    context['day'] = day
    context['step_list'] = step_list
    context['canonical_day'] = canonical_day
    context['workout'] = day.training
    return render_to_response('workout/timer.html',
                              context,
                              context_instance=RequestContext(request))
<commit_msg>Don't pop from empty step list, this fixes #55<commit_after># -*- coding: utf-8 -*-

# This file is part of wger Workout Manager.
#
# wger Workout Manager is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# wger Workout Manager 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 Affero General Public License

import logging
import uuid

from django.template import RequestContext
from django.shortcuts import render_to_response
from django.shortcuts import get_object_or_404
from django.http import HttpResponseRedirect
from django.core.context_processors import csrf
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django.core.cache import cache
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy

from django.contrib.auth.decorators import login_required

from django.views.generic import DeleteView
from django.views.generic import UpdateView

from wger.manager.models import Workout
from wger.manager.models import WorkoutLog
from wger.manager.models import Schedule
from wger.manager.models import Day
from wger.manager.models import Setting
from wger.manager.forms import WorkoutForm
from wger.manager.forms import WorkoutCopyForm

from wger.utils.generic_views import WgerFormMixin
from wger.utils.generic_views import WgerDeleteMixin
from wger.utils.generic_views import WgerPermissionMixin
from wger.utils.cache import cache_mapper

logger = logging.getLogger('wger.custom')

# ************************
# Workout functions
# ************************
@login_required
def overview(request):
    '''
    An overview of all the user's workouts
    '''

    template_data = {}

    latest_workouts = Workout.objects.filter(user=request.user)
    (current_workout, schedule) = Schedule.objects.get_current_workout(request.user)
    template_data['workouts'] = latest_workouts
    template_data['current_workout'] = current_workout

    return render_to_response('workout/overview.html',
                              template_data,
                              context_instance=RequestContext(request))

@login_required
def view(request, id):
    '''
    Show the workout with the given ID
    '''
    template_data = {}
    workout = get_object_or_404(Workout, pk=id, user=request.user)
    canonical = workout.canonical_representation

    # Create the backgrounds that show what muscles the workout will work on
    muscles_front = []
    muscles_back = []
    for i in canonical['muscles']['front']:
        if not i in muscles_front:
            muscles_front.append('images/muscles/main/muscle-{0}.svg'.format(i))
    for i in canonical['muscles']['back']:
        if not i in muscles_back:
            muscles_back.append('images/muscles/main/muscle-{0}.svg'.format(i))

    # Append the silhouette of the human body as the last entry so the browser
    # renders it in the background
    muscles_front.append('images/muscles/muscular_system_front.svg')
    muscles_back.append('images/muscles/muscular_system_back.svg')

    template_data['workout'] = workout
    template_data['muscle_backgrounds_front'] = muscles_front
    template_data['muscle_backgrounds_back'] = muscles_back

    return render_to_response('workout/view.html',
                              template_data,
                              context_instance=RequestContext(request))

@login_required
def copy_workout(request, pk):
    '''
    Makes a copy of a workout
    '''

    workout = get_object_or_404(Workout, pk=pk, user=request.user)

    # Process request
    if request.method == 'POST':
        workout_form = WorkoutCopyForm(request.POST)

        if workout_form.is_valid():

            # Copy workout
            days = workout.day_set.all()

            workout_copy = workout
            workout_copy.pk = None
            workout_copy.comment = workout_form.cleaned_data['comment']
            workout_copy.save()

            # Copy the days
            for day in days:
                sets = day.set_set.all()

                day_copy = day
                days_of_week = [i for i in day.day.all()]
                day_copy.pk = None
                day_copy.training = workout_copy
                day_copy.save()
                for i in days_of_week:
                    day_copy.day.add(i)
                day_copy.save()

                # Copy the sets
                for current_set in sets:
                    current_set_id = current_set.id
                    exercises = current_set.exercises.all()

                    current_set_copy = current_set
                    current_set_copy.pk = None
                    current_set_copy.exerciseday = day_copy
                    current_set_copy.save()

                    # Exercises has Many2Many relationship
                    current_set_copy.exercises = exercises

                    # Go through the exercises
                    for exercise in exercises:
                        settings = exercise.setting_set.filter(set_id=current_set_id)

                        # Copy the settings
                        for setting in settings:
                            setting_copy = setting
                            setting_copy.pk = None
                            setting_copy.set = current_set_copy
                            setting_copy.save()

            return HttpResponseRedirect(reverse('workout-view',
                                                kwargs={'id': workout.id}))
    else:
        workout_form = WorkoutCopyForm({'comment': workout.comment})

        template_data = {}
        template_data.update(csrf(request))
        template_data['title'] = _('Copy workout')
        template_data['form'] = workout_form
        template_data['form_action'] = reverse('workout-copy', kwargs={'pk': workout.id})
        template_data['form_fields'] = [workout_form['comment']]
        template_data['submit_text'] = _('Copy')

        return render_to_response('form.html',
                                  template_data,
                                  context_instance=RequestContext(request))

@login_required
def add(request):
    '''
    Add a new workout and redirect to its page
    '''
    workout = Workout()
    workout.user = request.user
    workout.save()

    return HttpResponseRedirect(reverse('workout-view', kwargs={'id': workout.id}))

class WorkoutDeleteView(WgerDeleteMixin, DeleteView, WgerPermissionMixin):
    '''
    Generic view to delete a workout routine
    '''

    model = Workout
    success_url = reverse_lazy('workout-overview')
    title = ugettext_lazy('Delete workout')
    form_action_urlname = 'workout-delete'
    messages = ugettext_lazy('Workout was successfully deleted')
    login_required = True

class WorkoutEditView(WgerFormMixin, UpdateView, WgerPermissionMixin):
    '''
    Generic view to update an existing workout routine
    '''

    model = Workout
    form_class = WorkoutForm
    title = ugettext_lazy('Edit workout')
    form_action_urlname = 'workout-edit'
    login_required = True

class LastWeightHelper():
    '''
    Small helper class to retrieve the last workout log for a certain
    user, exercise and repetition combination.
    '''
    user = None
    last_weight_list = {}

    def __init__(self, user):
        self.user = user

    def get_last_weight(self, exercise, reps):
        '''
        Returns an emtpy string if no entry is found

        :param exercise:
        :param reps:
        :return: WorkoutLog or '' if none is found
        '''
        key = '{0}-{1}-{2}'.format(self.user.pk, exercise.pk, reps)

        if self.last_weight_list.get(key) is None:
            last_log = WorkoutLog.objects.filter(user=self.user,
                                                 exercise=exercise,
                                                 reps=reps).order_by('-date')
            weight = last_log[0].weight if last_log.exists() else ''
            self.last_weight_list[key] = weight

        return self.last_weight_list.get(key)

@login_required
def timer(request, day_pk):
    '''
    The timer view ("gym mode") for a workout
    '''

    day = get_object_or_404(Day, pk=day_pk, training__user=request.user)
    canonical_day = day.canonical_representation
    context = {}
    step_list = []
    last_log = LastWeightHelper(request.user)

    # Go through the workout day and create the individual 'pages'
    for set_dict in canonical_day['set_list']:

        if not set_dict['is_superset']:
            for exercise_dict in set_dict['exercise_list']:
                exercise = exercise_dict['obj']
                for reps in exercise_dict['setting_list']:
                    step_list.append({'current_step': uuid.uuid4().hex,
                                      'step_percent': 0,
                                      'step_nr': len(step_list) + 1,
                                      'exercise': exercise,
                                      'type': 'exercise',
                                      'reps': reps,
                                      'weight': last_log.get_last_weight(exercise, reps)})

                    step_list.append({'current_step': uuid.uuid4().hex,
                                      'step_percent': 0,
                                      'step_nr': len(step_list) + 1,
                                      'type': 'pause',
                                      'time': 90})

        # Supersets need extra work to group the exercises and reps together
        else:
            total_reps = len(set_dict['exercise_list'][0]['setting_list'])
            for i in range(0, total_reps):
                for exercise_dict in set_dict['exercise_list']:
                    reps = exercise_dict['setting_list'][i]
                    exercise = exercise_dict['obj']

                    step_list.append({'current_step': uuid.uuid4().hex,
                                      'step_percent': 0,
                                      'step_nr': len(step_list) + 1,
                                      'exercise': exercise,
                                      'type': 'exercise',
                                      'reps': reps,
                                      'weight': last_log.get_last_weight(exercise, reps)})

                step_list.append({'current_step': uuid.uuid4().hex,
                                  'step_percent': 0,
                                  'step_nr': len(step_list) + 1,
                                  'type': 'pause',
                                  'time': 90})

    # Remove the last pause step as it is not needed. If the list is empty,
    # because the user didn't add any repetitions to any exercise, do nothing
    try:
        step_list.pop()
    except IndexError:
        pass

    # Go through the page list and calculate the correct value for step_percent
    for i, s in enumerate(step_list):
        step_list[i]['step_percent'] = (i + 1) * 100.0 / len(step_list)

    # Render template
    context['day'] = day
    context['step_list'] = step_list
    context['canonical_day'] = canonical_day
    context['workout'] = day.training
    return render_to_response('workout/timer.html',
                              context,
                              context_instance=RequestContext(request))
<|endoftext|>


<commit_before>#coding: utf-8

from wtdb import db

class WTTable:
    def __init__(self, tbname):
        self.tbname = tbname

    def addItem(self, idcolname=None, **values):
        id = db.insert(self.tbname, idcolname, **values)
        if idcolname:
            return id
        else:
            return None

    def updateItem(self, where, **values):
        db.update(self.tbname, where, **values)

    def deleteItem(self, where):
        db.delete(self.tbname, where)

    def searchItem(self, columns, where):
        items = db.select(self.tbname, what=columns, where=where)
        return items

class WTTipsTable(WTTable):
    def __init__(self, tbname):
        WTTable.__init__(self, tbname)
        self.id_col = 'tips_id'
        self.title_col = 'tips_title'
        self.content_col = 'tips_content'
        self.categoryid_col = 'category_id'

    def existTips(self, where):
        cur_tips = self.searchItem(
                columns='*',
                where=where
                )
        if len(cur_tips):
            tips_Exist = True
        else:
            tips_Exist = False
        return tips_Exist

    def deleteTipsByID(self, id):
        condition = self.id_col + '="' + str(id) + '"'
        exist_tips = self.existTips(where = condition)
        deleteSuccess = False
        if exist_tips:
            self.deleteItem(where = condition)
            deleteSuccess = True
        return deleteSuccess

    def getTipsByCategoryID(self, category_id):
        tips = self.searchItem(
                columns='*',
                where=self.categoryid_col + '="' + str(category_id) +'"'
                )
        tips_list = []
        if tips:
            tips_list = list(tips)
        return tips_list

    def addTips(self, new_tips):
        """
         new_tips: {'category_id':'X', 'title':'Y', 'content':'Z'}
        """
        values = {
                self.categoryid_col : new_tips['category_id'],
                self.title_col : new_tips['title'],
                self.content_col : new_tips['content']
                }

        id = self.addItem(
                self.id_col,
                **values
                )
        return id

    def updateTips(self, updated_tips):
        """
        Args:
            updated: {'id':'X', 'title':'Y', 'content':'Z'}
        """
        exist_tips = self.existTips(
                self.id_col + '="' + str(updated_tips['id']) + '"')

        updateSuccess = False
        if exist_tips:
            values = {
                    self.title_col : updated_tips['title'],
                    self.content_col : updated_tips['content']
                    }
            self.updateItem(
                    where=self.id_col + '="' + str(updated_tips['id']) + '"',
                    **values
                    )
            updateSuccess = True
        return updateSuccess

class WTCategoryTable(WTTable):
    def __init__(self, tbname):
        WTTable.__init__(self, tbname)
        self.id_col = 'category_id'
        self.name_col = 'category_name'
        self.userid_col = 'user_id'

    def getCategoryIDByName(self, category_name):
        ids = self.searchItem(
            columns=self.id_col,
            where=self.name_col + '="' + str(category_name) + '"'
            )
        idlist = []
        if ids:
            idlist = list(ids)

        id = None
        if idlist:
            id = idlist[0][self.id_col]
        return id

tips = WTTipsTable('wt_tips')
category = WTCategoryTable('wt_category')
<commit_msg>fix style error to pass pep8<commit_after>#coding: utf-8

from wtdb import db

class WTTable:
    def __init__(self, tbname):
        self.tbname = tbname

    def addItem(self, idcolname=None, **values):
        id = db.insert(self.tbname, idcolname, **values)
        if idcolname:
            return id
        else:
            return None

    def updateItem(self, where, **values):
        db.update(self.tbname, where, **values)

    def deleteItem(self, where):
        db.delete(self.tbname, where)

    def searchItem(self, columns, where):
        items = db.select(self.tbname, what=columns, where=where)
        return items

class WTTipsTable(WTTable):
    def __init__(self, tbname):
        WTTable.__init__(self, tbname)
        self.id_col = 'tips_id'
        self.title_col = 'tips_title'
        self.content_col = 'tips_content'
        self.categoryid_col = 'category_id'

    def existTips(self, where):
        cur_tips = self.searchItem(
                columns='*',
                where=where
                )
        if len(cur_tips):
            tips_Exist = True
        else:
            tips_Exist = False
        return tips_Exist

    def deleteTipsByID(self, id):
        condition = self.id_col + '="' + str(id) + '"'
        exist_tips = self.existTips(where=condition)
        deleteSuccess = False
        if exist_tips:
            self.deleteItem(where=condition)
            deleteSuccess = True
        return deleteSuccess

    def getTipsByCategoryID(self, category_id):
        tips = self.searchItem(
                columns='*',
                where=self.categoryid_col + '="' + str(category_id) + '"'
                )
        tips_list = []
        if tips:
            tips_list = list(tips)
        return tips_list

    def addTips(self, new_tips):
        """
         new_tips: {'category_id':'X', 'title':'Y', 'content':'Z'}
        """
        values = {
                self.categoryid_col: new_tips['category_id'],
                self.title_col: new_tips['title'],
                self.content_col: new_tips['content']
                }

        id = self.addItem(
                self.id_col,
                **values
                )
        return id

    def updateTips(self, updated_tips):
        """
        Args:
            updated: {'id':'X', 'title':'Y', 'content':'Z'}
        """
        exist_tips = self.existTips(
                self.id_col + '="' + str(updated_tips['id']) + '"')

        updateSuccess = False
        if exist_tips:
            values = {
                    self.title_col: updated_tips['title'],
                    self.content_col: updated_tips['content']
                    }
            self.updateItem(
                    where=self.id_col + '="' + str(updated_tips['id']) + '"',
                    **values
                    )
            updateSuccess = True
        return updateSuccess

class WTCategoryTable(WTTable):
    def __init__(self, tbname):
        WTTable.__init__(self, tbname)
        self.id_col = 'category_id'
        self.name_col = 'category_name'
        self.userid_col = 'user_id'

    def getCategoryIDByName(self, category_name):
        ids = self.searchItem(
            columns=self.id_col,
            where=self.name_col + '="' + str(category_name) + '"'
            )
        idlist = []
        if ids:
            idlist = list(ids)

        id = None
        if idlist:
            id = idlist[0][self.id_col]
        return id

tips = WTTipsTable('wt_tips')
category = WTCategoryTable('wt_category')
<|endoftext|>


<commit_before>#__BEGIN_LICENSE__
# Copyright (c) 2015, United States Government, as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All rights reserved.
#
# The xGDS platform is licensed under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0.
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#__END_LICENSE__

"""
This app may define some new parameters that can be modified in the
Django settings module.  Let's say one such parameter is FOO.  The
default value for FOO is defined in this file, like this:

  FOO = 'my default value'

If the admin for the site doesn't like the default value, they can
override it in the site-level settings module, like this:

  FOO = 'a better value'

Other modules can access the value of FOO like this:

  from django.conf import settings
  print settings.FOO

Don't try to get the value of FOO from django.conf.settings.  That
settings object will not know about the default value!
"""

XGDS_NOTES_MONIKER = 'Notes' # Sometimes we call them Console Log
XGDS_NOTES_ALLOW_MAPPING = True
XGDS_NOTES_ENABLE_GEOCAM_TRACK_MAPPING = True

XGDS_NOTES_NOTE_MODEL = 'xgds_notes2.LocatedNote'
XGDS_NOTES_TAG_MODEL = 'xgds_notes2.HierarchichalTag'
XGDS_NOTES_TAGGED_NOTE_MODEL = 'xgds_notes2.TaggedNote'

XGDS_NOTES_USER_SESSION_MODEL = 'xgds_notes2.UserSession'

XGDS_NOTES_POPULATE_NOTE_DATA = 'xgds_notes2.views.populateNoteData'

XGDS_NOTES_REVIEW_DEFAULT_SORT = '-event_time'

# If the default sort is not adequate, you can define a function to do your sort order for review.
# It currently takes no parameters.
XGDS_NOTES_SORT_FUNCTION = None

# Override this in your application with the path to a django_filters.FilterSet subclass to customise the available filters.
XGDS_NOTES_REVIEW_FILTER_CLASS = None

# All timestamps in xgds_notes data tables should always use the UTC
# time zone.  XGDS_NOTES_OPS_TIME_ZONE is currently used only to
# choose how to split up days in the daily notes index. We split at
# midnight in the specified time zone. Since ops are usually idle at
# night and we want to split during the idle period, we usually set this
# to the time zone where most ops actually occur.
# note this duplicates functionality in GEOCAM_TRACK so in your siteSettings you might set
# XGDS_NOTES_OPS_TIME_ZONE = GEOCAM_TRACK_OPS_TIME_ZONE
XGDS_NOTES_OPS_TIME_ZONE = 'UTC'

XGDS_NOTES_KML_EXPORT = False

XGDS_NOTES_NOTES_CHANNEL = 'live/notes'

XGDS_NOTES_TAG_TREE_URL = '/notes/tagsTree/'

# These are the columns that will show by default for all note tables.
XGDS_NOTES_TABLE_DEFAULT_COLUMNS = ['event_time', 'author', 'content', 'tags']

XGDS_NOTES_BOWER_INSTALLED_APPS = ('moment',
                                   'lodash#3.10.0',
                                   'fancytree=fancytree',
                                   'jquery-ui-contextmenu=https://github.com/mar10/jquery-ui-contextmenu.git',
                                   'bootstrap-tagsinput',
                                   'typeahead.js',
                                   'datatables-sorting-datetime-moment'
                                   )
<commit_msg>use getOrCreate for settings objects<commit_after>#__BEGIN_LICENSE__
# Copyright (c) 2015, United States Government, as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All rights reserved.
#
# The xGDS platform is licensed under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0.
#
# Unless required by applicable law or agreed to in writing, software distributed
# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
# CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
#__END_LICENSE__

from geocamUtil.SettingsUtil import getOrCreateDict, getOrCreateArray

"""
This app may define some new parameters that can be modified in the
Django settings module.  Let's say one such parameter is FOO.  The
default value for FOO is defined in this file, like this:

  FOO = 'my default value'

If the admin for the site doesn't like the default value, they can
override it in the site-level settings module, like this:

  FOO = 'a better value'

Other modules can access the value of FOO like this:

  from django.conf import settings
  print settings.FOO

Don't try to get the value of FOO from django.conf.settings.  That
settings object will not know about the default value!
"""

XGDS_NOTES_MONIKER = 'Notes' # Sometimes we call them Console Log
XGDS_NOTES_ALLOW_MAPPING = True
XGDS_NOTES_ENABLE_GEOCAM_TRACK_MAPPING = True

XGDS_NOTES_NOTE_MODEL = 'xgds_notes2.LocatedNote'
XGDS_NOTES_TAG_MODEL = 'xgds_notes2.HierarchichalTag'
XGDS_NOTES_TAGGED_NOTE_MODEL = 'xgds_notes2.TaggedNote'

XGDS_NOTES_USER_SESSION_MODEL = 'xgds_notes2.UserSession'

XGDS_NOTES_POPULATE_NOTE_DATA = 'xgds_notes2.views.populateNoteData'

XGDS_NOTES_REVIEW_DEFAULT_SORT = '-event_time'

# If the default sort is not adequate, you can define a function to do your sort order for review.
# It currently takes no parameters.
XGDS_NOTES_SORT_FUNCTION = None

# Override this in your application with the path to a django_filters.FilterSet subclass to customise the available filters.
XGDS_NOTES_REVIEW_FILTER_CLASS = None

# All timestamps in xgds_notes data tables should always use the UTC
# time zone.  XGDS_NOTES_OPS_TIME_ZONE is currently used only to
# choose how to split up days in the daily notes index. We split at
# midnight in the specified time zone. Since ops are usually idle at
# night and we want to split during the idle period, we usually set this
# to the time zone where most ops actually occur.
# note this duplicates functionality in GEOCAM_TRACK so in your siteSettings you might set
# XGDS_NOTES_OPS_TIME_ZONE = GEOCAM_TRACK_OPS_TIME_ZONE
XGDS_NOTES_OPS_TIME_ZONE = 'UTC'

XGDS_NOTES_KML_EXPORT = False

XGDS_NOTES_NOTES_CHANNEL = 'live/notes'

XGDS_NOTES_TAG_TREE_URL = '/notes/tagsTree/'

# These are the columns that will show by default for all note tables.
XGDS_NOTES_TABLE_DEFAULT_COLUMNS = ['event_time', 'author', 'content', 'tags']

BOWER_INSTALLED_APPS = getOrCreateArray('BOWER_INSTALLED_APPS')
BOWER_INSTALLED_APPS += ['moment',
                         'lodash#3.10.0',
                         'fancytree=fancytree',
                         'jquery-ui-contextmenu=https://github.com/mar10/jquery-ui-contextmenu.git',
                         'bootstrap-tagsinput',
                         'typeahead.js',
                         'datatables-sorting-datetime-moment'
                         ]

XGDS_MAP_SERVER_JS_MAP = getOrCreateDict('XGDS_MAP_SERVER_JS_MAP')
XGDS_MAP_SERVER_JS_MAP['Note'] = {'ol': 'xgds_notes2/js/olNoteMap.js',
                                      'model': XGDS_NOTES_NOTE_MODEL,
                                      'hiddenColumns': []}

XGDS_DATA_IMPORTS = getOrCreateDict('XGDS_DATA_IMPORTS')
XGDS_DATA_IMPORTS[XGDS_NOTES_MONIKER] = '/notes/import'

XGDS_DATA_MASKED_FIELDS = getOrCreateDict('XGDS_DATA_MASKED_FIELDS')
XGDS_DATA_MASKED_FIELDS['xgds_notes'] = {'Note': ['uuid',
                                                  'draft',
                                                  'event_time',
                                                  'creation_time',
                                                  'modification_time',
                                                  'console_position',
                                                  'tagged_items',
                                                  'asset_position',
                                                  'new_asset_position',
                                                  'still_frame',
                                                  ]
                                        }

# XGDS_DATA_EXPAND_RELATED = getOrCreateDict('XGDS_DATA_EXPAND_RELATED')
# XGDS_DATA_EXPAND_RELATED['xgds_notes'] = {  'Note': [('asset_position', 'depth', 'Depth'),
#                                                      ('new_asset_position', 'depthMeters', 'New Depth'),
#                                                      ('tags', 'all', 'Tags'),
#                                                      ],
#
#                                             }<|endoftext|>


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

from __future__ import print_function

import io
import time
import logging
import threading
import multiprocessing

from configparser import ConfigParser

try:
    import uwsgi
except ImportError:
    uwsgi = None

from isso.compat import PY2K

if PY2K:
    import thread
else:
    import _thread as thread

from isso.utils import parse
from isso.compat import text_type as str

from werkzeug.contrib.cache import NullCache, SimpleCache

logger = logging.getLogger("isso")

class Section:

    def __init__(self, conf, section):
        self.conf = conf
        self.section = section

    def get(self, key):
        return self.conf.get(self.section, key)

    def getint(self, key):
        return self.conf.getint(self.section, key)

    def getlist(self, key):
        return self.conf.getlist(self.section, key)

    def getiter(self, key):
        return self.conf.getiter(self.section, key)

    def getboolean(self, key):
        return self.conf.getboolean(self.section, key)

class IssoParser(ConfigParser):
    """
    Extended :class:`ConfigParser` to parse human-readable timedeltas
    into seconds and handles multiple values per key.

    >>> import io
    >>> parser = IssoParser(allow_no_value=True)
    >>> parser.read_file(io.StringIO(u'''
    ... [foo]
    ... bar = 1h
    ... baz = 12
    ... spam = a, b, cdef
    ... bla =
    ...     spam
    ...     ham
    ... asd = fgh
    ... '''))
    >>> parser.getint("foo", "bar")
    3600
    >>> parser.getint("foo", "baz")
    12
    >>> parser.getlist("foo", "spam")  # doctest: +IGNORE_UNICODE
    ['a', 'b', 'cdef']
    >>> list(parser.getiter("foo", "bla"))  # doctest: +IGNORE_UNICODE
    ['spam', 'ham']
    >>> list(parser.getiter("foo", "asd"))  # doctest: +IGNORE_UNICODE
    ['fgh']
    """

    @classmethod
    def _total_seconds(cls, td):
        return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6

    def getint(self, section, key):
        try:
            delta = parse.timedelta(self.get(section, key))
        except ValueError:
            return super(IssoParser, self).getint(section, key)
        else:
            try:
                return int(delta.total_seconds())
            except AttributeError:
                return int(IssoParser._total_seconds(delta))

    def getlist(self, section, key):
        return list(map(str.strip, self.get(section, key).split(',')))

    def getiter(self, section, key):
        for item in map(str.strip, self.get(section, key).split('\n')):
            if item:
                yield item

    def section(self, section):
        return Section(self, section)

class Config:

    default = [
        "[general]",
        "name = ",
        "dbpath = /tmp/isso.db",
        "host = ",
        "max-age = 15m",
        "notify = ",
        "[moderation]",
        "enabled = false",
        "purge-after = 30d",
        "[server]",
        "listen = http://localhost:8080/",
        "reload = off", "profile = off",
        "[smtp]",
        "username = ", "password = ",
        "host = localhost", "port = 587", "security = starttls",
        "to = ", "from = ",
        "timeout = 10",
        "[guard]",
        "enabled = true",
        "ratelimit = 2",
        "direct-reply = 3",
        "reply-to-self = false",
        "[markup]",
        "options = strikethrough, superscript, autolink",
        "allowed-elements = ",
        "allowed-attributes = "
    ]

    @classmethod
    def load(cls, configfile):

        # return set of (section, option)
        setify = lambda cp: set((section, option) for section in cp.sections()
                                for option in cp.options(section))

        rv = IssoParser(allow_no_value=True)
        rv.read_file(io.StringIO(u'\n'.join(Config.default)))

        a = setify(rv)

        if configfile:
            rv.read(configfile)

        diff = setify(rv).difference(a)

        if diff:
            for item in diff:
                logger.warn("no such option: [%s] %s", *item)
                if item in (("server", "host"), ("server", "port")):
                    logger.warn("use `listen = http://$host:$port` instead")
                if item == ("smtp", "ssl"):
                    logger.warn("use `security = none | starttls | ssl` instead")
                if item == ("general", "session-key"):
                    logger.info("Your `session-key` has been stored in the "
                                "database itself, this option is now unused")

        return rv

class Cache:
    """Wrapper around werkzeug's cache class, to make it compatible to
    uWSGI's cache framework.
    """

    def __init__(self, cache):
        self.cache = cache

    def get(self, cache, key):
        return self.cache.get(key)

    def set(self, cache, key, value):
        return self.cache.set(key, value)

    def delete(self, cache, key):
        return self.cache.delete(key)

class Mixin(object):

    def __init__(self, conf):
        self.lock = threading.Lock()
        self.cache = Cache(NullCache())

    def notify(self, subject, body, retries=5):
        pass

def threaded(func):
    """
    Decorator to execute each :param func: call in a separate thread.
    """

    def dec(self, *args, **kwargs):
        thread.start_new_thread(func, (self, ) + args, kwargs)

    return dec

class ThreadedMixin(Mixin):

    def __init__(self, conf):

        super(ThreadedMixin, self).__init__(conf)

        if conf.getboolean("moderation", "enabled"):
            self.purge(conf.getint("moderation", "purge-after"))

        self.cache = Cache(SimpleCache(threshold=1024, default_timeout=3600))

    @threaded
    def purge(self, delta):
        while True:
            with self.lock:
                self.db.comments.purge(delta)
            time.sleep(delta)

class ProcessMixin(ThreadedMixin):

    def __init__(self, conf):

        super(ProcessMixin, self).__init__(conf)
        self.lock = multiprocessing.Lock()

class uWSGICache(object):
    """Uses uWSGI Caching Framework. INI configuration:

    .. code-block:: ini

        cache2 = name=hash,items=1024,blocksize=32

    """

    @classmethod
    def get(self, cache, key):
        return uwsgi.cache_get(key, cache)

    @classmethod
    def set(self, cache, key, value):
        uwsgi.cache_set(key, value, 3600, cache)

    @classmethod
    def delete(self, cache, key):
        uwsgi.cache_del(key, cache)

class uWSGIMixin(Mixin):

    def __init__(self, conf):

        super(uWSGIMixin, self).__init__(conf)

        self.lock = multiprocessing.Lock()
        self.cache = uWSGICache

        timedelta = conf.getint("moderation", "purge-after")
        purge = lambda signum: self.db.comments.purge(timedelta)
        uwsgi.register_signal(1, "", purge)
        uwsgi.add_timer(1, timedelta)

        # run purge once
        purge(1)
<commit_msg>remove superscript extension from Markdown<commit_after># -*- encoding: utf-8 -*-

from __future__ import print_function

import io
import time
import logging
import threading
import multiprocessing

from configparser import ConfigParser

try:
    import uwsgi
except ImportError:
    uwsgi = None

from isso.compat import PY2K

if PY2K:
    import thread
else:
    import _thread as thread

from isso.utils import parse
from isso.compat import text_type as str

from werkzeug.contrib.cache import NullCache, SimpleCache

logger = logging.getLogger("isso")

class Section:

    def __init__(self, conf, section):
        self.conf = conf
        self.section = section

    def get(self, key):
        return self.conf.get(self.section, key)

    def getint(self, key):
        return self.conf.getint(self.section, key)

    def getlist(self, key):
        return self.conf.getlist(self.section, key)

    def getiter(self, key):
        return self.conf.getiter(self.section, key)

    def getboolean(self, key):
        return self.conf.getboolean(self.section, key)

class IssoParser(ConfigParser):
    """
    Extended :class:`ConfigParser` to parse human-readable timedeltas
    into seconds and handles multiple values per key.

    >>> import io
    >>> parser = IssoParser(allow_no_value=True)
    >>> parser.read_file(io.StringIO(u'''
    ... [foo]
    ... bar = 1h
    ... baz = 12
    ... spam = a, b, cdef
    ... bla =
    ...     spam
    ...     ham
    ... asd = fgh
    ... '''))
    >>> parser.getint("foo", "bar")
    3600
    >>> parser.getint("foo", "baz")
    12
    >>> parser.getlist("foo", "spam")  # doctest: +IGNORE_UNICODE
    ['a', 'b', 'cdef']
    >>> list(parser.getiter("foo", "bla"))  # doctest: +IGNORE_UNICODE
    ['spam', 'ham']
    >>> list(parser.getiter("foo", "asd"))  # doctest: +IGNORE_UNICODE
    ['fgh']
    """

    @classmethod
    def _total_seconds(cls, td):
        return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6

    def getint(self, section, key):
        try:
            delta = parse.timedelta(self.get(section, key))
        except ValueError:
            return super(IssoParser, self).getint(section, key)
        else:
            try:
                return int(delta.total_seconds())
            except AttributeError:
                return int(IssoParser._total_seconds(delta))

    def getlist(self, section, key):
        return list(map(str.strip, self.get(section, key).split(',')))

    def getiter(self, section, key):
        for item in map(str.strip, self.get(section, key).split('\n')):
            if item:
                yield item

    def section(self, section):
        return Section(self, section)

class Config:

    default = [
        "[general]",
        "name = ",
        "dbpath = /tmp/isso.db",
        "host = ",
        "max-age = 15m",
        "notify = ",
        "[moderation]",
        "enabled = false",
        "purge-after = 30d",
        "[server]",
        "listen = http://localhost:8080/",
        "reload = off", "profile = off",
        "[smtp]",
        "username = ", "password = ",
        "host = localhost", "port = 587", "security = starttls",
        "to = ", "from = ",
        "timeout = 10",
        "[guard]",
        "enabled = true",
        "ratelimit = 2",
        "direct-reply = 3",
        "reply-to-self = false",
        "[markup]",
        "options = strikethrough, autolink",
        "allowed-elements = ",
        "allowed-attributes = "
    ]

    @classmethod
    def load(cls, configfile):

        # return set of (section, option)
        setify = lambda cp: set((section, option) for section in cp.sections()
                                for option in cp.options(section))

        rv = IssoParser(allow_no_value=True)
        rv.read_file(io.StringIO(u'\n'.join(Config.default)))

        a = setify(rv)

        if configfile:
            rv.read(configfile)

        diff = setify(rv).difference(a)

        if diff:
            for item in diff:
                logger.warn("no such option: [%s] %s", *item)
                if item in (("server", "host"), ("server", "port")):
                    logger.warn("use `listen = http://$host:$port` instead")
                if item == ("smtp", "ssl"):
                    logger.warn("use `security = none | starttls | ssl` instead")
                if item == ("general", "session-key"):
                    logger.info("Your `session-key` has been stored in the "
                                "database itself, this option is now unused")

        return rv

class Cache:
    """Wrapper around werkzeug's cache class, to make it compatible to
    uWSGI's cache framework.
    """

    def __init__(self, cache):
        self.cache = cache

    def get(self, cache, key):
        return self.cache.get(key)

    def set(self, cache, key, value):
        return self.cache.set(key, value)

    def delete(self, cache, key):
        return self.cache.delete(key)

class Mixin(object):

    def __init__(self, conf):
        self.lock = threading.Lock()
        self.cache = Cache(NullCache())

    def notify(self, subject, body, retries=5):
        pass

def threaded(func):
    """
    Decorator to execute each :param func: call in a separate thread.
    """

    def dec(self, *args, **kwargs):
        thread.start_new_thread(func, (self, ) + args, kwargs)

    return dec

class ThreadedMixin(Mixin):

    def __init__(self, conf):

        super(ThreadedMixin, self).__init__(conf)

        if conf.getboolean("moderation", "enabled"):
            self.purge(conf.getint("moderation", "purge-after"))

        self.cache = Cache(SimpleCache(threshold=1024, default_timeout=3600))

    @threaded
    def purge(self, delta):
        while True:
            with self.lock:
                self.db.comments.purge(delta)
            time.sleep(delta)

class ProcessMixin(ThreadedMixin):

    def __init__(self, conf):

        super(ProcessMixin, self).__init__(conf)
        self.lock = multiprocessing.Lock()

class uWSGICache(object):
    """Uses uWSGI Caching Framework. INI configuration:

    .. code-block:: ini

        cache2 = name=hash,items=1024,blocksize=32

    """

    @classmethod
    def get(self, cache, key):
        return uwsgi.cache_get(key, cache)

    @classmethod
    def set(self, cache, key, value):
        uwsgi.cache_set(key, value, 3600, cache)

    @classmethod
    def delete(self, cache, key):
        uwsgi.cache_del(key, cache)

class uWSGIMixin(Mixin):

    def __init__(self, conf):

        super(uWSGIMixin, self).__init__(conf)

        self.lock = multiprocessing.Lock()
        self.cache = uWSGICache

        timedelta = conf.getint("moderation", "purge-after")
        purge = lambda signum: self.db.comments.purge(timedelta)
        uwsgi.register_signal(1, "", purge)
        uwsgi.add_timer(1, timedelta)

        # run purge once
        purge(1)
<|endoftext|>