oerppy-hackers team mailing list archive
-
oerppy-hackers team
-
Mailing list archive
-
Message #00021
[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