<commit_before>#!/usr/bin/python
"""
Sketchcode outline of task list manager for Kamaelia (and software in general)
"""

class dashboard(object):
    status = "(Started|Running|Completed|Dropped|Stasis|Blocked)"
    status_text = "Associated single sentence (eg why blocked)"
    currentdevelopers = "you!"
    devlocation = "Normally /Sketches/ initially"
    startdate = "date"
    milestonedate ="date"
    milestonetag = "(met|slipped|missed)"
    expectedenddate = "(date|n/a)"
    enddate = "date"
    dateupdated = "date"
    estimatedeffortsofar = "int"

class update(object):
    what = ""
    who = "name"
    date = "date"
    timespent = "in 1/4 days"
    output = "if anything"
    statuschange = "if appropriate"

class descripton(object):
    goal = "Short one line of what the task is designed to achieve/create."
    result = "A practical, clear result of what will be possible as a result of achieving this task. This is best described in the case of a user story."
    context = "The context in which this task sits. Has this task any history? Is it the result of any previous tasks - either within the project or outside."
    benefits = "What benefits will be gained by working on this task, and achieving its goals? Speculative as well as certained/realistically expected benefits are valid here."

class requirement(object):
    reqtype = "(MUST|SHOULD|MAY|WOULDLIKE)"
    whofrom = "name"
    what = "string describing the requirement"

class inputs(object):
    tasksponsor = "WHO is the sponsor - (can be main developer)"
    taskowner = "WHO is the owner - (likely to be main developer)"
    developers = list("name") # (if empty list, assert task.dashboard.status == "stasis"
    users = list( "name" )
    interestedparties = list( "name" )
    requirements = list ( [ requirement ] )
    influencingfactors = list ( "strings" )

class output(object):
    outputtype = "(code|presentation|documentation)"
    location = list ( "string" )

class outputs(object):
    expected = list ( "strings" )
    actual = list ([ output ])
    arisingpossilities = list ( "Realistic possibility arising as a result of activity on this task" )

class microtask(object):
    description = "subtasks as bullet points with the sort of information you'd put on a project task page, but for which it seems overkill to create a project task page for."

class relatedtasks(object):
    tasksenablingthis = list ( "Task" )
    subtasks = list  ([microtask , "Task"])
    cotasks = list ( "Task" )

class comment(object):
    who = "name"
    when = "timedate"
    what = "string"

class Task(object):
    taskid = int
    descripton
    dashboard
    inputs
    outputs
    relatedtasks
    tasklog = list ([ update ])
    discussion = list ([ comment ])
    consolidateddiscussion = "string"
<commit_msg>Initial version that has a functional data model<commit_after>#!/usr/bin/python
"""
Sketchcode outline of task list manager for Kamaelia (and software in general)
"""

import shelve
import pprint

# -- Conceptual------------------------------------------------
class _spec_descripton(object):
    "# added "
    goal = "Short one line of what the task is designed to achieve/create."
    result = "A practical, clear result of what will be possible as a result of achieving this task. This is best described in the case of a user story."
    context = "The context in which this task sits. Has this task any history? Is it the result of any previous tasks - either within the project or outside."
    benefits = "What benefits will be gained by working on this task, and achieving its goals? Speculative as well as certained/realistically expected benefits are valid here."

class _spec_dashboard(object):
    "# added "
    status = "(Started|Running|Completed|Dropped|Stasis|Blocked)"
    status_text = "Associated single sentence (eg why blocked)"
    currentdevelopers = "you!"
    devlocation = "Normally /Sketches/ initially"
    startdate = "date"
    milestonedate ="date"
    milestonetag = "(met|slipped|missed)"
    expectedenddate = "(date|n/a)"
    enddate = "date"
    dateupdated = "date"
    estimatedeffortsofar = "int"

class _spec_comment(object):
    "# added "
    who = "name"
    when = "timedate"
    what = "string"

class _spec_update(object):
    what = ""
    who = "name"
    date = "date"
    timespent = "in 1/4 days"
    output = "if anything"
    statuschange = "if appropriate"

class _spec_requirement(object):
    "# added "
    reqtype = "(MUST|SHOULD|MAY|WOULDLIKE)"
    whofrom = "name"
    what = "string describing the requirement"

class _spec_inputs(object):
    "# added "
    tasksponsor = "WHO is the sponsor - (can be main developer)"
    taskowner = "WHO is the owner - (likely to be main developer)"
    developers = list("name") # (if empty list, assert task.dashboard.status == "stasis"
    users = list( "name" )
    interestedparties = list( "name" )
    requirements = list ( [ _spec_requirement ] )
    influencingfactors = list ( "strings" )

class _spec_output(object):
    "# added "
    outputtype = "(code|presentation|documentation)"
    location = list ( "string" )

class _spec_outputs(object):
    "# added "
    expected = list ( "strings" )
    actual = list ([ _spec_output ])
    arisingpossilities = list ( "Realistic possibility arising as a result of activity on this task" )

class _spec_microtask(object):
    "# added "
    description = "subtasks as bullet points with the sort of information you'd put on a project task page, but for which it seems overkill to create a project task page for."

class _spec_relatedtasks(object):
    "# added "
    tasksenablingthis = list ( "Task" )
    subtasks = list  ([_spec_microtask , "Task"])
    cotasks = list ( "Task" )

class _spec_Task(object):
    "# added "
    taskid = int
    _spec_descripton
    _spec_dashboard
    _spec_inputs
    _spec_outputs
    _spec_relatedtasks
    tasklog = list ([ _spec_update ])
    discussion = list ([ _spec_comment ])
    consolidateddiscussion = "string"

# -- Concrete ------------------------------------------------
class Update(object):
    def __init__(self, what, who, when, timespent, output, statuschange):
        self.what = what
        self.who = who
        self.when = when
        self.timespent = timespent
        self.output = output
        self.statuschange = statuschange
    def asdict(self):
        return {
            "what" : self.what,
            "who" : self.who,
            "date" : self.when,
            "timespent" : self.timespent,
            "output" : self.output,
            "statuschange" : self.statuschange,
        }

class Comment(object):
    def __init__(self, who, when , what ):
        self.who = who
        self.when = when
        self.what = what
    def asdict(self):
        return {
            "who" : self.who,
            "when" : self.when,
            "what" : self.what,
        }

class Dashboard(object):
    def __init__(self):
        self.status = ""
        self.status_text = ""
        self.currentdevelopers = ""
        self.devlocation = ""
        self.startdate = ""
        self.milestonedate = ""
        self.milestonetag = ""
        self.expectedenddate = ""
        self.enddate = ""
        self.dateupdated = ""
        self.estimatedeffortsofar = ""

    def asdict(self):
        return {
            "status": self.status,
            "status_text": self.status_text,
            "currentdevelopers": self.currentdevelopers,
            "devlocation": self.devlocation,
            "startdate": self.startdate,
            "milestonedate": self.milestonedate,
            "milestonetag": self.milestonetag,
            "expectedenddate": self.expectedenddate,
            "enddate": self.enddate,
            "dateupdated": self.dateupdated,
            "estimatedeffortsofar": self.estimatedeffortsofar,
        }

class Descripton(object):
    def __init__(self):
        self.goal = ""
        self.result = ""
        self.context = ""
        self.benefits = []

    def asdict(self):
        return {
            "goal": self.goal,
            "result" : self.result,
            "context" : self.context,
            "benefits" : self.benefits,
        }

class Requirement(object):
    def __init__(self, reqtype, who, what, when=None, by=None, why=None):
        self.reqtype = reqtype
        self.who = who
        self.what = what
        self.when = when
        self.by = by
        self.why = why
    def asdict(self):
        return {
            "reqtype" : self.reqtype,
            "who" : self.who,
            "what" : self.what,
            "when" : self.when,
            "by" : self.by,
            "why" : self.why,
        }

class Inputs(object):
    def __init__(self):
        self.tasksponsor = ""
        self.taskowner = ""
        self.developers = []
        self.users = []
        self.interestedparties = []
        self.influencingfactors = []
        self.requirements = []
    def asdict(self):
        return {
            "tasksponsor" : self.tasksponsor,
            "taskowner" : self.taskowner,
            "developers" : self.developers,
            "users" : self.users,
            "interestedparties" : self.interestedparties,
            "influencingfactors" : self.influencingfactors,
            "requirements" : [ x.asdict() for x in self.requirements],
        }

class Output(object):
    def __init__(self, output_type, location, description):
        self.output_type = output_type
        self.location = location
        self.description = description
    def asdict(self):
        return {
            "output_type" : self.output_type,
            "location" : self.location,
            "description" : self.description,
        }

class Outputs(object):
    def __init__(self):
        self.expected = []
        self.actual = []
        self.arisingpossibilities = []
    def asdict(self):
        return {
            "expected" : self.expected,
            "actual" : [ x.asdict() for x in self.actual ],
            "arisingpossibilities" : self.arisingpossibilities,
        }

class Relatedtasks(object):
    def __init__(self):
        self.tasksenablingthis = []
        self.subtasks = []
        self.cotasks = []
    def asdict(self):
        return {
            "tasksenablingthis" : self.tasksenablingthis,
            "subtasks" : self.subtasks,
            "cotasks" : self.cotasks,
        }

class Task(object):
    def __init__(self, taskid):
        self.taskid =  taskid,             # OK
        self.description = Descripton()    # OK
        self.dashboard = Dashboard()       # OK
        self.inputs = Inputs()             # OK
        self.outputs = Outputs()           # OK
        self.relatedtasks = Relatedtasks() #
        self.tasklog = []                  # OK
        self.discussion = []               # OK
        self.consolidateddiscussion = ""   # OK

    def asdict(self):
        return {
            "taskid" :  self.taskid,
            "description" : self.description.asdict(),
            "dashboard" : self.dashboard.asdict(),
            "inputs" : self.inputs.asdict(),
            "outputs" : self.outputs.asdict(),
            "relatedtasks" : self.relatedtasks.asdict(),
            "tasklog" : [x.asdict() for x in self.tasklog],
            "discussion" : [x.asdict() for x in self.discussion],
            "consolidateddiscussion" : self.consolidateddiscussion ,
        }

class Tasks(object):
    def __init__(self, filename):
        self.filename = shelve
        self.db = shelve.open(filename, "c")
        self.meta = shelve.open(filename+".meta", "c")
    def new_task(self):
        try:
            x = self.meta["highest"]
        except KeyError:
            x = 0
        x = x + 1
        self.meta["highest"] = x
        return Task(x)

    def zap(self):
        for i in self.meta.keys():
            del self.meta[i]
        for i in self.db.keys():
            del self.db[i]

T = Tasks("taskfile")
T.zap()

task = T.new_task()

task.description.goal = "Short one line of what the task is designed to achieve/create."
task.description.result = "A practical, clear result of what will be possible as a result of achieving this task. This is best described in the case of a user story."
task.description.context = "The context in which this task sits. Has this task any history? Is it the result of any previous tasks - either within the project or outside."
task.description.benefits.append( "What benefits will be gained by working on this task")
task.description.benefits.append( "... and achieving its goals?")
task.description.benefits.append("Speculative as well as certained/realistically expected benefits are valid here.")

task.dashboard.status = "(Started|Running|Completed|Dropped|Stasis|Blocked)"
task.dashboard.status_text = "Associated single sentence (eg why blocked)"
task.dashboard.currentdevelopers = "you!"
task.dashboard.devlocation = "Normally /Sketches/ initially"
task.dashboard.startdate = "date"
task.dashboard.milestonedate ="date"
task.dashboard.milestonetag = "(met|slipped|missed)"
task.dashboard.expectedenddate = "(date|n/a)"
task.dashboard.enddate = "date"
task.dashboard.dateupdated = "date"
task.dashboard.estimatedeffortsofar = "int"

task.discussion.append( Comment(who = "name1", when = "timedate", what = "YES!") )
task.discussion.append( Comment(who = "name1", when = "timedate", what = "NO!") )
task.discussion.append( Comment(who = "name1", when = "timedate", what = "MAYBE!") )

task.consolidateddiscussion = "--\n".join([ x.what for x in task.discussion ])

task.tasklog.append( Update(what="what", who="name", when="now", timespent="5", output="", statuschange="") )
task.tasklog.append( Update(what="what", who="name", when="now", timespent="5", output="", statuschange="") )
task.tasklog.append( Update(what="what", who="name", when="now", timespent="5", output="", statuschange="") )

task.inputs.tasksponsor = "Tom"
task.inputs.taskowner = "Tom"
task.inputs.developers.append( "Tom" )
task.inputs.users.append( "Tom" )
task.inputs.users.append( "Dick" )
task.inputs.users.append( "Harry" )
task.inputs.interestedparties.append("Jane")
task.inputs.interestedparties.append("Deliah")
task.inputs.influencingfactors.append("Would be really cool")
task.inputs.influencingfactors.append("Wanted this for ages!")

task.inputs.requirements.append( Requirement(reqtype="MUST", who="Jane", what="Pink") )
task.inputs.requirements.append( Requirement(reqtype="SHOULD", who="Jane", what="Chocolate") )
task.inputs.requirements.append( Requirement(reqtype="MAY", who="Tom", what="Work") )
task.inputs.requirements.append( Requirement(reqtype="WOULDLIKE", who="Deliah", what="Fluffy") )

task.outputs.expected.append("The widget should curl")
task.outputs.expected.append("The widget should frown")
task.outputs.expected.append("The widget should stamp")

task.outputs.actual.append(Output(output_type="code", location="svn://.../..py",description="fish"))
task.outputs.actual.append(Output(output_type="presentation", location="PYCONUK08",description="Woo"))
task.outputs.actual.append(Output(output_type="documentation", location="http:/..../",description="Docs"))
task.outputs.actual.append(Output(output_type="other", location="Kitchen",description="Cake"))

task.outputs.arisingpossibilities.append("Should be able to sing a musical!")
task.outputs.arisingpossibilities.append("Should be able to put on a show!")

task.relatedtasks.tasksenablingthis.append( 2 )
task.relatedtasks.tasksenablingthis.append( 3 )
task.relatedtasks.tasksenablingthis.append( 4 )

task.relatedtasks.subtasks.append( ("microtask", "Step to the left" ))
task.relatedtasks.subtasks.append( ("microtask", "jump to the right" ))
task.relatedtasks.subtasks.append( ("microtask", "pull knees in tight" ))
task.relatedtasks.subtasks.append( ("task", 5 ))
task.relatedtasks.subtasks.append( ("task", 6 ))
task.relatedtasks.subtasks.append( ("task", 7 ))

task.relatedtasks.cotasks.append( 8 )
task.relatedtasks.cotasks.append( 9 )
task.relatedtasks.cotasks.append( 10 )

pprint.pprint ( task.asdict(), width=170 )

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
import os, re, datetime, calendar

__version__ = 0.5

class Perlinpinpin(object):
    def __init__(self):
        self.regexp = [
            # Il y a x
            (re.compile(
                r'''^
                    il\sy\sa\s
                    ((?P<weeks>\d+) \s semaine(s?)?)?
                    [^\d]*
                    ((?P<days>\d+) \s jour(s?)?)?
                    [^\d]*
                    ((?P<hours>\d+) \s heure(s?)?)?
                    [^\d]*
                    ((?P<minutes>\d+) \s minute(s?)?)?
                    [^\d]*
                    ((?P<seconds>\d+) \s seconde(s?)?)?
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() - datetime.timedelta(
                    days=int(m.group('days') or 0),
                    seconds=int(m.group('seconds') or 0),
                    minutes=int(m.group('minutes') or 0),
                    hours=int(m.group('hours') or 0),
                    weeks=int(m.group('weeks') or 0))),

            # Dans x
            (re.compile(
                r'''^
                    dans\s
                    ((?P<weeks>\d+) \s semaine(s?)?)?
                    [^\d]*
                    ((?P<days>\d+) \s jour(s?)?)?
                    [^\d]*
                    ((?P<hours>\d+) \s heure(s?)?)?
                    [^\d]*
                    ((?P<minutes>\d+) \s minute(s?)?)?
                    [^\d]*
                    ((?P<seconds>\d+) \s seconde(s?)?)?
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() + datetime.timedelta(
                    days=int(m.group('days') or 0),
                    seconds=int(m.group('seconds') or 0),
                    minutes=int(m.group('minutes') or 0),
                    hours=int(m.group('hours') or 0),
                    weeks=int(m.group('weeks') or 0))),

            # Today
            (re.compile(
                r'''^
                    aujourd\'?hui                    # Today
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today()),

            # Now
            (re.compile(
                r'''^
                    maintenant                      # Now
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today()),

            # Tomorrow
            (re.compile(
                r'''^
                    demain                          # Tomorrow
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() + datetime.timedelta(days=1)),

            # Yesterday
            (re.compile(
                r'''^
                    hier                            # Yesterday
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() - datetime.timedelta(days=1)),

            # After-tomorrow
            (re.compile(
                r'''^
                    apres\-?\s?demain              # Afer-tomorrow
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() + datetime.timedelta(days=2)),

            # Before-yesterday
            (re.compile(
                r'''^
                    avant\-?\s?hier              # Before-yesterday
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() - datetime.timedelta(days=2)),

            # This morning
            (re.compile(
                r'''^
                    (ce\s+)?                        # this
                    matin                           # morning
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today()),

            # This afternoon
            (re.compile(
                r'''^
                    (cet\s+)?                       # this
                    apres\-?midi                    # afternoon
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today()),

            # 4
            (re.compile(
                r'''^
                    (le                             # le
                    \s+)?                           # whitespace
                    (\w+\s+)?                       # vendredi
                    (?P<day>\d{1,2})                # 4
                    (?:(\s+)?(ier|er|iere))?        # optional suffix
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today().replace(
                day=int(m.group('day')))),

            # 4 Janvier
            (re.compile(
                r'''^
                    (le                             # le
                    \s+)?                           # whitespace
                    (\w+\s+)?                       # vendredi
                    (?P<day>\d{1,2})                # 4
                    (?:(\s+)?(ier|er|iere))?        # optional suffix
                    \s+                             # whitespace
                    (?P<month>\w+)                  # Janvier
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today().replace(
                day=int(m.group('day')),
                month=self._parseMonth(m.group('month')))),

            # 4 Janvier 2003
            (re.compile(
                r'''^
                    (le                             # le
                    \s+)?                           # whitespace
                    (\w+\s+)?                       # vendredi
                    (?P<day>\d{1,2})                # 4
                    (?:(\s+)?(ier|er|iere))?        # optional suffix
                    \s+                             # whitespace
                    (?P<month>\w+)                  # Janvier
                    ,?                              # optional comma
                    \s+                             # whitespace
                    (?P<year>\d{4})                 # 2003
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date(
                year=int(m.group('year')),
                month=self._parseMonth(m.group('month')),
                day=int(m.group('day')))),

            # dd/mm/yyyy (European style, default in case of doubt)
            (re.compile(
                r'''^
                    (le                             # le
                    \s+)?                           # whitespace
                    (?P<day>0?[1-9]|[12]\d|30|31)   # d or dd
                    /                               #
                    (?P<month>0?[1-9]|10|11|12)     # m or mm
                    /                               #
                    (?P<year>\d{4})               # yyyy
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date(
                year=int(m.group('year')),
                month=int(m.group('month')),
                day=int(m.group('day')))),

            # mm/dd/yyyy (American style)
            (re.compile(
                r'''^
                    (?P<month>0?[1-9]|10|11|12)     # m or mm
                    /                               #
                    (?P<day>0?[1-9]|[12]\d|30|31)   # d or dd
                    /                               #
                    (?P<year>\d{4})               # yyyy
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date(
                year=int(m.group('year')),
                month=int(m.group('month')),
                day=int(m.group('day')))),

            # yyyy-mm-dd (ISO style)
            (re.compile(
                r'''^
                    (?P<year>\d{4})                 # yyyy
                    -                               #
                    (?P<month>0?[1-9]|10|11|12)     # m or mm
                    -                               #
                    (?P<day>0?[1-9]|[12]\d|30|31)   # d or dd
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date(
                year=int(m.group('year')),
                month=int(m.group('month')),
                day=int(m.group('day')))),

            # yyyymmdd
            (re.compile(
                r'''^
                    (?P<year>\d{4})                 # yyyy
                    (?P<month>0?[1-9]|10|11|12)     # m or mm
                    (?P<day>0?[1-9]|[12]\d|30|31)   # d or dd
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date(
                year=int(m.group('year')),
                month=int(m.group('month')),
                day=int(m.group('day')))),

            # Semaine dernière
            (re.compile(
                r'''^
                    (la                             # la
                    \s+)?                           # whitespace
                    semaine                         # week
                    \s+                             # whitespace
                    derniere                        # last
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() - datetime.timedelta(days=7)),

            # Semaine prochaine
            (re.compile(
                r'''^
                    (la                             # la
                    \s+)?                           # whitespace
                    semaine                         # week
                    \s+                             # whitespace
                    prochaine                       # last
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() + datetime.timedelta(days=7)),

            # Mardi prochain
            (re.compile(
                r'''^
                    (?P<weekday>\w+)                # Mardi
                    \s+                             # whitespace
                    prochain                        # next
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: self._nextWeekday(self._parseWeekday(m.group('weekday')))),

            # Mardi dernier
            (re.compile(
                r'''^
                    (?P<weekday>\w+)                # Mardi
                    (\s+                            # whitespace
                    dernier)?                       # last
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: self._lastWeekday(self._parseWeekday(m.group('weekday')))),
       ]

    def parse(self, input, tz=None):
        input = self._normalize(input)
        if tz is not None: os.environ['TZ'] = tz
        for r, f in self.regexp:
            m = r.match(input.strip())
            if m:
                return f(m)
        raise ValueError

    def _normalize(self, input):
        import unicodedata
        return unicodedata.normalize('NFKD', unicode(input)).encode('ASCII', 'ignore')

    def _parseMonth(self, input):
        months = "Janvier Fevrier Mars Avril Mai Juin Juillet Aout Septembre Octobre Novembre Decembre".split(' ')
        for i, month in enumerate(months):
            p = re.compile(input, re.IGNORECASE)
            if p.match(month): return i+1
        else:
            raise ValueError

    def _parseWeekday(self, input):
        days = "Lundi Mardi Mercredi Jeudi Vendredi Samedi Dimanche".split(' ')
        for i, day in enumerate(days):
            p = re.compile(input, re.IGNORECASE)
            if p.match(day): return i
        else:
            raise ValueError

    def _nextWeekday(self, weekday):
        day = datetime.date.today() + datetime.timedelta(days=1)
        while calendar.weekday(*day.timetuple()[:3]) != weekday:
            day = day + datetime.timedelta(days=1)
        return day

    def _lastWeekday(self, weekday):
        day = datetime.date.today() - datetime.timedelta(days=1)
        while calendar.weekday(*day.timetuple()[:3]) != weekday:
            day = day - datetime.timedelta(days=1)
        return day

def perlinpinpin(input, tz=None):
    return Perlinpinpin().parse(input, tz)
<commit_msg>prepare for release<commit_after># -*- coding: utf-8 -*-
import os, re, datetime, calendar

__version__ = 0.6

class Perlinpinpin(object):
    def __init__(self):
        self.regexp = [
            # Il y a x
            (re.compile(
                r'''^
                    il\sy\sa\s
                    ((?P<weeks>\d+) \s semaine(s?)?)?
                    [^\d]*
                    ((?P<days>\d+) \s jour(s?)?)?
                    [^\d]*
                    ((?P<hours>\d+) \s heure(s?)?)?
                    [^\d]*
                    ((?P<minutes>\d+) \s minute(s?)?)?
                    [^\d]*
                    ((?P<seconds>\d+) \s seconde(s?)?)?
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() - datetime.timedelta(
                    days=int(m.group('days') or 0),
                    seconds=int(m.group('seconds') or 0),
                    minutes=int(m.group('minutes') or 0),
                    hours=int(m.group('hours') or 0),
                    weeks=int(m.group('weeks') or 0))),

            # Dans x
            (re.compile(
                r'''^
                    dans\s
                    ((?P<weeks>\d+) \s semaine(s?)?)?
                    [^\d]*
                    ((?P<days>\d+) \s jour(s?)?)?
                    [^\d]*
                    ((?P<hours>\d+) \s heure(s?)?)?
                    [^\d]*
                    ((?P<minutes>\d+) \s minute(s?)?)?
                    [^\d]*
                    ((?P<seconds>\d+) \s seconde(s?)?)?
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() + datetime.timedelta(
                    days=int(m.group('days') or 0),
                    seconds=int(m.group('seconds') or 0),
                    minutes=int(m.group('minutes') or 0),
                    hours=int(m.group('hours') or 0),
                    weeks=int(m.group('weeks') or 0))),

            # Today
            (re.compile(
                r'''^
                    aujourd\'?hui                    # Today
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today()),

            # Now
            (re.compile(
                r'''^
                    maintenant                      # Now
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today()),

            # Tomorrow
            (re.compile(
                r'''^
                    demain                          # Tomorrow
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() + datetime.timedelta(days=1)),

            # Yesterday
            (re.compile(
                r'''^
                    hier                            # Yesterday
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() - datetime.timedelta(days=1)),

            # After-tomorrow
            (re.compile(
                r'''^
                    apres\-?\s?demain              # Afer-tomorrow
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() + datetime.timedelta(days=2)),

            # Before-yesterday
            (re.compile(
                r'''^
                    avant\-?\s?hier              # Before-yesterday
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() - datetime.timedelta(days=2)),

            # This morning
            (re.compile(
                r'''^
                    (ce\s+)?                        # this
                    matin                           # morning
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today()),

            # This afternoon
            (re.compile(
                r'''^
                    (cet\s+)?                       # this
                    apres\-?midi                    # afternoon
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today()),

            # 4
            (re.compile(
                r'''^
                    (le                             # le
                    \s+)?                           # whitespace
                    (\w+\s+)?                       # vendredi
                    (?P<day>\d{1,2})                # 4
                    (?:(\s+)?(ier|er|iere))?        # optional suffix
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today().replace(
                day=int(m.group('day')))),

            # 4 Janvier
            (re.compile(
                r'''^
                    (le                             # le
                    \s+)?                           # whitespace
                    (\w+\s+)?                       # vendredi
                    (?P<day>\d{1,2})                # 4
                    (?:(\s+)?(ier|er|iere))?        # optional suffix
                    \s+                             # whitespace
                    (?P<month>\w+)                  # Janvier
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today().replace(
                day=int(m.group('day')),
                month=self._parseMonth(m.group('month')))),

            # 4 Janvier 2003
            (re.compile(
                r'''^
                    (le                             # le
                    \s+)?                           # whitespace
                    (\w+\s+)?                       # vendredi
                    (?P<day>\d{1,2})                # 4
                    (?:(\s+)?(ier|er|iere))?        # optional suffix
                    \s+                             # whitespace
                    (?P<month>\w+)                  # Janvier
                    ,?                              # optional comma
                    \s+                             # whitespace
                    (?P<year>\d{4})                 # 2003
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date(
                year=int(m.group('year')),
                month=self._parseMonth(m.group('month')),
                day=int(m.group('day')))),

            # dd/mm/yyyy (European style, default in case of doubt)
            (re.compile(
                r'''^
                    (le                             # le
                    \s+)?                           # whitespace
                    (?P<day>0?[1-9]|[12]\d|30|31)   # d or dd
                    /                               #
                    (?P<month>0?[1-9]|10|11|12)     # m or mm
                    /                               #
                    (?P<year>\d{4})               # yyyy
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date(
                year=int(m.group('year')),
                month=int(m.group('month')),
                day=int(m.group('day')))),

            # mm/dd/yyyy (American style)
            (re.compile(
                r'''^
                    (?P<month>0?[1-9]|10|11|12)     # m or mm
                    /                               #
                    (?P<day>0?[1-9]|[12]\d|30|31)   # d or dd
                    /                               #
                    (?P<year>\d{4})               # yyyy
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date(
                year=int(m.group('year')),
                month=int(m.group('month')),
                day=int(m.group('day')))),

            # yyyy-mm-dd (ISO style)
            (re.compile(
                r'''^
                    (?P<year>\d{4})                 # yyyy
                    -                               #
                    (?P<month>0?[1-9]|10|11|12)     # m or mm
                    -                               #
                    (?P<day>0?[1-9]|[12]\d|30|31)   # d or dd
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date(
                year=int(m.group('year')),
                month=int(m.group('month')),
                day=int(m.group('day')))),

            # yyyymmdd
            (re.compile(
                r'''^
                    (?P<year>\d{4})                 # yyyy
                    (?P<month>0?[1-9]|10|11|12)     # m or mm
                    (?P<day>0?[1-9]|[12]\d|30|31)   # d or dd
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date(
                year=int(m.group('year')),
                month=int(m.group('month')),
                day=int(m.group('day')))),

            # Semaine dernière
            (re.compile(
                r'''^
                    (la                             # la
                    \s+)?                           # whitespace
                    semaine                         # week
                    \s+                             # whitespace
                    derniere                        # last
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() - datetime.timedelta(days=7)),

            # Semaine prochaine
            (re.compile(
                r'''^
                    (la                             # la
                    \s+)?                           # whitespace
                    semaine                         # week
                    \s+                             # whitespace
                    prochaine                       # last
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: datetime.date.today() + datetime.timedelta(days=7)),

            # Mardi prochain
            (re.compile(
                r'''^
                    (?P<weekday>\w+)                # Mardi
                    \s+                             # whitespace
                    prochain                        # next
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: self._nextWeekday(self._parseWeekday(m.group('weekday')))),

            # Mardi dernier
            (re.compile(
                r'''^
                    (?P<weekday>\w+)                # Mardi
                    (\s+                            # whitespace
                    dernier)?                       # last
                    $                               # EOL
                ''',
                (re.VERBOSE | re.IGNORECASE)),
            lambda m: self._lastWeekday(self._parseWeekday(m.group('weekday')))),
       ]

    def parse(self, input, tz=None):
        input = self._normalize(input)
        if tz is not None: os.environ['TZ'] = tz
        for r, f in self.regexp:
            m = r.match(input.strip())
            if m:
                return f(m)
        raise ValueError

    def _normalize(self, input):
        import unicodedata
        return unicodedata.normalize('NFKD', unicode(input)).encode('ASCII', 'ignore')

    def _parseMonth(self, input):
        months = "Janvier Fevrier Mars Avril Mai Juin Juillet Aout Septembre Octobre Novembre Decembre".split(' ')
        for i, month in enumerate(months):
            p = re.compile(input, re.IGNORECASE)
            if p.match(month): return i+1
        else:
            raise ValueError

    def _parseWeekday(self, input):
        days = "Lundi Mardi Mercredi Jeudi Vendredi Samedi Dimanche".split(' ')
        for i, day in enumerate(days):
            p = re.compile(input, re.IGNORECASE)
            if p.match(day): return i
        else:
            raise ValueError

    def _nextWeekday(self, weekday):
        day = datetime.date.today() + datetime.timedelta(days=1)
        while calendar.weekday(*day.timetuple()[:3]) != weekday:
            day = day + datetime.timedelta(days=1)
        return day

    def _lastWeekday(self, weekday):
        day = datetime.date.today() - datetime.timedelta(days=1)
        while calendar.weekday(*day.timetuple()[:3]) != weekday:
            day = day - datetime.timedelta(days=1)
        return day

def perlinpinpin(input, tz=None):
    return Perlinpinpin().parse(input, tz)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
hyper/http20/connection
~~~~~~~~~~~~~~~~~~~~~~~

Objects that build hyper's connection-level HTTP/2.0 abstraction.
"""
from .hpack import Encoder, Decoder
from .stream import Stream
from .tls import wrap_socket
from .frame import DataFrame, HeadersFrame, SettingsFrame, Frame

import socket

class HTTP20Connection(object):
    """
    An object representing a single HTTP/2.0 connection to a server.

    This object behaves similarly to the Python standard library's
    HTTPConnection object, with a few critical differences.
    """
    def __init__(self, host, port=None, **kwargs):
        """
        Creates an HTTP/2.0 connection to a specific server.

        Most of the standard library's arguments to the constructor are
        irrelevant for HTTP/2.0 or not supported by hyper.
        """
        if port is None:
            try:
                self.host, self.port = host.split(':')
                self.port = int(self.port)
            except ValueError:
                self.host, self.port = host, 443
        else:
            self.host, self.port = host, port

        # Streams are stored in a dictionary keyed off their stream IDs. We
        # also save the most recent one for easy access without having to walk
        # the dictionary.
        self.streams = {}
        self.recent_stream = None
        self.next_stream_id = 1

        # Header encoding/decoding is at the connection scope, so we embed a
        # header encoder and a decoder. These get passed to child stream
        # objects.
        self.encoder = Encoder()
        self.decoder = Decoder()

        # The socket used to send data.
        self._sock = None

        # The inbound and outbound flow control windows.
        self._out_flow_control_window = 65535
        self._in_flow_control_window = 65535

        return

    def request(self, method, url, body=None, headers={}):
        """
        This will send a request to the server using the HTTP request method
        ``method`` and the selector ``url``. If the ``body`` argument is
        present, it should be string or bytes object of data to send after the
        headers are finished. Strings are encoded as UTF-8. To use other
        encodings, pass a bytes object. The Content-Length header is set to the
        length of the body field.

        Returns a stream ID for the request.
        """
        pass

    def getresponse(self, stream_id=None):
        """
        Should be called after a request is sent to get a response from the
        server. If sending multiple parallel requests, pass the stream ID of
        the request whose response you want. Returns a HTTPResponse instance.
        If you pass no stream_id, you will receive the oldest HTTPResponse
        still outstanding.
        """
        pass

    def connect(self):
        """
        Connect to the server specified when the object was created. This is a
        no-op if we're already connected.
        """
        if self._sock is None:
            sock = socket.create_connection((self.host, self.port), 5)
            sock = wrap_socket(sock, self.host)
            self._sock = sock

            # We need to send a Settings frame immediately on this connection.
            f = SettingsFrame(0)
            f.settings[SettingsFrame.ENABLE_PUSH] = 0
            self._send_cb(f)

        return

    def close(self):
        """
        Close the connection to the server.
        """
        pass

    def putrequest(self, method, selector, **kwargs):
        """
        This should be the first call for sending a given HTTP request to a
        server. It returns a stream ID for the given connection that should be
        passed to all subsequent request building calls.
        """
        # Create a new stream.
        s = self._new_stream()

        # To this stream we need to immediately add a few headers that are
        # HTTP/2.0 specific. These are: ":method", ":scheme", ":authority" and
        # ":path". We can set all of these now.
        s.add_header(":method", method)
        s.add_header(":scheme", "https")  # We only support HTTPS at this time.
        s.add_header(":authority", self.host)
        s.add_header(":path", selector)

        # Save the stream.
        self.streams[s.stream_id] = s
        self.recent_stream = s

        return s.stream_id

    def putheader(self, header, argument, stream_id=None):
        """
        Sends an HTTP header to the server, with name ``header`` and value
        ``argument``.

        Unlike the httplib version of this function, this version does not
        actually send anything when called. Instead, it queues the headers up
        to be sent when you call ``endheaders``.
        """
        stream = (self.streams[stream_id] if stream_id is not None
                  else self.recent_stream)

        stream.add_header(header, argument)

        return

    def endheaders(self, message_body=None, final=False, stream_id=None):
        """
        Sends the prepared headers to the server. If the ``message_body``
        argument is provided it will also be sent to the server as the body of
        the request, and the stream will immediately be closed. If the
        ``final`` argument is set to True, the stream will also immediately
        be closed: otherwise, the stream will be left open and subsequent calls
        to ``send()`` will be required.
        """
        stream = (self.streams[stream_id] if stream_id is not None
                  else self.recent_stream)

        # Close this if we've been told no more data is coming and we don't
        # have any to send.
        stream.open(final and message_body is None)

        # Send whatever data we have.
        if message_body is not None:
            stream.send_data(message_body, final)

        return

    def send(self, data, final=False, stream_id=None):
        """
        Sends some data to the server. This data will be sent immediately
        (excluding the normal HTTP/2.0 flow control rules). If this is the last
        data that will be sent as part of this request, the ``final`` argument
        should be set to ``True``. This will cause the stream to be closed.
        """
        stream = (self.streams[stream_id] if stream_id is not None
                  else self.recent_stream)

        stream.send_data(data, final)

        return

    def _new_stream(self):
        """
        Returns a new stream object for this connection.
        """
        s = Stream(
            self.next_stream_id, self._send_cb, self._recv_cb, self.encoder,
            self.decoder
        )
        self.next_stream_id += 2

        return s

    def _send_cb(self, frame):
        """
        This is the callback used by streams to send data on the connection.

        It expects to receive a single frame, and then to serialize that frame
        and send it on the connection. It does so obeying the connection-level
        flow-control principles of HTTP/2.0.
        """
        # Maintain our outgoing flow-control window.
        if (isinstance(frame, DataFrame) and
            not isinstance(frame, HeadersFrame)):
            if self._out_flow_control_window < len(frame.data):
                raise RuntimeError("Flow control not yet implemented.")

            self._out_flow_control_window -= len(frame.data)

        data = frame.serialize()

        self._sock.send(data)

    def _recv_cb(self):
        """
        This is the callback used by streams to read data from the connection.

        It expects to read a single frame, and then to deserialize that frame
        and pass it to the relevant stream. This is generally called by a
        stream, not by the connection itself, and it's likely that streams will
        read a frame that doesn't belong to them. That's ok: streams need to
        make a decision to spin around again.
        """
        # Begin by reading 8 bytes from the socket.
        header = self._sock.recv(8)

        # Parse the header.
        frame, length = Frame.parse_frame_header(header)

        # Read the remaining data from the socket.
        data = self._sock.recv(length)
        frame.parse_body(data)

        # Work out to whom this frame should go.
        if frame.stream_id != 0:
            self.streams[frame.stream_id].receive_frame(frame)
        else:
            self.receive_frame(frame)
<commit_msg>We should remember to connect.<commit_after># -*- coding: utf-8 -*-
"""
hyper/http20/connection
~~~~~~~~~~~~~~~~~~~~~~~

Objects that build hyper's connection-level HTTP/2.0 abstraction.
"""
from .hpack import Encoder, Decoder
from .stream import Stream
from .tls import wrap_socket
from .frame import DataFrame, HeadersFrame, SettingsFrame, Frame

import socket

class HTTP20Connection(object):
    """
    An object representing a single HTTP/2.0 connection to a server.

    This object behaves similarly to the Python standard library's
    HTTPConnection object, with a few critical differences.
    """
    def __init__(self, host, port=None, **kwargs):
        """
        Creates an HTTP/2.0 connection to a specific server.

        Most of the standard library's arguments to the constructor are
        irrelevant for HTTP/2.0 or not supported by hyper.
        """
        if port is None:
            try:
                self.host, self.port = host.split(':')
                self.port = int(self.port)
            except ValueError:
                self.host, self.port = host, 443
        else:
            self.host, self.port = host, port

        # Streams are stored in a dictionary keyed off their stream IDs. We
        # also save the most recent one for easy access without having to walk
        # the dictionary.
        self.streams = {}
        self.recent_stream = None
        self.next_stream_id = 1

        # Header encoding/decoding is at the connection scope, so we embed a
        # header encoder and a decoder. These get passed to child stream
        # objects.
        self.encoder = Encoder()
        self.decoder = Decoder()

        # The socket used to send data.
        self._sock = None

        # The inbound and outbound flow control windows.
        self._out_flow_control_window = 65535
        self._in_flow_control_window = 65535

        return

    def request(self, method, url, body=None, headers={}):
        """
        This will send a request to the server using the HTTP request method
        ``method`` and the selector ``url``. If the ``body`` argument is
        present, it should be string or bytes object of data to send after the
        headers are finished. Strings are encoded as UTF-8. To use other
        encodings, pass a bytes object. The Content-Length header is set to the
        length of the body field.

        Returns a stream ID for the request.
        """
        pass

    def getresponse(self, stream_id=None):
        """
        Should be called after a request is sent to get a response from the
        server. If sending multiple parallel requests, pass the stream ID of
        the request whose response you want. Returns a HTTPResponse instance.
        If you pass no stream_id, you will receive the oldest HTTPResponse
        still outstanding.
        """
        pass

    def connect(self):
        """
        Connect to the server specified when the object was created. This is a
        no-op if we're already connected.
        """
        if self._sock is None:
            sock = socket.create_connection((self.host, self.port), 5)
            sock = wrap_socket(sock, self.host)
            self._sock = sock

            # We need to send a Settings frame immediately on this connection.
            f = SettingsFrame(0)
            f.settings[SettingsFrame.ENABLE_PUSH] = 0
            self._send_cb(f)

        return

    def close(self):
        """
        Close the connection to the server.
        """
        pass

    def putrequest(self, method, selector, **kwargs):
        """
        This should be the first call for sending a given HTTP request to a
        server. It returns a stream ID for the given connection that should be
        passed to all subsequent request building calls.
        """
        # Create a new stream.
        s = self._new_stream()

        # To this stream we need to immediately add a few headers that are
        # HTTP/2.0 specific. These are: ":method", ":scheme", ":authority" and
        # ":path". We can set all of these now.
        s.add_header(":method", method)
        s.add_header(":scheme", "https")  # We only support HTTPS at this time.
        s.add_header(":authority", self.host)
        s.add_header(":path", selector)

        # Save the stream.
        self.streams[s.stream_id] = s
        self.recent_stream = s

        return s.stream_id

    def putheader(self, header, argument, stream_id=None):
        """
        Sends an HTTP header to the server, with name ``header`` and value
        ``argument``.

        Unlike the httplib version of this function, this version does not
        actually send anything when called. Instead, it queues the headers up
        to be sent when you call ``endheaders``.
        """
        stream = (self.streams[stream_id] if stream_id is not None
                  else self.recent_stream)

        stream.add_header(header, argument)

        return

    def endheaders(self, message_body=None, final=False, stream_id=None):
        """
        Sends the prepared headers to the server. If the ``message_body``
        argument is provided it will also be sent to the server as the body of
        the request, and the stream will immediately be closed. If the
        ``final`` argument is set to True, the stream will also immediately
        be closed: otherwise, the stream will be left open and subsequent calls
        to ``send()`` will be required.
        """
        self.connect()

        stream = (self.streams[stream_id] if stream_id is not None
                  else self.recent_stream)

        # Close this if we've been told no more data is coming and we don't
        # have any to send.
        stream.open(final and message_body is None)

        # Send whatever data we have.
        if message_body is not None:
            stream.send_data(message_body, final)

        return

    def send(self, data, final=False, stream_id=None):
        """
        Sends some data to the server. This data will be sent immediately
        (excluding the normal HTTP/2.0 flow control rules). If this is the last
        data that will be sent as part of this request, the ``final`` argument
        should be set to ``True``. This will cause the stream to be closed.
        """
        stream = (self.streams[stream_id] if stream_id is not None
                  else self.recent_stream)

        stream.send_data(data, final)

        return

    def _new_stream(self):
        """
        Returns a new stream object for this connection.
        """
        s = Stream(
            self.next_stream_id, self._send_cb, self._recv_cb, self.encoder,
            self.decoder
        )
        self.next_stream_id += 2

        return s

    def _send_cb(self, frame):
        """
        This is the callback used by streams to send data on the connection.

        It expects to receive a single frame, and then to serialize that frame
        and send it on the connection. It does so obeying the connection-level
        flow-control principles of HTTP/2.0.
        """
        # Maintain our outgoing flow-control window.
        if (isinstance(frame, DataFrame) and
            not isinstance(frame, HeadersFrame)):
            if self._out_flow_control_window < len(frame.data):
                raise RuntimeError("Flow control not yet implemented.")

            self._out_flow_control_window -= len(frame.data)

        data = frame.serialize()

        self._sock.send(data)

    def _recv_cb(self):
        """
        This is the callback used by streams to read data from the connection.

        It expects to read a single frame, and then to deserialize that frame
        and pass it to the relevant stream. This is generally called by a
        stream, not by the connection itself, and it's likely that streams will
        read a frame that doesn't belong to them. That's ok: streams need to
        make a decision to spin around again.
        """
        # Begin by reading 8 bytes from the socket.
        header = self._sock.recv(8)

        # Parse the header.
        frame, length = Frame.parse_frame_header(header)

        # Read the remaining data from the socket.
        data = self._sock.recv(length)
        frame.parse_body(data)

        # Work out to whom this frame should go.
        if frame.stream_id != 0:
            self.streams[frame.stream_id].receive_frame(frame)
        else:
            self.receive_frame(frame)
<|endoftext|>


<commit_before>###############################################################
# Improfunctions by Daniel Field                              #
# contains functions to be used in the improvisor             #
# check out www.github.com/dan-field/jazz for info and rights #
###############################################################

# functions included:
#
# GetChords(fname) - reads the chord chart
# BreakDown(bar, beats) - breaks a bar into its roots and durations (crotchet = 1.0)
# Octavify(roots, octave) - appends octave number to the root names

from music import *

def GetChords(fname):
   with open(fname) as f:
      chords_rough = f.readlines()
      # now chords_rough is a list where each list element
      # is a line (as a string) from the file

   chords = [] # create an empty list for the chords

   for line in chords_rough:
      if line[0] == '%' or line[0] == '#': # first character is '%' or '#'
         pass # it's a comment line; skip it
      elif line == []:
         pass # it's an empty line; skip it
      else:
         line = line.split() # break down using whitespace as delimiter
         for element in line: # now take each element in the 'line' list
            chords.append(element) # and append it to the 'chords' list

   # Now we have a list of all the chords and bar separators.
   # We want to group these back into bars
   # Note - we could have done this on the first pass, but there would
   # have been no way for a bar to carry over a newline

   bars = []
   this_bar = []
   for element in chords: # each element is a single chord or a barline
      if element == '|': # end of a bar
         bars.append(this_bar) # add existing bar to the list of bars
         this_bar = [] # start a fresh new bar
      else:
         this_bar.append(element) # add the chord to the current bar

   return(chords, bars)

def BreakDown(bar, beats):
   roots = []
   durations = []
   chord_duration = beats/len(bar) # because the mini-language requires bars to be evenly divided
   for chord in bar:
      # convert the standard chord notation into Jython notation
      # and extract the chord type
      foundquality = False
      if chord[0] == '/':
         root = 'R'
      else:
         root = chord[0]
         if len(chord) > 1:
            if chord[1] == 'b':
               root += 'F'
            elif chord[1] == '#':
               root += 'S'
            elif chord[1] == 'M':
               foundquality = True
               quality = 'major'
               qualpos = 1
            elif chord [1] == 'm' or chord [1] == '-':
               foundquality = True
               quality = 'minor'
               qualpos = 1
      roots.append(root)
      durations.append(chord_duration)

   return(roots, durations)

def Octavify(roots, octave): # octave number as string, to make concatenation easier
   MIDI_nums = []
   for element in roots:
      element += octave # concatenation of chord root with MIDI octave number
      note = eval(element) # Jython Music evaluates the string to a MIDI note number
      MIDI_nums.append(note)
   return(MIDI_nums)

def ExtractRoots(bars, beats_per_bar):
   roots = [] # initialise an empty list for the root notes
   durations = [] # initialise an empty list for the durations
   last_root = 'NC' # initialise the last root, which will be needed if there's a chord repeat symbol
   # Work through one bar at a time
   for bar_count, bar in enumerate(bars):
      # get the root notes and durations
      roots_this_bar, durations_this_bar = BreakDown(bar, beats_per_bar)
      if roots_this_bar[-1] != 'R': # the bar contains actual chords, not a 'repeat chord' symbol
         root_numbers = Octavify(roots_this_bar, '3') # pass the MIDI octave number as a string
         last_root = root_numbers
      else:
         root_numbers = last_root
      roots.append(root_numbers)
      durations.append(durations_this_bar)
   # the next two lines make 'flat' lists out of the lists of lists
   roots_flat = [item for sublist in roots for item in sublist]
   durations_flat = [item for sublist in durations for item in sublist]
   return roots_flat, durations_flat<commit_msg>Delete improfunctions.py<commit_after><|endoftext|>


<commit_before>import copy
import deepdish as dd
import numpy as np
from .tools.normalize import normalize as normalizer
from .tools.reduce import reduce as reducer
from .tools.align import align as aligner
from .tools.format_data import format_data
from .config import __version__

class DataGeometry(object):
    """
    Hypertools data object class

    A DataGeometry object contains the data, figure handles and transform
    functions used to create a plot.  Note: this class should not be called
    directly, but is used by the `hyp.plot` function to create a plot object.

    Parameters
    ----------

    fig : matplotlib.Figure
        The matplotlib figure handle for the plot

    ax : matplotlib.Axes
        The matplotlib axes handle for the plot

    line_ani : matplotlib.animation.FuncAnimation
        The matplotlib animation handle (if the plot is an animation)

    data : list
        A list of numpy arrays representing the raw data

    xform_data : list
        A list of numpy arrays representing the transformed data

    reduce : dict
        A dictionary containing the reduction model and parameters

    align : dict
        A dictionary containing align model and parameters

    normalize : str
        A string representing the kind of normalization

    kwargs : dict
        A dictionary containing all kwargs passed to the plot function

    version : str
        The version of the software used to create the class instance

    """

    def __init__(self, fig=None, ax=None, line_ani=None, data=None, xform_data=None,
                 reduce=None, align=None, normalize=None, text=None, kwargs=None,
                 version=__version__):

        # matplotlib figure handle
        self.fig = fig

        # matplotlib axis handle
        self.ax = ax

        # matplotlib line_ani handle (if its an animation)
        self.line_ani = line_ani

        # the raw data
        self.data = data

        # the transformed data
        self.xform_data = xform_data

        # dictionary of model and model_params
        self.reduce = reduce

        # 'hyper', 'SRM' or None
        self.align = align

        # 'within', 'across', 'row' or False
        self.normalize = normalize

        # text params
        self.text = text

        # dictionary of kwargs
        self.kwargs = kwargs

        # hypertools version
        self.version = version

    # a function to transform new data
    def transform(self, data=None):
        """
        Return transformed data, or transform new data using the same model
        parameters

        Parameters
        ----------
        data : numpy array, pandas dataframe or list of arrays/dfs
            The data to transform.  If no data is passed, the xform_data from
            the DataGeometry object will be returned.

        Returns
        ----------
        xformed_data : list of numpy arrays
            The transformed data

        """
        # if no new data passed,
        if data is None:
            return self.xform_data
        else:
            reduce_model = {'model' : self.reduce['model'],
                            'params' : self.reduce['params']}
            align_model = {'model' : self.align['model'],
                           'params' : self.align['params']}
            text_model = {'model' : self.text['model'],
                          'params' : self.text['params']}
            return format_data(
                aligner(
                reducer(
                normalizer(data,
                normalize=self.normalize),
                reduce=reduce_model,
                ndims=self.reduce['params']['n_components']),
                align=align_model),
                text=text_model, ppca=True)

    # a function to plot the data
    def plot(self, data=None, **kwargs):
        """
        Plot the data

        Parameters
        ----------
        data : numpy array, pandas dataframe or list of arrays/dfs
            The data to plot.  If no data is passed, the xform_data from
            the DataGeometry object will be returned.

        kwargs : keyword arguments
            Any keyword arguments supported by `hypertools.plot` are also supported
            by this method

        Returns
        ----------
        geo : hypertools.DataGeometry
            A new data geometry object

        """

        # import plot here to avoid circular imports
        from .plot.plot import plot as plotter

        if data is None:
            data = self.xform_data
            transform = False
            if any([k in kwargs for k in ['reduce', 'align', 'normalize', 'text']]):
                data = self.data
                transform = True
        else:
            transform = True

        # get kwargs and update with new kwargs
        new_kwargs = copy.copy(self.kwargs)
        for key in kwargs:
            new_kwargs.update({key : kwargs[key]})

        return plotter(data, transform=transform, **new_kwargs)

    def save(self, fname, compression='blosc'):
        """
        Save method for the data geometry object

        The data will be saved as a 'geo' file, which is a dictionary containing
        the elements of a data geometry object saved in the hd5 format using
        `deepdish`.

        Parameters
        ----------

        fname : str
            A name for the file.  If the file extension (.geo) is not specified,
            it will be appended.

        compression : str
            The kind of compression to use.  See the deepdish documentation for
            options: http://deepdish.readthedocs.io/en/latest/api_io.html#deepdish.io.save

        """

        # put geo vars into a dict
        geo = {
            'data' : self.data,
            'xform_data' : self.xform_data,
            'reduce' : self.reduce,
            'align' : self.align,
            'normalize' : self.normalize,
            'text' : self.text,
            'kwargs' : self.kwargs,
            'version' : self.version
        }

        # if extension wasn't included, add it
        if fname[-4:]!='.geo':
            fname+='.geo'

        # save
        dd.io.save(fname, geo, compression=compression)
<commit_msg>fixed format_data implementation in transform<commit_after>import copy
import deepdish as dd
import numpy as np
from .tools.normalize import normalize as normalizer
from .tools.reduce import reduce as reducer
from .tools.align import align as aligner
from .tools.format_data import format_data
from .config import __version__

class DataGeometry(object):
    """
    Hypertools data object class

    A DataGeometry object contains the data, figure handles and transform
    functions used to create a plot.  Note: this class should not be called
    directly, but is used by the `hyp.plot` function to create a plot object.

    Parameters
    ----------

    fig : matplotlib.Figure
        The matplotlib figure handle for the plot

    ax : matplotlib.Axes
        The matplotlib axes handle for the plot

    line_ani : matplotlib.animation.FuncAnimation
        The matplotlib animation handle (if the plot is an animation)

    data : list
        A list of numpy arrays representing the raw data

    xform_data : list
        A list of numpy arrays representing the transformed data

    reduce : dict
        A dictionary containing the reduction model and parameters

    align : dict
        A dictionary containing align model and parameters

    normalize : str
        A string representing the kind of normalization

    kwargs : dict
        A dictionary containing all kwargs passed to the plot function

    version : str
        The version of the software used to create the class instance

    """

    def __init__(self, fig=None, ax=None, line_ani=None, data=None, xform_data=None,
                 reduce=None, align=None, normalize=None, text=None, kwargs=None,
                 version=__version__):

        # matplotlib figure handle
        self.fig = fig

        # matplotlib axis handle
        self.ax = ax

        # matplotlib line_ani handle (if its an animation)
        self.line_ani = line_ani

        # the raw data
        self.data = data

        # the transformed data
        self.xform_data = xform_data

        # dictionary of model and model_params
        self.reduce = reduce

        # 'hyper', 'SRM' or None
        self.align = align

        # 'within', 'across', 'row' or False
        self.normalize = normalize

        # text params
        self.text = text

        # dictionary of kwargs
        self.kwargs = kwargs

        # hypertools version
        self.version = version

    # a function to transform new data
    def transform(self, data=None):
        """
        Return transformed data, or transform new data using the same model
        parameters

        Parameters
        ----------
        data : numpy array, pandas dataframe or list of arrays/dfs
            The data to transform.  If no data is passed, the xform_data from
            the DataGeometry object will be returned.

        Returns
        ----------
        xformed_data : list of numpy arrays
            The transformed data

        """
        # if no new data passed,
        if data is None:
            return self.xform_data
        else:
            reduce_model = {'model' : self.reduce['model'],
                            'params' : self.reduce['params']}
            align_model = {'model' : self.align['model'],
                           'params' : self.align['params']}
            text_model = {'model' : self.text['model'],
                          'params' : self.text['params']}
            return aligner(
                reducer(
                normalizer(
                format_data(data,
                text=text_model, ppca=True),
                normalize=self.normalize),
                reduce=reduce_model,
                ndims=self.reduce['params']['n_components']),
                align=align_model)

    # a function to plot the data
    def plot(self, data=None, **kwargs):
        """
        Plot the data

        Parameters
        ----------
        data : numpy array, pandas dataframe or list of arrays/dfs
            The data to plot.  If no data is passed, the xform_data from
            the DataGeometry object will be returned.

        kwargs : keyword arguments
            Any keyword arguments supported by `hypertools.plot` are also supported
            by this method

        Returns
        ----------
        geo : hypertools.DataGeometry
            A new data geometry object

        """

        # import plot here to avoid circular imports
        from .plot.plot import plot as plotter

        if data is None:
            data = self.xform_data
            transform = False
            if any([k in kwargs for k in ['reduce', 'align', 'normalize', 'text']]):
                data = self.data
                transform = True
        else:
            transform = True

        # get kwargs and update with new kwargs
        new_kwargs = copy.copy(self.kwargs)
        for key in kwargs:
            new_kwargs.update({key : kwargs[key]})

        return plotter(data, transform=transform, **new_kwargs)

    def save(self, fname, compression='blosc'):
        """
        Save method for the data geometry object

        The data will be saved as a 'geo' file, which is a dictionary containing
        the elements of a data geometry object saved in the hd5 format using
        `deepdish`.

        Parameters
        ----------

        fname : str
            A name for the file.  If the file extension (.geo) is not specified,
            it will be appended.

        compression : str
            The kind of compression to use.  See the deepdish documentation for
            options: http://deepdish.readthedocs.io/en/latest/api_io.html#deepdish.io.save

        """

        # put geo vars into a dict
        geo = {
            'data' : self.data,
            'xform_data' : self.xform_data,
            'reduce' : self.reduce,
            'align' : self.align,
            'normalize' : self.normalize,
            'text' : self.text,
            'kwargs' : self.kwargs,
            'version' : self.version
        }

        # if extension wasn't included, add it
        if fname[-4:]!='.geo':
            fname+='.geo'

        # save
        dd.io.save(fname, geo, compression=compression)
<|endoftext|>


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

"""
Implements animated trajectory plot

INPUTS:
-numpy array(s)
-list of numpy arrays

OUTPUTS:
-None
"""

##PACKAGES##
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
from .._shared.helpers import *
from ..util.reduce import reduce as reduceD

##MAIN FUNCTION##
def animated_plot(x, *args, **kwargs):

    if 'legend' in kwargs:
        legend=True
        legend_data = kwargs['legend']
        del kwargs['legend']

        palette = kwargs['color_palette']
        del kwargs['color_palette']
    else:
        legend=False

    if 'save_path' in kwargs:
        save=True
        save_path = kwargs['save_path']
        del kwargs['save_path']
    else:
        save=False

    ##SUB FUNCTIONS##
    def get_cube_scale(x, c):
        x = np.vstack(x)
        dists = np.sqrt(np.sum(np.square(x), axis=1))
        return c*np.max(dists)

    def plot_cube(scale, const=1):
        cube = {
            "top"    : ( [[-1,1],[-1,1]], [[-1,-1],[1,1]], [[1,1],[1,1]] ),
            "bottom" : ( [[-1,1],[-1,1]], [[-1,-1],[1,1]], [[-1,-1],[-1,-1]] ),
            "left"   : ( [[-1,-1],[-1,-1]], [[-1,1],[-1,1]], [[-1,-1],[1,1]] ),
            "right"  : ( [[1,1],[1,1]], [[-1,1],[-1,1]], [[-1,-1],[1,1]] ),
            "front"  : ( [[-1,1],[-1,1]], [[-1,-1],[-1,-1]], [[-1,-1],[1,1]] ),
            "back"   : ( [[-1,1],[-1,1]], [[1,1],[1,1]], [[-1,-1],[1,1]] )
            }

        plane_list = []
        for side in cube:
            (Xs, Ys, Zs) = (
                np.asarray(cube[side][0])*scale*const,
                np.asarray(cube[side][1])*scale*const,
                np.asarray(cube[side][2])*scale*const
                )
            plane_list.append(ax.plot_wireframe(Xs, Ys, Zs, rstride=1, cstride=1, color='black', linewidth=3))
        return plane_list

    def update_lines(num, data_lines, lines, trail_lines, cube_scale, tail_len=30, tail_style=':', speed=1):

        if hasattr(update_lines, 'planes'):
            for plane in update_lines.planes:
                plane.remove()

        update_lines.planes = plot_cube(cube_scale)
        ax.view_init(elev=10, azim=speed*num/5)
        ax.dist=cube_scale-1

        for line, data, trail in zip(lines, data_lines, trail_lines):
            if num<=tail_len:
                    line.set_data(data[0:num+1, 0:2].T)
                    line.set_3d_properties(data[0:num+1, 2])
            else:
                line.set_data(data[num-tail_len:num+1, 0:2].T)
                line.set_3d_properties(data[num-tail_len:num+1, 2])
        return lines,trail_lines

    args_list = parse_args(x,args)
    kwargs_list = parse_kwargs(x,kwargs)

    # Attaching 3D axis to the figure
    fig = plt.figure()
    ax = p3.Axes3D(fig)

    if type(x) is not list:
        x = [x]

    if x[0].shape[1]>3:
        x = reduceD(x,3)
    x = interp_array_list(x)
    x = center(x)

    lines = [ax.plot(dat[0, 0:1], dat[1, 0:1], dat[2, 0:1], linewidth=3, *args_list[idx], **kwargs_list[idx])[0] for idx,dat in enumerate(x)]
    trail = [ax.plot(dat[0, 0:1], dat[1, 0:1], dat[2, 0:1])[0] for dat in x]

    ax.set_axis_off()

    # Get cube scale from data
    cube_scale = get_cube_scale(x,1)

    # Setting the axes properties
    ax.set_xlim3d([-cube_scale, cube_scale])
    ax.set_ylim3d([-cube_scale, cube_scale])
    ax.set_zlim3d([-cube_scale, cube_scale])

    #add legend
    if legend:
        proxies = [plt.Rectangle((0, 0), 1, 1, fc=palette[idx]) for idx,label in enumerate(legend_data)]
        ax.legend(proxies,legend_data)

    # Creating the Animation object
    line_ani = animation.FuncAnimation(fig, update_lines, 1000, fargs=(x, lines, trail, cube_scale),
                                   interval=8, blit=False)
    if save:
        Writer = animation.writers['ffmpeg']
        writer = Writer(fps=30, bitrate=1800)
        line_ani.save(save_path, writer=writer)

    plt.show()
<commit_msg>fixed animation bug<commit_after>#!/usr/bin/env python

"""
Implements animated trajectory plot

INPUTS:
-numpy array(s)
-list of numpy arrays

OUTPUTS:
-None
"""

##PACKAGES##
from __future__ import division
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
from .._shared.helpers import *
from ..util.reduce import reduce as reduceD

##MAIN FUNCTION##
def animated_plot(x, *args, **kwargs):

    if 'legend' in kwargs:
        legend=True
        legend_data = kwargs['legend']
        del kwargs['legend']
    else:
        legend=False

    if 'color_palette' in kwargs:
        palette = kwargs['color_palette']
        del kwargs['color_palette']

    if 'save_path' in kwargs:
        save=True
        save_path = kwargs['save_path']
        del kwargs['save_path']
    else:
        save=False

    ##SUB FUNCTIONS##
    def get_cube_scale(x, c):
        x = np.vstack(x)
        dists = np.sqrt(np.sum(np.square(x), axis=1))
        return c*np.max(dists)

    def plot_cube(scale, const=1):
        cube = {
            "top"    : ( [[-1,1],[-1,1]], [[-1,-1],[1,1]], [[1,1],[1,1]] ),
            "bottom" : ( [[-1,1],[-1,1]], [[-1,-1],[1,1]], [[-1,-1],[-1,-1]] ),
            "left"   : ( [[-1,-1],[-1,-1]], [[-1,1],[-1,1]], [[-1,-1],[1,1]] ),
            "right"  : ( [[1,1],[1,1]], [[-1,1],[-1,1]], [[-1,-1],[1,1]] ),
            "front"  : ( [[-1,1],[-1,1]], [[-1,-1],[-1,-1]], [[-1,-1],[1,1]] ),
            "back"   : ( [[-1,1],[-1,1]], [[1,1],[1,1]], [[-1,-1],[1,1]] )
            }

        plane_list = []
        for side in cube:
            (Xs, Ys, Zs) = (
                np.asarray(cube[side][0])*scale*const,
                np.asarray(cube[side][1])*scale*const,
                np.asarray(cube[side][2])*scale*const
                )
            plane_list.append(ax.plot_wireframe(Xs, Ys, Zs, rstride=1, cstride=1, color='black', linewidth=3))
        return plane_list

    def update_lines(num, data_lines, lines, trail_lines, cube_scale, tail_len=30, tail_style=':', speed=1):

        if hasattr(update_lines, 'planes'):
            for plane in update_lines.planes:
                plane.remove()

        update_lines.planes = plot_cube(cube_scale)
        ax.view_init(elev=10, azim=speed*num/5)
        ax.dist=cube_scale-1

        for line, data, trail in zip(lines, data_lines, trail_lines):
            if num<=tail_len:
                    line.set_data(data[0:num+1, 0:2].T)
                    line.set_3d_properties(data[0:num+1, 2])
            else:
                line.set_data(data[num-tail_len:num+1, 0:2].T)
                line.set_3d_properties(data[num-tail_len:num+1, 2])
        return lines,trail_lines

    args_list = parse_args(x,args)
    kwargs_list = parse_kwargs(x,kwargs)

    # Attaching 3D axis to the figure
    fig = plt.figure()
    ax = p3.Axes3D(fig)

    if type(x) is not list:
        x = [x]

    if x[0].shape[1]>3:
        x = reduceD(x,3)
    x = interp_array_list(x)
    x = center(x)

    lines = [ax.plot(dat[0, 0:1], dat[1, 0:1], dat[2, 0:1], linewidth=3, *args_list[idx], **kwargs_list[idx])[0] for idx,dat in enumerate(x)]
    trail = [ax.plot(dat[0, 0:1], dat[1, 0:1], dat[2, 0:1])[0] for dat in x]

    ax.set_axis_off()

    # Get cube scale from data
    cube_scale = get_cube_scale(x,1)

    # Setting the axes properties
    ax.set_xlim3d([-cube_scale, cube_scale])
    ax.set_ylim3d([-cube_scale, cube_scale])
    ax.set_zlim3d([-cube_scale, cube_scale])

    #add legend
    if legend:
        proxies = [plt.Rectangle((0, 0), 1, 1, fc=palette[idx]) for idx,label in enumerate(legend_data)]
        ax.legend(proxies,legend_data)

    # Creating the Animation object
    line_ani = animation.FuncAnimation(fig, update_lines, 1000, fargs=(x, lines, trail, cube_scale),
                                   interval=8, blit=False)
    if save:
        Writer = animation.writers['ffmpeg']
        writer = Writer(fps=30, bitrate=1800)
        line_ani.save(save_path, writer=writer)

    plt.show()
<|endoftext|>


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

# libraries
import warnings
import numpy as np

## reduction models
from sklearn.decomposition import PCA, FastICA, IncrementalPCA, KernelPCA, FactorAnalysis, TruncatedSVD, SparsePCA, MiniBatchSparsePCA, DictionaryLearning, MiniBatchDictionaryLearning
from sklearn.manifold import TSNE, MDS, SpectralEmbedding, LocallyLinearEmbedding, Isomap

# internal libraries
from ..tools.df2mat import df2mat
from .._shared.helpers import *

# main function
@memoize
def reduce(x, reduce='IncrementalPCA', ndims=None, internal=False):
    """
    Reduces dimensionality of an array, or list of arrays

    Parameters
    ----------
    x : Numpy array or list of arrays
        Dimensionality reduction using PCA is performed on this array.  If
        there are nans present in the data, the function will try to use
        PPCA to interpolate the missing values.

    reduce : str or dict
        Decomposition/manifold learning model to use.  Models supported: PCA,
        IncrementalPCA, SparsePCA, MiniBatchSparsePCA, KernelPCA, FastICA,
        FactorAnalysis, TruncatedSVD, DictionaryLearning, MiniBatchDictionaryLearning,
        TSNE, Isomap, SpectralEmbedding, LocallyLinearEmbedding, and MDS. Can be
        passed as a string, but for finer control of the model parameters, pass
        as a dictionary, e.g. reduce={'model' : 'PCA', 'params' : {'whiten' : True}}.
        See scikit-learn specific model docs for details on parameters supported
        for each model.

    ndims : int
        Number of dimensions to reduce

    Returns
    ----------
    x_reduced : Numpy array or list of arrays
        The reduced data with ndims dimensionality is returned.  If the input
        is a list, a list is returned.

    """

    # sub functions
    def reduce_list(x, model):
        split = np.cumsum([len(xi) for xi in x])[:-1]
        x_r = np.vsplit(model.fit_transform(np.vstack(x)), split)
        if len(x)>1:
            return [xi for xi in x_r]
        else:
            return [x_r[0]]

    # dictionary of models
    models = {
        'PCA' : PCA,
        'IncrementalPCA' : IncrementalPCA,
        'SparsePCA' : SparsePCA,
        'MiniBatchSparsePCA' : MiniBatchSparsePCA,
        'KernelPCA' : KernelPCA,
        'FastICA' : FastICA,
        'FactorAnalysis' : FactorAnalysis,
        'TruncatedSVD' : TruncatedSVD,
        'DictionaryLearning' : DictionaryLearning,
        'MiniBatchDictionaryLearning' : MiniBatchDictionaryLearning,
        'TSNE' : TSNE,
        'Isomap' : Isomap,
        'SpectralEmbedding' : SpectralEmbedding,
        'LocallyLinearEmbedding' : LocallyLinearEmbedding,
        'MDS' : MDS
    }

    # if model is None, just return data
    if (reduce is None) or (ndims is None):
        return x
    else:

        # common format
        x = format_data(x)

        if all([i.shape[1]<=ndims for i in x]):
            return x

        # if reduce is a string, find the corresponding model
        if type(reduce) in [str, np.string_]:
            model = models[reduce]
            model_params = {
                'n_components' : ndims
            }
        # if its a dict, use custom params
        elif type(reduce) is dict:
            if type(reduce['model']) is str:
                model = models[reduce['model']]
                if reduce['params'] is None:
                    model_params = {
                        'n_components' : ndims
                    }
                else:
                    model_params = reduce['params']
        # initialize model
        model = model(**model_params)

        # reduce data
        x_reduced = reduce_list(x, model)

        # return data
        if internal or len(x_reduced)>1:
            return x_reduced
        else:
            return x_reduced[0]
<commit_msg>update reduce docs<commit_after>#!/usr/bin/env python

# libraries
import warnings
import numpy as np

## reduction models
from sklearn.decomposition import PCA, FastICA, IncrementalPCA, KernelPCA, FactorAnalysis, TruncatedSVD, SparsePCA, MiniBatchSparsePCA, DictionaryLearning, MiniBatchDictionaryLearning
from sklearn.manifold import TSNE, MDS, SpectralEmbedding, LocallyLinearEmbedding, Isomap

# internal libraries
from ..tools.df2mat import df2mat
from .._shared.helpers import *

# main function
@memoize
def reduce(x, reduce='IncrementalPCA', ndims=None, internal=False):
    """
    Reduces dimensionality of an array, or list of arrays

    Parameters
    ----------
    x : Numpy array or list of arrays
        Dimensionality reduction using PCA is performed on this array.

    reduce : str or dict
        Decomposition/manifold learning model to use.  Models supported: PCA,
        IncrementalPCA, SparsePCA, MiniBatchSparsePCA, KernelPCA, FastICA,
        FactorAnalysis, TruncatedSVD, DictionaryLearning, MiniBatchDictionaryLearning,
        TSNE, Isomap, SpectralEmbedding, LocallyLinearEmbedding, and MDS. Can be
        passed as a string, but for finer control of the model parameters, pass
        as a dictionary, e.g. reduce={'model' : 'PCA', 'params' : {'whiten' : True}}.
        See scikit-learn specific model docs for details on parameters supported
        for each model.

    ndims : int
        Number of dimensions to reduce

    Returns
    ----------
    x_reduced : Numpy array or list of arrays
        The reduced data with ndims dimensionality is returned.  If the input
        is a list, a list is returned.

    """

    # sub functions
    def reduce_list(x, model):
        split = np.cumsum([len(xi) for xi in x])[:-1]
        x_r = np.vsplit(model.fit_transform(np.vstack(x)), split)
        if len(x)>1:
            return [xi for xi in x_r]
        else:
            return [x_r[0]]

    # dictionary of models
    models = {
        'PCA' : PCA,
        'IncrementalPCA' : IncrementalPCA,
        'SparsePCA' : SparsePCA,
        'MiniBatchSparsePCA' : MiniBatchSparsePCA,
        'KernelPCA' : KernelPCA,
        'FastICA' : FastICA,
        'FactorAnalysis' : FactorAnalysis,
        'TruncatedSVD' : TruncatedSVD,
        'DictionaryLearning' : DictionaryLearning,
        'MiniBatchDictionaryLearning' : MiniBatchDictionaryLearning,
        'TSNE' : TSNE,
        'Isomap' : Isomap,
        'SpectralEmbedding' : SpectralEmbedding,
        'LocallyLinearEmbedding' : LocallyLinearEmbedding,
        'MDS' : MDS
    }

    # if model is None, just return data
    if (reduce is None) or (ndims is None):
        return x
    else:

        # common format
        x = format_data(x)

        if all([i.shape[1]<=ndims for i in x]):
            return x

        # if reduce is a string, find the corresponding model
        if type(reduce) in [str, np.string_]:
            model = models[reduce]
            model_params = {
                'n_components' : ndims
            }
        # if its a dict, use custom params
        elif type(reduce) is dict:
            if type(reduce['model']) is str:
                model = models[reduce['model']]
                if reduce['params'] is None:
                    model_params = {
                        'n_components' : ndims
                    }
                else:
                    model_params = reduce['params']
        # initialize model
        model = model(**model_params)

        # reduce data
        x_reduced = reduce_list(x, model)

        # return data
        if internal or len(x_reduced)>1:
            return x_reduced
        else:
            return x_reduced[0]
<|endoftext|>