← Back to team overview

oerppy-hackers team mailing list archive

[Branch ~oerppy-hackers/oerppy/trunk] Rev 41: * Added get_mondays utility function.

 

------------------------------------------------------------
revno: 41
committer: duncan@xxxxxxxxxx
branch nick: trunk
timestamp: Wed 2011-06-08 21:06:38 -0600
message:
  * Added get_mondays utility function.
  * Split out some of the date logic from the start_of_week utility function.
  * Fixed up csv export function.
  * Added a project class for grouping a project's time-related data.
  * Converted the numbered attributes in the TimeEntry model to groups of
    attributes, greatly improving the ease with which one can iterate/search
    through the project data (e.g., match project names).
  * Added a model for the project.project entity search results.
  * Fixed up MonthExport class.
  * Added logic to get_project_month_timesheets.
  * Split out logic in large week export method into separate private methods.
  * Added project-specific methods to CanonicalQuery.
  * Changed the query class to use a getter for the user_dept data instead of an
    attribute.
modified:
  bin/export_month_timesheets
  oerppy/addons/canonical/query.py
  oerppy/addons/canonical/scripting/export.py
  oerppy/addons/project/model.py
  oerppy/addons/project/query.py
  oerppy/export.py
  oerppy/model.py
  oerppy/scripting/base.py
  oerppy/tests/test_util.py
  oerppy/util.py


--
lp:oerppy
https://code.launchpad.net/~oerppy-hackers/oerppy/trunk

Your team oerppy Hackers is subscribed to branch lp:oerppy.
To unsubscribe from this branch go to https://code.launchpad.net/~oerppy-hackers/oerppy/trunk/+edit-subscription
=== modified file 'bin/export_month_timesheets'
--- bin/export_month_timesheets	2011-06-03 22:03:45 +0000
+++ bin/export_month_timesheets	2011-06-09 03:06:38 +0000
@@ -1,17 +1,21 @@
 #!/usr/bin/env python
+from oerppy import query
 from oerppy.config import get_config_data
 from oerppy.reg import registry
 
 # XXX we probably want to eventually put these addons in their own project
-from oerppy.addons.canonical import query
+from oerppy.addons.canonical import canonical_query
 from oerppy.addons.canonical.scripting import export
+from oerppy.addons.hr import hr_query
 
 
 # get configuration; note that option parsing is handled by the script
 # class(es)
 config_data = get_config_data()
 # setup any addons that we want
-registry.add("canonical", query.CanonicalQuery)
+registry.add("base", query.Query)
+registry.add("hr", hr_query.HRQuery)
+registry.add("canonical", canonical_query.CanonicalQuery)
 # run the exporter
-monthly_export = export.MonthlyExport(config_data)
+monthly_export = export.MonthExport(config_data)
 monthly_export.run()

=== modified file 'oerppy/addons/canonical/query.py'
--- oerppy/addons/canonical/query.py	2011-06-06 18:29:31 +0000
+++ oerppy/addons/canonical/query.py	2011-06-09 03:06:38 +0000
@@ -1,6 +1,6 @@
 from datetime import date, timedelta
 
-from oerppy import query
+from oerppy import query, util
 from oerppy.addons import project
 from oerppy.addons.canonical import const, model
 from oerppy.reg import registry
@@ -10,7 +10,7 @@
 
     def __init__(self, *args, **kwargs):
         super(CanonicalQuery, self).__init__(*args, **kwargs)
-        self.user_dept = None
+        self._cache = {}
 
     def views(self):
         return const.TIMESHEET_VIEWS
@@ -25,8 +25,6 @@
         """
         if not domains:
             domains = []
-        if not self.user_dept:
-            self.user_dept = registry.get_addon("hr").get_departments()
         if report == const.TIMESHEET_HOURS:
             records = self.get_timesheet_hours(domains)
         elif report == const.TIMESHEETS_WEEKLY:
@@ -35,6 +33,14 @@
             raise OpenERPPyParameterError("Unknown value for 'report.'")
         return records
 
+    def get_user_dept(self):
+        user_dept = self._cache.get("user_dept")
+        if user_dept:
+            return user_dept
+        user_dept = registry.get_addon("hr").get_departments()
+        self._cache["user_dept"] = user_dept
+        return user_dept
+
     def get_entity_fields(self, entity_name):
         """
         Query an entity, returning just the column/field names.
@@ -43,7 +49,7 @@
             entity=entity_name,
             query=[],
             fields=[])
-        #import pdb;pdb.set_trace()
+        import pdb;pdb.set_trace()
         return raw_results[0].keys()
 
     def get_oem_users(self):
@@ -61,13 +67,55 @@
         """
         Get the IDs for everyone who uses the OEM timesheets.
         """
+        user_ids = self._cache.get("oem_user_ids")
+        if user_ids:
+            return user_ids
         raw_results = self.client.searchfields(
             entity='canonical.user.category',
             query=[('code','=','OEMTIMESHEET')],
             fields=['user_ids'])
         result = model.UserCategoriesSet(raw_results).get_first()
+        self._cache["oem_user_ids"] = result.user_ids
         return result.user_ids
 
+    def get_project(self, id=None, name=""):
+        if name:
+            query = [('name','=',name)]
+        elif id:
+            query = [('id','=',id)]
+        raw_results = self.client.searchfields(
+            entity='project.project',
+            query=query,
+            fields=[])
+        return project.model.ProjectSet(raw_results).get_first()
+
+    def get_project_id(self, project_name):
+        project = self.get_project(name=project_name)
+        if project:
+            return project.id
+
+    def get_project_user_ids(self, project_name="", project_id=None):
+        if project_name:
+            query = [('name', '=', project_name)]
+        elif project_id:
+            query = [('id', '=', project_id)]
+        else:
+            query = []
+        raw_results = self.client.searchfields(
+            entity='project.project',
+            query=query,
+            fields=['members'])
+        result = project.model.ProjectSet(raw_results).get_first()
+        return result.members
+
+    def get_project_names(self):
+        raw_results = self.client.searchfields(
+            entity='project.project',
+            query=[],
+            fields=['name'])
+        results = project.model.ProjectSet(raw_results)
+        return [x.name for x in results]
+
     def get_timesheet_hours(self, domains=None):
         if not domains:
             domains = []
@@ -75,7 +123,7 @@
             entity="project.task.work",
             query=domains,
             fields=[])
-        return model.TaskWorkSet(raw_results, self.user_dept)
+        return model.TaskWorkSet(raw_results, self.get_user_dept())
 
     def get_timeentry_week_range(self, start_date, end_date):
         """
@@ -100,52 +148,93 @@
         return self.get_timeentry_week_range(
             start_date, last_week.isoformat())
 
-    def get_users_timeentry(self, week, user_ids):
+    def get_users_timeentry(self, user_ids, date):
         """
         Get the timeentry for a given week.
         """
         raw_results = self.client.searchfields(
             'project.timeentry',
-            [('week_start', '=', week.date),('user_id', 'in', user_ids)],
+            [('week_start', '=', date),('user_id', 'in', user_ids)],
             [])
         return project.model.TimeEntrySet(raw_results)
 
-    def get_all_weeks_oem_timesheets(self, domains):
+    def _get_total_times(self, timesheets):
+        """
+        Create a lookup for total time by user ID for the provided timesheets.
+        """
+        total_time = {}
+        if timesheets:
+            for timesheet in timesheets:
+                total_time[timesheet.user.key] = {
+                    'time': timesheet.total_time,
+                    'date': timesheet.week_start,
+                    'state': timesheet.state}
+        return total_time
+
+    def _get_users_time_data(self, timesheets, user_ids, week_date=""):
+        users = registry.get_addon("base").get_user_names(user_ids)
+        # store lookup of total time by user for this week
+        total_time = self._get_total_times(timesheets)
+        for user_id in user_ids:
+            if not week_date:
+                week_date = total_time.get("date")
+            record = {
+                'Week Commencing': week_date,
+                'User': users.get_user(user_id).name,
+                'Department': self.get_user_dept().get(user_id, '?')}
+            if total_time.get(user_id):
+                record['Total Time'] = total_time.get(
+                    user_id,{}).get('time',0)
+                record['State'] = total_time[user_id]['state'].capitalize()
+            else:
+                # missing timesheet
+                record['Total Time'] = 0
+                record['State'] = 'Missing'
+        return record
+    
+    def get_oem_timesheets(self, weeks, user_ids):
+        """
+        Get the timesheet data for the provided weeks and user IDs.
+        """
+        records = []
+        for week in weeks:
+            # get timesheets for the week
+            timesheets = self.get_users_timeentry(user_ids, week.date)
+            record = self._get_users_time_data(user_ids, week.date)
+            records.append(record)
+        return records
+
+    def get_all_weeks_oem_timesheets(self):
         """
         Get the weekly timeentry records, highlighting the missing weeks.
         """
         # get user IDs of timesheet users and the weeks we're interested in
         weeks = self.get_all_timeentry_weeks()
         user_ids = self.get_oem_user_ids()
-        users = registry.get_addon("base").get_user_names(user_ids)
+        return self.get_oem_timesheets(weeks, user_ids)
+
+    def get_month_project_timesheets(self, month, year, project_name, 
+                                     user_ids=None):
+        mondays = util.get_mondays(date(year, month, 1), return_iso=True)
+        if not user_ids:
+            user_ids = self.get_project_user_ids(project_name=project_name)
+        all_timesheets = []
         records = []
-        for week in weeks:
-            # get timesheets for the week
-            timesheets = self.get_users_timeentry(week, user_ids)
-            # store lookup of total time by user for this week
-            total_time = {}
-            if timesheets:
-                for timesheet in timesheets:
-                    total_time[timesheet.user.key] = {
-                        'time': timesheet.total_time,
-                        'state': timesheet.state}
-
-            for user_id in user_ids:
-                record = {
-                    'Week Commencing': week.date,
-                    'User': users.get_user(user_id).name,
-                    'Department': self.user_dept.get(user_id, '?')}
-                if total_time.get(user_id):
-                    record['Total Time'] = total_time.get(
-                        user_id,{}).get('time',0)
-                    record['State'] = total_time[user_id]['state'].capitalize()
-                else:
-                    # missing timesheet
-                    record['Total Time'] = 0
-                    record['State'] = 'Missing'
-                records.append(record)
+        for monday in mondays:
+            week_timesheets = self.get_users_timeentry(user_ids, monday)
+            for timesheet in week_timesheets:
+                for project_data in timesheet.projects:
+                    if project_data.project.value != project_name:
+                        continue
+                    # we want the following data points: user name, project
+                    # name, date, and hours
+                    row = {
+                        "project_name": project_name,
+                        "user": timesheet.user.value,
+                        "date": timesheet.week_start,
+                        "week_total_hours": timesheet.total_time,
+                        "task_category": project_data.task.value,
+                        "work_category": project_data.work.value,
+                        "hours": project_data.hours}
+                    records.append(row)
         return records
-
-    # XXX fill this in
-    def get_month_timesheets(self, month, year):
-        pass

=== modified file 'oerppy/addons/canonical/scripting/export.py'
--- oerppy/addons/canonical/scripting/export.py	2011-06-06 18:29:31 +0000
+++ oerppy/addons/canonical/scripting/export.py	2011-06-09 03:06:38 +0000
@@ -2,11 +2,10 @@
 
 from oerppy import config
 from oerppy.scripting import ExportScript
-
 from oerppy.addons.canonical import const
 
 
-class MonthlyExport(ExportScript):
+class MonthExport(ExportScript):
     """
     Generate an export of a given month's timesheet data.
     """
@@ -15,7 +14,7 @@
         Options for monthly exports.
         """
         now = datetime.now()
-        parser = super(MonthlyExport, self).get_option_parser()
+        parser = super(MonthExport, self).get_option_parser()
         parser.add_option(
             "-m", "--month", dest="month", action="store", type="int",
             default=now.month,
@@ -24,11 +23,21 @@
             "-y", "--year", dest="year", action="store", type="int",
             default=now.year,
             help="the year to export")
+        parser.add_option(
+            "-p", "--project", dest="project_name", action="store",
+            default=now.year,
+            help="the project to get timesheet data for (if not provided, "
+                 "all projects will be returned).")
         return parser
 
     def run(self):
+        # we need to set purge to true here, since the options will have
+        # already been gotten by the parent class at this time, and we need the
+        # options of this class added to those
+        options = self.get_options(purge=True)
         addon = self.service.get_addon("canonical")
-        data = addon.get_monthly_timesheets(self.month, self.year)
+        data = addon.get_month_project_timesheets(
+            options.month, options.year, options.project_name)
         self.export(data)
 
 

=== modified file 'oerppy/addons/project/model.py'
--- oerppy/addons/project/model.py	2011-06-06 18:29:31 +0000
+++ oerppy/addons/project/model.py	2011-06-09 03:06:38 +0000
@@ -1,6 +1,66 @@
 from oerppy import model
 
 
+class Project(model.Model):
+    """
+    This is an object model that maps to search results from OpenERP's
+    "project.project" entity.
+    """
+    def __init__(self, row):
+        self.id = row.get("id")
+        self.code = row.get("code")
+        self.name = row.get("name")
+        self.sequence = row.get("sequence")
+        self.date = row.get("date")
+        self.tasks = row.get("tasks")
+        self.warn_footer = row.get("warn_footer")
+        self.warn_manager = row.get("warn_manager")
+        self.to_invoice = row.get("to_invoice")
+        self.warn_customer = row.get("warn_customer")
+        self.date_start = row.get("date_start")
+        self.priority = row.get("priority")
+        self.state = row.get("state")
+        self.complete_name = row.get("complete_name")
+        self.type = row.get("type")
+        self.description = row.get("description")
+        self.child_ids = row.get("child_ids")
+        self.members = row.get("members")
+        self.active = row.get("active")
+        self.child_complete_ids = row.get("child_complete_ids")
+        self.warn_header = row.get("warn_header")
+        self.line_ids = row.get("line_ids")
+        self.type_ids = row.get("type_ids")
+        self.analytic_account_id = row.get("analytic_account_id")
+        # The following are key/value pairs, each with .key and .value
+        # attributes
+        self.contact_id = model.KeyValuePairField(row.get("contact_id"))
+        self.currency_id = model.KeyValuePairField(row.get("currency_id"))
+        self.company_id = model.KeyValuePairField(row.get("company_id"))
+        self.parent_id = model.KeyValuePairField(row.get("parent_id"))
+        self.pricelist_id = model.KeyValuePairField(row.get("pricelist_id"))
+        self.user_id = model.KeyValuePairField(row.get("user_id"))
+        self.partner_id = model.KeyValuePairField(row.get("partner_id"))
+        # The following are computed attributes
+        self.amount_invoiced = float(row.get("amount_invoiced", 0))
+        self.amount_max = float(row.get("amount_max", 0))
+        self.debit = float(row.get("debit", 0))
+        self.credit = float(row.get("credit", 0))
+        self.balance = float(row.get("balance", 0))
+        self.effective_hours = float(row.get("effective_hours", 0))
+        self.total_hours = float(row.get("total_hours", 0)) 
+        self.planned_hours = float(row.get("planned_hours", 0))
+        self.progress_rate = float(row.get("progress_rate", 0))
+        self.quantity = float(row.get("quantity", 0))
+        self.quantity_max = float(row.get("quantity_max", 0))
+
+
+class ProjectSet(model.ResultSet):
+    """
+    An object for collecting Project records together.
+    """
+    model = Project
+
+
 class TaskWork(model.Model):
     """
     This is an object model that maps to search results from OpenERP's
@@ -16,8 +76,12 @@
         self.task = model.KeyValuePairField(row.get("task_id"))
         self.user = model.KeyValuePairField(row.get("user_id"))
         # The following are computed attributes
-        self.year = int(self.date[0:4])
-        self.month = int(self.date[5:7])
+        if self.date:
+            self.year = int(self.date[0:4])
+            self.month = int(self.date[5:7])
+        else:
+            self.year = None
+            self.month = None
         self.department_name = depts.get(self.user.key)
 
 
@@ -28,6 +92,20 @@
     model = TaskWork
 
 
+class TimeEntryProjectData(object):
+    """
+    A simple object class for storying time-entry project data.
+    """
+    def __init__(self, project, task, work, hours):
+        self.project = project
+        self.task = task
+        self.work = work
+        self.hours = hours
+
+    def __repr__(self):
+        return str(self.__dict__)
+
+
 class TimeEntry(model.Model):
     """
     This is an object model that maps to search results from OpenERP's
@@ -38,54 +116,24 @@
         self.week_start = row.get("week_start")
         self.submitted_date = row.get("submitted")
         self.state = row.get("state")
+        self.projects = []
         # The following are key/value pairs, each with .key and .value
         # attributes 
         self.user = model.KeyValuePairField(row.get("user_id"))
 
-        self.project1 = model.KeyValuePairField(row.get("project1_id"))
-        self.project2 = model.KeyValuePairField(row.get("project2_id"))
-        self.project3 = model.KeyValuePairField(row.get("project3_id"))
-        self.project4 = model.KeyValuePairField(row.get("project4_id"))
-        self.project5 = model.KeyValuePairField(row.get("project5_id"))
-        self.project6 = model.KeyValuePairField(row.get("project6_id"))
-        self.project7 = model.KeyValuePairField(row.get("project7_id"))
-        self.project8 = model.KeyValuePairField(row.get("project8_id"))
-        self.project9 = model.KeyValuePairField(row.get("project9_id"))
-        self.project10 = model.KeyValuePairField(row.get("project10_id"))
+        for index in xrange(1, 11):
+            project_key = "project%s_id" % index
+            task_key = "task%s_id" % index
+            work_key = "work%s_id" % index
+            hours_key = "hours%s_id" % index
+            data = TimeEntryProjectData(
+                model.KeyValuePairField(row.get(project_key)),
+                model.KeyValuePairField(row.get(task_key)),
+                model.KeyValuePairField(row.get(work_key)),
+                float(row.get(hours_key, 0)))
+            self.projects.append(data)
 
-        self.task1 = model.KeyValuePairField(row.get("task1_id"))
-        self.task2 = model.KeyValuePairField(row.get("task2_id"))
-        self.task3 = model.KeyValuePairField(row.get("task3_id"))
-        self.task4 = model.KeyValuePairField(row.get("task4_id"))
-        self.task5 = model.KeyValuePairField(row.get("task5_id"))
-        self.task6 = model.KeyValuePairField(row.get("task6_id"))
-        self.task7 = model.KeyValuePairField(row.get("task7_id"))
-        self.task8 = model.KeyValuePairField(row.get("task8_id"))
-        self.task9 = model.KeyValuePairField(row.get("task9_id"))
-        self.task10 = model.KeyValuePairField(row.get("task10_id"))
-        
-        self.work1 = model.KeyValuePairField(row.get("work1_id"))
-        self.work2 = model.KeyValuePairField(row.get("work2_id"))
-        self.work3 = model.KeyValuePairField(row.get("work3_id"))
-        self.work4 = model.KeyValuePairField(row.get("work4_id"))
-        self.work5 = model.KeyValuePairField(row.get("work5_id"))
-        self.work6 = model.KeyValuePairField(row.get("work6_id"))
-        self.work7 = model.KeyValuePairField(row.get("work7_id"))
-        self.work8 = model.KeyValuePairField(row.get("work8_id"))
-        self.work9 = model.KeyValuePairField(row.get("work9_id"))
-        self.work10 = model.KeyValuePairField(row.get("work10_id"))
-        # The following are computed attributes
-        self.hours1 = float(row.get("hours1"))
-        self.hours2 = float(row.get("hours2"))
-        self.hours3 = float(row.get("hours3"))
-        self.hours4 = float(row.get("hours4"))
-        self.hours5 = float(row.get("hours5"))
-        self.hours6 = float(row.get("hours6"))
-        self.hours7 = float(row.get("hours7"))
-        self.hours8 = float(row.get("hours8"))
-        self.hours9 = float(row.get("hours9"))
-        self.hours10 = float(row.get("hours10"))
-        self.total_time = float(row.get("total_time"))
+        self.total_time = float(row.get("total_time", 0))
 
 
 class TimeEntrySet(model.ResultSet):

=== modified file 'oerppy/addons/project/query.py'
--- oerppy/addons/project/query.py	2011-06-06 18:29:31 +0000
+++ oerppy/addons/project/query.py	2011-06-09 03:06:38 +0000
@@ -10,7 +10,7 @@
             entity="project.task.work",
             query=domains,
             fields=[])
-        return model.TaskWorkSet(raw_results, self.user_dept)
+        return model.TaskWorkSet(raw_results, self.get_user_dept())
 
     def get_all_timeentry_weeks(self):
         """

=== modified file 'oerppy/export.py'
--- oerppy/export.py	2011-06-02 16:51:35 +0000
+++ oerppy/export.py	2011-06-09 03:06:38 +0000
@@ -1,13 +1,13 @@
-def export_csv(self, data, file_handle):
+import csv
+
+
+def export_csv(data, file_handle):
     if not data:
         return
     # Export the records to a CSV file
-    writer = csv.DictWriter(file_handle, data)
-    # Create the header row
-    header = {}
-    for key in data.keys():
-        header[key] = key
-    writer.writerow(header)
+    header_row = data[0].keys()
+    writer = csv.DictWriter(file_handle, header_row)
+    writer.writerow(dict(zip(header_row, header_row)))
     # Output the records
     for row in data:
         writer.writerow(row)

=== modified file 'oerppy/model.py'
--- oerppy/model.py	2011-06-06 18:29:31 +0000
+++ oerppy/model.py	2011-06-09 03:06:38 +0000
@@ -5,7 +5,7 @@
     def __init__(self, data):
         self.value = value
     def __repr__(self):
-        return self.value
+        return self.value or ""
     def __str__(self):
         return str(self.value)
 

=== modified file 'oerppy/scripting/base.py'
--- oerppy/scripting/base.py	2011-06-03 20:29:11 +0000
+++ oerppy/scripting/base.py	2011-06-09 03:06:38 +0000
@@ -1,7 +1,7 @@
 from optparse import OptionParser
 import sys
 
-from oerppy import config, const, service
+from oerppy import config, const, export, service
 
 
 class Script(object):
@@ -75,7 +75,7 @@
             help="the format to export the data in")
         return parser
 
-    def export(self, data):
+    def export(self, data, *args, **kwargs):
         # we need to set purge to true here, since the options will have
         # already been gotten by the parent class at this time, and we need the
         # options of this class added to those
@@ -90,7 +90,7 @@
             import json
             fd.write(json.dumps(data))
         elif options.export_format == const.EXPORT_CSV:
-            export.export_csv(data, fd)
+            export.export_csv(data, fd, *args, **kwargs)
         #elif options.export_format == const.EXPORT_GOOGLE_DOCS:
         #    export.export_google(user, pass, data, ..., ?)
 

=== modified file 'oerppy/tests/test_util.py'
--- oerppy/tests/test_util.py	2011-06-01 22:52:15 +0000
+++ oerppy/tests/test_util.py	2011-06-09 03:06:38 +0000
@@ -72,6 +72,11 @@
 
 class DateTestCase(unittest.TestCase):
 
+    def test_get_datetime(self):
+       date = "2011-01-01"
+       result = util.get_datetime(date)
+       self.assertEqual(result, datetime.date(2011, 1, 1))
+
     def test_start_of_week_with_iso(self):
        date = "2011-01-01"
        result = util.start_of_week(date)
@@ -91,3 +96,36 @@
        date = datetime.date(2011, 1, 1)
        result = util.start_of_week(date, return_iso=True)
        self.assertEqual(result, "2010-12-27")
+
+    def test_get_mondays(self):
+        date = datetime.date(2011,1,1)
+        result = util.get_mondays(date)
+        self.assertEqual(
+            result, 
+            [datetime.date(2011, 1, 3),
+             datetime.date(2011, 1, 10), 
+             datetime.date(2011, 1, 17), 
+             datetime.date(2011, 1, 24), 
+             datetime.date(2011, 1, 31)])
+        result = util.get_mondays("2011-02-01", return_iso=True)
+        self.assertEqual(
+            result, 
+            ['2011-02-07', '2011-02-14', '2011-02-21', '2011-02-28'])
+        result = util.get_mondays("2011-02-28", return_iso=True)
+        self.assertEqual(
+            result,
+            ['2011-02-07', '2011-02-14', '2011-02-21', '2011-02-28'])
+        result = util.get_mondays("2011-08-01", return_iso=True)
+        self.assertEqual(
+            result, 
+            ['2011-08-01', '2011-08-08', '2011-08-15', '2011-08-22',
+             '2011-08-29'])
+        result = util.get_mondays("2011-08-31", return_iso=True)
+        self.assertEqual(
+            result, 
+            ['2011-08-01', '2011-08-08', '2011-08-15', '2011-08-22',
+             '2011-08-29'])
+        result = util.get_mondays("2011-09-21", return_iso=True)
+        self.assertEqual(
+            result,
+            ['2011-09-05', '2011-09-12', '2011-09-19', '2011-09-26'])

=== modified file 'oerppy/util.py'
--- oerppy/util.py	2011-06-02 19:03:01 +0000
+++ oerppy/util.py	2011-06-09 03:06:38 +0000
@@ -1,4 +1,5 @@
 import os
+import calendar
 import datetime
 from urlparse import urlparse, urlunparse
 import xmlrpclib
@@ -6,6 +7,9 @@
 from oerppy import const, exceptions
 
 
+MONDAY = 0
+
+
 def write_default_config(dest_dir=""):
     """
     Create a default config file.
@@ -123,7 +127,7 @@
     return checker
 
 
-def start_of_week(date, return_iso=False):
+def get_datetime(date):
     # check to see if the date is in ISO date format
     if isinstance(date, basestring):
         date_object = datetime.date(
@@ -131,7 +135,29 @@
     # or if it's a date object already
     elif isinstance(date, datetime.date):
         date_object = date
+    return date_object
+
+
+def start_of_week(date, return_iso=False):
+    date_object = get_datetime(date)
     monday = date_object + datetime.timedelta(days=-date_object.weekday())
     if return_iso:
         monday = monday.isoformat()
     return monday
+
+
+def get_mondays(date, return_iso=False):
+    date_object = get_datetime(date)
+    _get_date = partial(datetime.date, date_object.year, date_object.month)
+    month = calendar.monthcalendar(date_object.year, date_object.month)
+    mondays = []
+    for week in month:
+        # days outside of the month are given the value 0; we skip those
+        day = week[MONDAY]
+        if day == 0:
+            continue
+        monday = _get_date(day)
+        if return_iso:
+            monday = monday.isoformat()
+        mondays.append(monday)
+    return mondays