From 9d00e1df61c77682d0d4bd66f26ec7ea05aa8446 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Fri, 26 Jul 2013 13:22:49 +0530 Subject: [PATCH] [calendar] [feature] Added Recurring Events --- core/doctype/event/event.js | 8 +++ core/doctype/event/event.py | 98 ++++++++++++++++++++++++++++++++---- core/doctype/event/event.txt | 87 ++++++++++++++++++++++++++++++-- docs/docs.attributions.md | 52 +++++++++++++++++++ webnotes/utils/__init__.py | 8 +-- 5 files changed, 233 insertions(+), 20 deletions(-) create mode 100644 docs/docs.attributions.md diff --git a/core/doctype/event/event.js b/core/doctype/event/event.js index f469f882a8..387356de17 100644 --- a/core/doctype/event/event.js +++ b/core/doctype/event/event.js @@ -24,6 +24,14 @@ cur_frm.cscript.add_list_breadcrumb = function(appframe) { appframe.add_breadcrumb("icon-calendar", "Calendar/Event", "Calendar"); } +cur_frm.cscript.repeat_on = function(doc, cdt, cdn) { + if(doc.repeat_on==="Every Day") { + $.each(["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"], function(i,v) { + cur_frm.set_value(v, 1); + }) + } +} + cur_frm.cscript.refresh = function(doc, cdt, cdn) { } diff --git a/core/doctype/event/event.py b/core/doctype/event/event.py index 1465f27c8c..c2e649bd66 100644 --- a/core/doctype/event/event.py +++ b/core/doctype/event/event.py @@ -22,6 +22,10 @@ from __future__ import unicode_literals import webnotes +from webnotes.utils import getdate, cint, add_months, date_diff, add_days + +weekdays = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"] + class DocType: def __init__(self, d, dl): self.doc, self.doclist = d, dl @@ -30,19 +34,91 @@ class DocType: def get_events(start, end): roles = webnotes.get_roles() events = webnotes.conn.sql("""select name, subject, - starts_on, ends_on, owner, all_day, event_type + starts_on, ends_on, owner, all_day, event_type, repeat_this_event, repeat_on, + monday, tuesday, wednesday, thursday, friday, saturday, sunday from tabEvent where ( - (starts_on between %s and %s) - or (ends_on between %s and %s) + (starts_on between '%(start)s' and '%(end)s') + or (ends_on between '%(start)s' and '%(end)s') + ) or ( + starts_on < '%(start)s' and ifnull(repeat_this_event,0)=1 and + ifnull(repeat_till, "3000-01-01 00:00:00") > '%(start)s' ) - and (event_type='Public' or owner=%s + and (event_type='Public' or owner='%(user)s' or exists(select * from `tabEvent User` where - `tabEvent User`.parent=tabEvent.name and person=%s) + `tabEvent User`.parent=tabEvent.name and person='%(user)s') or exists(select * from `tabEvent Role` where `tabEvent Role`.parent=tabEvent.name - and `tabEvent Role`.role in ('%s'))) - order by starts_on""" % ('%s', '%s', '%s', '%s', '%s', '%s', - "', '".join(roles)), (start, end, start, end, - webnotes.session.user, webnotes.session.user), as_dict=1) - - return events \ No newline at end of file + and `tabEvent Role`.role in ('%(roles)s'))) + order by starts_on""" % { + "start": start, + "end": end, + "user": webnotes.session.user, + "roles": "', '".join(roles) + }, as_dict=1) + + # process recurring events + start = start.split(" ")[0] + end = end.split(" ")[0] + add_events = [] + + def add_event(e, date): + new_event = e.copy() + new_event.starts_on = date + " " + e.starts_on.split(" ")[1] + new_event.ends_on = date + " " + e.ends_on.split(" ")[1] + add_events.append(new_event) + + for e in events: + if e.repeat_this_event: + event_start, time_str = e.starts_on.split(" ") + if e.repeat_on=="Every Year": + start_year = cint(start.split("-")[0]) + end_year = cint(end.split("-")[0]) + event_start = "-".join(event_start.split("-")[1:]) + + # repeat for all years in period + for year in range(start_year, end_year+1): + date = str(year) + "-" + event_start + if date > start and date < end: + add_event(e, date) + events.remove(e) + + if e.repeat_on=="Every Month": + date = start.split("-")[0] + "-" + start.split("-")[1] + "-" + event_start.split("-")[2] + + # last day of month issue, start from prev month! + try: + getdate(date) + except ValueError: + date = date.split("-") + date = date[0] + "-" + str(cint(date[1]) - 1) + "-" + date[2] + + start_from = date + for i in xrange(int(date_diff(end, start) / 30) + 3): + if date >= start and date <= end and date >= event_start: + add_event(e, date) + date = add_months(start_from, i+1) + events.remove(e) + + if e.repeat_on=="Every Week": + weekday = getdate(event_start).weekday() + # monday is 0 + start_weekday = getdate(start).weekday() + + # start from nearest weeday after last monday + date = add_days(start, weekday - start_weekday) + + for cnt in xrange(int(date_diff(end, start) / 7) + 3): + if date >= start and date <= end and date >= event_start: + add_event(e, date) + + date = add_days(date, 7) + events.remove(e) + + if e.repeat_on=="Every Day": + for cnt in xrange(date_diff(end, start) + 1): + date = add_days(start, cnt) + if date <= end and e[weekdays[getdate(date).weekday()]]: + add_event(e, date) + events.remove(e) + + return events + add_events \ No newline at end of file diff --git a/core/doctype/event/event.txt b/core/doctype/event/event.txt index 5999c2c3bc..8e973b3d49 100644 --- a/core/doctype/event/event.txt +++ b/core/doctype/event/event.txt @@ -2,7 +2,7 @@ { "creation": "2013-06-10 13:17:47", "docstatus": 0, - "modified": "2013-07-05 14:36:46", + "modified": "2013-07-26 13:19:41", "modified_by": "Administrator", "owner": "Administrator" }, @@ -72,6 +72,12 @@ "fieldname": "column_break_4", "fieldtype": "Column Break" }, + { + "doctype": "DocField", + "fieldname": "all_day", + "fieldtype": "Check", + "label": "All Day" + }, { "doctype": "DocField", "fieldname": "starts_on", @@ -88,9 +94,84 @@ }, { "doctype": "DocField", - "fieldname": "all_day", + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, + { + "doctype": "DocField", + "fieldname": "repeat_this_event", "fieldtype": "Check", - "label": "All Day" + "label": "Repeat this Event" + }, + { + "depends_on": "repeat_this_event", + "doctype": "DocField", + "fieldname": "repeat_on", + "fieldtype": "Select", + "label": "Repeat On", + "options": "\nEvery Day\nEvery Week\nEvery Month\nEvery Year" + }, + { + "depends_on": "repeat_this_event", + "description": "Leave blank if you have not decided the end date.", + "doctype": "DocField", + "fieldname": "repeat_till", + "fieldtype": "Date", + "label": "Repeat Till" + }, + { + "doctype": "DocField", + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "doctype": "DocField", + "fieldname": "monday", + "fieldtype": "Check", + "label": "Monday" + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "doctype": "DocField", + "fieldname": "tuesday", + "fieldtype": "Check", + "label": "Tuesday" + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "doctype": "DocField", + "fieldname": "wednesday", + "fieldtype": "Check", + "label": "Wednesday" + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "doctype": "DocField", + "fieldname": "thursday", + "fieldtype": "Check", + "label": "Thursday" + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "doctype": "DocField", + "fieldname": "friday", + "fieldtype": "Check", + "label": "Friday" + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "doctype": "DocField", + "fieldname": "saturday", + "fieldtype": "Check", + "label": "Saturday" + }, + { + "depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"", + "doctype": "DocField", + "fieldname": "sunday", + "fieldtype": "Check", + "label": "Sunday" }, { "doctype": "DocField", diff --git a/docs/docs.attributions.md b/docs/docs.attributions.md new file mode 100644 index 0000000000..b226402608 --- /dev/null +++ b/docs/docs.attributions.md @@ -0,0 +1,52 @@ +--- +{ + "_label": "Attributions" +} +--- +ERPNext is made using these amazing Open Source Projects. + +### System Requirements: + +1. [Linux Operating System](http://en.wikipedia.org/wiki/Linux): The operating system that brought a revolution in Open Source software. +1. [MySQL Database](http://www.mysql.com/): The world's most popular Open Source Database. +1. [Apache HTTPD web server](http://httpd.apache.org): The Number One HTTP Server On The Internet. +1. [Memcached](http://memcached.org/): Free & open source, high-performance, distributed memory object caching system. + +### Tools and Languages: + +1. [Python Programming Language](http://python.org/): The "batteries included" language that lets you write elegant code, quickly. With third-party modules: + - MySQLdb + - pytz + - jinja2 + - markdown2 + - dateutil + - termcolor + - python-memcached + - requests + - chardet + - pygeoip + - dropbox + - google-api-python-client +1. [Git - Source Code Management](http://git-scm.com/): Git - Source Code Management + +### Libraries and Frameworks + +1. [wnframework](https://github.com/webnotes/wnframework): The full stack Python + Javascript web application framework on which ERPNext is built. +1. [JQuery](http://jquery.com/): The write less, do more Javascript Library. +1. [JQuery UI](http://jqueryui.com/): A curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library. +1. [Bootstrap](http://twitter.github.com/bootstrap/index.html): Sleek, intuitive, and powerful front-end framework for faster and easier web development. +1. [Font Awesome](http://fortawesome.github.com/Font-Awesome/): The iconic font designed for use with Twitter Bootstrap. +1. [SlickGrid](https://github.com/mleibman/SlickGrid): A lightning fast JavaScript grid/spreadsheet. +1. [FullCalendar](http://arshaw.com/fullcalendar/): FullCalendar is a jQuery plugin that provides a full-sized, drag and drop calendar. +1. [Flot Charting Library](http://www.flotcharts.org/): Attractive JavaScript plotting for jQuery. +1. [Ace Code Editor](http://ace.ajax.org/): High Performance Code Editor for the web. +1. [JQuery.Gantt](http://taitems.github.com/jQuery.Gantt/): Draw Gantt charts with the famous jQuery ease of development. +1. [JQuery Tag-it](http://aehlke.github.io/tag-it/): Simple and configurable tag editing widget with autocomplete support. +1. [JSColor](http://jscolor.com/): HTML/Javascript Color Picker. +1. [QUnit](http://qunitjs.com/): A JavaScript Unit Testing framework. +1. [Downloadify](https://github.com/dcneiner/Downloadify): A tiny javascript + Flash library that enables the creation and download of text files without server interaction. +1. [GeoLite](http://dev.maxmind.com/geoip/geolite): GeoLite data created by MaxMind, available from https://www.maxmind.com. + +--- + +For more information please write to us at support@erpnext.com \ No newline at end of file diff --git a/webnotes/utils/__init__.py b/webnotes/utils/__init__.py index 17924c73d2..9aab825765 100644 --- a/webnotes/utils/__init__.py +++ b/webnotes/utils/__init__.py @@ -152,11 +152,7 @@ def getdate(string_date): if " " in string_date: string_date = string_date.split(" ")[0] - try: - return datetime.datetime.strptime(string_date, "%Y-%m-%d").date() - except ValueError: - webnotes.msgprint("Cannot understand date - '%s'" % \ - (string_date,), raise_exception=1) + return datetime.datetime.strptime(string_date, "%Y-%m-%d").date() def add_to_date(date, years=0, months=0, days=0): """Adds `days` to the given date""" @@ -165,7 +161,7 @@ def add_to_date(date, years=0, months=0, days=0): date = getdate(date) else: raise Exception, "Start date required" - + from dateutil.relativedelta import relativedelta date += relativedelta(years=years, months=months, days=days)