def fix_users(field): log.debug(field) if is_undefined(field): return undefined if not field: return undefined if isinstance(field, basestring): return set([build_user_key(id) for id in re.split("[\\s,;]+", field) if len(id) > 0]) return [build_user_key(user) for user in field]
def fix_users(field): log.debug(field) if is_undefined(field): return undefined if not field: return undefined if isinstance(field, basestring): return set([ build_user_key(id) for id in re.split("[\\s,;]+", field) if len(id) > 0 ]) return [build_user_key(user) for user in field]
def issue_search(viewer, simple=None, query=None, complex=None): permission_verify(viewer, "issue", "read") if not complex: if query: complex = query_to_complex_search(query) else: # Status is open and assigned to me, or closing and verified by me. complex = { "boolean" : "or", "sub" : [ { "boolean" : "and", "sub" : [ { "field" : "status", "operator" : "in", "value" : [ "triage", "assigned", "working" ] }, { "field" : "assignees", "operator" : "==", "value" : [ viewer ] } ] }, { "boolean" : "and", "sub" : [ { "field" : "status", "operator" : "in", "value" : [ "fixed", "rejected" ] }, { "field" : "verifiers", "operator" : "==", "value" : [ viewer ] } ] } ] } result = [] ndb_query, first_sort = complex_search_to_ndb_query(complex) if permission_is_root(viewer): if ndb_query: dataset = Issue.query().filter(ndb_query) else: dataset = Issue.query().filter() else: privacy_query = ndb.OR(Issue.privacy == "public", Issue.assignees == build_user_key(viewer), Issue.reporters == build_user_key(viewer), Issue.verifiers == build_user_key(viewer), Issue.cc == build_user_key(viewer)) if ndb_query: dataset = Issue.query().filter(ndb.AND(ndb_query, privacy_query)) else: dataset = Issue.query().filter(privacy_query) if first_sort: dataset = dataset.order(first_sort) else: dataset = dataset.order(-Issue.score, -Issue.created) for issue in dataset: issue.history = [] result.append(to_model(viewer, issue)) return result
def task_create(actor, key=None, task_ids=None, name=undefined, active=True, **kwargs): key = key or task_key(task_ids or name) task = Task(key=key) task.name = task_ids[-1] task.parent = key.parent() task.created_by = build_user_key(actor) return task_update(actor, task=task, active=True, name=name, **kwargs)
def tag_create(actor, key=None, tag_ids=None, name=undefined, active=True, **kwargs): key = key or tag_key(tag_ids or name) tag = Tag(key=key) tag.name = tag_ids[-1] tag.parent = key.parent() tag.created_by = build_user_key(actor) return tag_update(actor, tag=tag, active=True, name=name, **kwargs)
def project_create(actor, key=None, client_id=None, project_ids=None, name=undefined, active=True, **kwargs): key = key or project_key(client_id, project_ids or name) project = Project(key=key) project.name = project_ids[-1] project.parent = key.parent() project.created_by = build_user_key(actor) return project_update(actor, project=project, active=True, name=name, **kwargs)
def check_out(actor, asset=None, asset_key=None, asset_id=None, checked_out_to=undefined, project=undefined, expected=undefined, timezoneoffset=None, **ignored): asset = asset or get(actor, asset_key=asset_key, asset_id=asset_id) if asset.checkout: raise IllegalError("Asset is already checked out") checkout = AssetCheckout(parent=asset.key) checkout.checked_out_by = build_user_key(actor) checkout.checked_out_to = build_user_key(actor) checkout.condition_out = asset.condition if is_defined(expected): if expected == "": expected = None else: if timezoneoffset: offset = timedelta(minutes=int(timezoneoffset)) client_time = datetime.utcnow() - offset parsed_time = lib.parsedatetime.Calendar().parse( expected, client_time) else: offset = datetime.timedelta(0) parsed_time = lib.parsedatetime.Calendar().parse(expected) if parsed_time[1] == 1: checkout.expected = datetime(*parsed_time[0][:3]) + offset else: checkout.expected = datetime(*parsed_time[0][:6]) + offset if is_defined(checked_out_to) and checked_out_to: checkout.checked_out_to = build_user_key(checked_out_to) if is_defined(project) and project: checkout.project = project checkout.put() asset.checkout = checkout.key asset.put() return checkout
def project_deactivate(actor, client_id=None, project_ids=None, key=None, project=None, **ignored): project = project_get(client_id, project_ids, key, project) project.updated_by = build_user_key(actor) project.active = False project.put()
def issue_create(actor, key=None, issue_id=None, name=undefined, active=True, **kwargs): if "status" not in kwargs: kwargs["status"] = "triage" if "priority" not in kwargs: kwargs["priority"] = 2 if "severity" not in kwargs: kwargs["severity"] = 2 if "reporters" not in kwargs or not kwargs["reporters"]: kwargs["reporters"] = [ actor ] if "assignees" not in kwargs or not kwargs["assignees"]: kwargs["assignees"] = [ actor ] if "verifiers" not in kwargs or not kwargs["verifiers"]: kwargs["verifiers"] = [ actor ] issue = Issue() issue.created_by = build_user_key(actor) issue.privacy = "public" return issue_update(actor, issue=issue, active=True, name=name, **kwargs)
def client_update(actor, client_id=None, key=None, client=None, name=undefined, active=undefined, **ignored): client = client or (key or client_key(client_id)).get() if is_defined(name): client.name = name if is_defined(active): client.active = active client.updated_by = build_user_key(actor) client.put() return to_model(client)
def project_update(actor, client_id=None, project_ids=None, key=None, project=None, name=undefined, active=undefined, **ignored): project = project or (key or project_key(client_id, project_ids)).get() if is_defined(name): project.name = name if is_defined(active): project.active = active project.updated_by = build_user_key(actor) project.put() return to_model(project)
def remark_create(actor, target_key, text, subtext=None, blobs=[]): remark = Remark(parent=target_key) remark.target = target_key remark.text = text remark.subtext = subtext remark.blobs = blobs or [] remark.created_by = build_user_key(actor) remark.put() for blob in remark.blobs: blob_claim(actor, blob_key=blob, target=remark.key) return to_model(actor, remark)
def task_update(actor, task_ids=None, key=None, task=None, name=undefined, active=undefined, **ignored): task = task or (key or task_key(task_ids)).get() if is_defined(name): task.name = name if is_defined(active): task.active = active task.updated_by = build_user_key(actor) task.put() return to_model(task)
def tag_update(actor, tag_ids=None, key=None, tag=None, name=undefined, active=undefined, **ignored): tag = tag or (key or tag_key(tag_ids)).get() if is_defined(name): tag.name = name if is_defined(active): tag.active = active tag.updated_by = build_user_key(actor) tag.put() return to_model(tag)
def check_out(actor, asset=None, asset_key=None, asset_id=None, checked_out_to=undefined, project=undefined, expected=undefined, timezoneoffset=None, **ignored): asset = asset or get(actor, asset_key=asset_key, asset_id=asset_id) if asset.checkout: raise IllegalError("Asset is already checked out") checkout = AssetCheckout(parent=asset.key) checkout.checked_out_by = build_user_key(actor) checkout.checked_out_to = build_user_key(actor) checkout.condition_out = asset.condition if is_defined(expected): if expected == "": expected = None else: if timezoneoffset: offset = timedelta(minutes=int(timezoneoffset)) client_time = datetime.utcnow() - offset parsed_time = lib.parsedatetime.Calendar().parse(expected, client_time) else: offset = datetime.timedelta(0) parsed_time = lib.parsedatetime.Calendar().parse(expected) if parsed_time[1] == 1: checkout.expected = datetime(*parsed_time[0][:3]) + offset else: checkout.expected = datetime(*parsed_time[0][:6]) + offset if is_defined(checked_out_to) and checked_out_to: checkout.checked_out_to = build_user_key(checked_out_to) if is_defined(project) and project: checkout.project = project checkout.put() asset.checkout = checkout.key asset.put() return checkout
def tag_apply(viewer, target, tag, **ignored): if permission_check(viewer, "tag", "apply") or permission_is_root(viewer): key = tag_key(tag) if not AppliedTag.query(AppliedTag.tag == key, AppliedTag.target == target, ancestor=target).get(): new_tag = AppliedTag(parent=target) new_tag.applied_by = build_user_key(viewer) new_tag.tag = key new_tag.target = target new_tag.put() log.debug("Tag %s applied" % key) return "/tags/%s" % "/".join(tag) else: log.debug("Tag already applied") else: log.debug("Not allowed")
def issue_get(viewer, issue_id=None, key=None, issue=None, silent=False): result = issue or (key or issue_key(issue_id)).get() if result: if result.privacy == "public" or \ build_user_key(viewer) in result.cc + result.assignees + result.reporters + result.verifiers or \ permission_is_root(viewer): result.history = remark_list(viewer, result.key) return result elif silent: return None else: raise NotAllowedError() elif silent: return None else: raise NotFoundError()
def check_in(actor, asset=None, asset_key=None, asset_id=None, condition=undefined, **ignored): asset = asset or get(actor, asset_key=asset_key, asset_id=asset_id) if not asset.checkout: raise IllegalError("Asset is not checked out") checkout = asset.checkout.get() checkout.checked_in_by = build_user_key(actor) checkout.checked_in = datetime.now() checkout.condition_in = asset.condition if is_defined(condition): checkout.condition_in = condition checkout.put() asset.checkout = None asset.condition = checkout.condition_in asset.put() return checkout
def issue_create(actor, key=None, issue_id=None, name=undefined, active=True, **kwargs): if "status" not in kwargs: kwargs["status"] = "triage" if "priority" not in kwargs: kwargs["priority"] = 2 if "severity" not in kwargs: kwargs["severity"] = 2 if "reporters" not in kwargs or not kwargs["reporters"]: kwargs["reporters"] = [actor] if "assignees" not in kwargs or not kwargs["assignees"]: kwargs["assignees"] = [actor] if "verifiers" not in kwargs or not kwargs["verifiers"]: kwargs["verifiers"] = [actor] issue = Issue() issue.created_by = build_user_key(actor) issue.privacy = "public" return issue_update(actor, issue=issue, active=True, name=name, **kwargs)
def tag_deactivate(actor, tag_ids=None, key=None, tag=None, **ignored): tag = tag_get(tag_ids, key, tag) tag.updated_by = build_user_key(actor) tag.active = False tag.put()
def issue_update(actor, issue_id=None, key=None, issue=None, summary=undefined, project=undefined, status=undefined, priority=undefined, severity=undefined, reporters=undefined, assignees=undefined, verifiers=undefined, cc=undefined, depends_on=undefined, blocking=undefined, privacy=undefined, due_date=undefined, body="", send_mail=True, blobs=undefined, **args): issue = issue or (key or issue_key(issue_id)).get() header = "" is_root = permission_is_root(actor) if not is_root and issue.privacy != "public" and \ build_user_key(actor) not in issue.cc + issue.assignees + issue.reporters + issue.verifiers: raise NotAllowedError() blob_list = None to_recipients = set([]) if issue.assignees: to_recipients.update(issue.assignees) if issue.reporters: to_recipients.update(issue.reporters) if issue.verifiers: to_recipients.update(issue.verifiers) cc_recipients = set(issue.cc) if is_root or issue.privacy != "secure" or build_user_key( actor) in issue.assignees + issue.verifiers: # Rewrite input types if necessary def fix_users(field): log.debug(field) if is_undefined(field): return undefined if not field: return undefined if isinstance(field, basestring): return set([ build_user_key(id) for id in re.split("[\\s,;]+", field) if len(id) > 0 ]) return [build_user_key(user) for user in field] reporters = fix_users(reporters) assignees = fix_users(assignees) verifiers = fix_users(verifiers) cc = fix_users(cc) if is_defined(depends_on): depends_on = set([ issue_key(id) for id in re.split("[\\s,;]+", depends_on) if len(id) > 0 ]) if is_defined(blocking): blocking = set([ issue_key(id) for id in re.split("[\\s,;]+", blocking) if len(id) > 0 ]) if is_defined(due_date): if due_date == "": due_date = None else: if "timezoneoffset" in args: offset = datetime.timedelta( minutes=int(args["timezoneoffset"])) client_time = datetime.datetime.utcnow() - offset parsed_time = lib.parsedatetime.Calendar().parse( due_date, client_time) log.debug("client_time = %s" % client_time) else: offset = datetime.timedelta(0) parsed_time = lib.parsedatetime.Calendar().parse(due_date) if parsed_time[1] == 1: due_date = datetime.datetime(*parsed_time[0][:3]) + offset else: due_date = datetime.datetime(*parsed_time[0][:6]) + offset # Update all fields if is_defined(summary) and summary != issue.summary: header = header + "**Summary:** " + summary + " \n" issue.summary = summary issue.summary_index = set(re.split("[^\\w\\d]+", summary.lower())) if is_defined(project) and project != issue.project: header = header + "**Project:** " + project + " \n" issue.project = project if is_defined(status) and status != issue.status: if not status in issue_transitions: raise IllegalError("Status not recognized") if not status in issue_transitions[issue.status]: raise IllegalError("Status transition not allowed") header = header + "**Status:** " + status + " \n" issue.status = status if is_defined(priority) and int(priority) != issue.priority: header = header + "**Priority:** " + str(priority) + " \n" issue.priority = int(priority) if is_defined(severity) and int(severity) != issue.severity: header = header + "**Severity:** " + str(severity) + " \n" issue.severity = int(severity) if is_defined(reporters) and reporters != set(issue.reporters): log.debug("Reporters: %s" % reporters) header = header + "**Reporters:** " + ", ".join( [user.id() for user in reporters]) + " \n" issue.reporters = reporters if is_defined(assignees) and assignees != set(issue.assignees): header = header + "**Assignees:** " + ", ".join( [user.id() for user in assignees]) + " \n" issue.assignees = assignees if is_defined(verifiers) and verifiers != set(issue.verifiers): header = header + "**Verifiers:** " + ", ".join( [user.id() for user in verifiers]) + " \n" issue.verifiers = verifiers if is_defined(cc) and cc != set(issue.cc): header = header + "**CC:** " + ", ".join( [user.id() for user in cc]) + " \n" issue.cc = list(cc) if is_defined(depends_on) and depends_on != set(issue.depends_on): header = header + "**Depends On:** " + ", ".join( [str(iss.id()) for iss in depends_on]) + " \n" issue.depends_on = list(depends_on) if is_defined(blocking) and blocking != set(issue.blocking): header = header + "**Blocking:** " + ", ".join( [str(iss.id()) for iss in blocking]) + " \n" issue.blocking = list(blocking) if is_defined(privacy) and privacy != issue.privacy: header = header + "**Privacy:** " + privacy + " \n" issue.privacy = privacy if is_defined(due_date) and due_date != issue.due_date: header = header + "**Due Date:** " + str(due_date) + " UTC \n" issue.due_date = due_date if is_defined(blobs) and blobs: blob_list = build_blob_keys(blobs) header = header + "**Attachments:** %s File(s) \n" % len( blob_list) # Fix up missing stuff if not issue.reporters: issue.reporters = [build_user_key(actor)] if not issue.verifiers: issue.verifiers = [build_user_key(actor)] if not issue.assignees: issue.assignees = [build_user_key(actor)] to_recipients.update(issue.assignees) to_recipients.update(issue.reporters) to_recipients.update(issue.verifiers) cc_recipients.update(issue.cc) issue.score, issue.score_description = calculate_issue_score(issue) issue.text_index = set(issue.text_index) | \ set(issue.summary_index) | \ set(re.split("[^\\w\\d]+", body.lower())) issue.updated_by = build_user_key(actor) issue.put() issue.history = [ remark_create(actor, issue.key, body.strip(), header.strip(), blobs=blob_list) ] if send_mail: settings = get_system_settings() if "host" in settings and settings["host"] and settings["host"] != "": host = settings["host"] else: host = "%s.appspot.com" % app_identity.get_application_id() message_id = "<issue-%s@%s>" % (issue.key.id(), app_identity.get_application_id()) url = "http://%s/issues/%s" % (host, issue.key.id()) text = "%s\n\n%s\n\n%s" % (header, body, url) html = "<div style='font-size: 0.8em'>%s</div><div>%s</div><div>%s</div>" % \ (lib.markdown.markdown(header), lib.markdown.markdown(body), url) try: if len(cc_recipients) > 0: mail.send_mail(sender=actor.user.email(), to=[user.id() for user in to_recipients], cc=[user.id() for user in cc_recipients], reply_to=actor.user.email(), subject="[" + str(issue.key.id()) + "] " + issue.summary, body=text, html=html, headers={ "In-Reply-To": message_id, "References": message_id }) else: mail.send_mail(sender=actor.user.email(), to=[user.id() for user in to_recipients], reply_to=actor.user.email(), subject="[" + str(issue.key.id()) + "] " + issue.summary, body=text, html=html, headers={ "In-Reply-To": message_id, "References": message_id }) log.debug("Email sent to %s" % (to_recipients | cc_recipients)) except: log.warn("Email quota exceeded, email not sent") log.debug(text) return to_model(actor, issue)
def complex_search_to_ndb_query(query): if not query: return None, None sort_order = None phrases = [] for phrase in query["sub"]: if "boolean" in phrase: new_phrase, new_order = complex_search_to_ndb_query(phrase) if new_phrase: phrases.append(new_phrase) sort_order = sort_order or new_order elif phrase["field"] == "text": pass else: if phrase["field"] in ["priority", "severity"]: values = [int(value) for value in phrase["value"]] elif phrase["field"] in ["created", "updated"]: values = [] elif phrase["field"] in [ "updated_by", "created_by", "assignees", "reporters", "verifiers", "cc" ]: values = [build_user_key(value) for value in phrase["value"]] elif phrase["field"] in ["summary_index", "text_index"]: values = [value.lower() for value in phrase["value"]] else: values = phrase["value"] field = getattr(Issue, phrase["field"]) subphrases = [] if phrase["operator"] == "=" or phrase[ "operator"] == "==" or phrase["operator"] == "in": for value in values: subphrases.append(field == value) if len(subphrases) > 0: phrases.append(ndb.OR(*subphrases)) elif phrase["operator"] == "!=": sort_order = field for value in values: subphrases.append(field != value) if len(subphrases) > 0: phrases.append(ndb.AND(*subphrases)) elif phrase["operator"] == ">": sort_order = field for value in values: subphrases.append(field > value) if len(subphrases) > 0: phrases.append(ndb.AND(*subphrases)) elif phrase["operator"] == ">=": sort_order = field for value in values: subphrases.append(field >= value) if len(subphrases) > 0: phrases.append(ndb.AND(*subphrases)) elif phrase["operator"] == "<": sort_order = field for value in values: subphrases.append(field < value) if len(subphrases) > 0: phrases.append(ndb.AND(*subphrases)) elif phrase["operator"] == "<=": sort_order = field for value in values: subphrases.append(field <= value) if len(subphrases) > 0: phrases.append(ndb.AND(*subphrases)) if len(phrases) > 0: if query["boolean"] == "or": return ndb.OR(*phrases), sort_order else: return ndb.AND(*phrases), sort_order else: return None, None
def issue_search(viewer, simple=None, query=None, complex=None): permission_verify(viewer, "issue", "read") if not complex: if query: complex = query_to_complex_search(query) else: # Status is open and assigned to me, or closing and verified by me. complex = { "boolean": "or", "sub": [{ "boolean": "and", "sub": [{ "field": "status", "operator": "in", "value": ["triage", "assigned", "working"] }, { "field": "assignees", "operator": "==", "value": [viewer] }] }, { "boolean": "and", "sub": [{ "field": "status", "operator": "in", "value": ["fixed", "rejected"] }, { "field": "verifiers", "operator": "==", "value": [viewer] }] }] } result = [] ndb_query, first_sort = complex_search_to_ndb_query(complex) if permission_is_root(viewer): if ndb_query: dataset = Issue.query().filter(ndb_query) else: dataset = Issue.query().filter() else: privacy_query = ndb.OR(Issue.privacy == "public", Issue.assignees == build_user_key(viewer), Issue.reporters == build_user_key(viewer), Issue.verifiers == build_user_key(viewer), Issue.cc == build_user_key(viewer)) if ndb_query: dataset = Issue.query().filter(ndb.AND(ndb_query, privacy_query)) else: dataset = Issue.query().filter(privacy_query) if first_sort: dataset = dataset.order(first_sort) else: dataset = dataset.order(-Issue.score, -Issue.created) for issue in dataset: issue.history = [] result.append(to_model(viewer, issue)) return result
def issue_deactivate(actor, issue_id=None, key=None, issue=None, **ignored): issue = issue_get(actor, issue_id, key, issue) issue.updated_by = build_user_key(actor) issue.active = False issue.put()
def create(actor, asset_id=None, asset_key=None, **kwargs): asset = Asset(key=asset_key or key(asset_id)) asset.created_by = build_user_key(actor) asset.condition = "new" return update(actor, asset=asset, **kwargs)
def client_deactivate(actor, client_id=None, key=None, client=None, **ignored): client = client_get(client_id, key, client) client.updated_by = build_user_key(actor) client.active = False client.put()
def complex_search_to_ndb_query(query): if not query: return None, None sort_order = None phrases = [] for phrase in query["sub"]: if "boolean" in phrase: new_phrase, new_order = complex_search_to_ndb_query(phrase) if new_phrase: phrases.append(new_phrase) sort_order = sort_order or new_order elif phrase["field"] == "text": pass else: if phrase["field"] in [ "priority", "severity" ]: values = [ int(value) for value in phrase["value"] ] elif phrase["field"] in [ "created", "updated" ]: values = [] elif phrase["field"] in [ "updated_by", "created_by", "assignees", "reporters", "verifiers", "cc" ]: values = [ build_user_key(value) for value in phrase["value"] ] elif phrase["field"] in [ "summary_index", "text_index" ]: values = [ value.lower() for value in phrase["value"] ] else: values = phrase["value"] field = getattr(Issue, phrase["field"]) subphrases = [] if phrase["operator"] == "=" or phrase["operator"] == "==" or phrase["operator"] == "in": for value in values: subphrases.append(field == value) if len(subphrases) > 0: phrases.append(ndb.OR(*subphrases)) elif phrase["operator"] == "!=": sort_order = field for value in values: subphrases.append(field != value) if len(subphrases) > 0: phrases.append(ndb.AND(*subphrases)) elif phrase["operator"] == ">": sort_order = field for value in values: subphrases.append(field > value) if len(subphrases) > 0: phrases.append(ndb.AND(*subphrases)) elif phrase["operator"] == ">=": sort_order = field for value in values: subphrases.append(field >= value) if len(subphrases) > 0: phrases.append(ndb.AND(*subphrases)) elif phrase["operator"] == "<": sort_order = field for value in values: subphrases.append(field < value) if len(subphrases) > 0: phrases.append(ndb.AND(*subphrases)) elif phrase["operator"] == "<=": sort_order = field for value in values: subphrases.append(field <= value) if len(subphrases) > 0: phrases.append(ndb.AND(*subphrases)) if len(phrases) > 0: if query["boolean"] == "or": return ndb.OR(*phrases), sort_order else: return ndb.AND(*phrases), sort_order else: return None, None
def task_deactivate(actor, task_ids=None, key=None, task=None, **ignored): task = task_get(task_ids, key, task) task.updated_by = build_user_key(actor) task.active = False task.put()
def client_create(actor, key=None, client_id=None, name=undefined, active=True, **kwargs): key = key or client_key(client_id or name) client = Client(key=key) client.name = client_id client.created_by = build_user_key(actor) return client_update(actor, client=client, active=True, name=name, **kwargs)
def issue_update(actor, issue_id=None, key=None, issue=None, summary=undefined, project=undefined, status=undefined, priority=undefined, severity=undefined, reporters=undefined, assignees=undefined, verifiers=undefined, cc=undefined, depends_on=undefined, blocking=undefined, privacy=undefined, due_date=undefined, body="", send_mail=True, blobs=undefined, **args): issue = issue or (key or issue_key(issue_id)).get() header = "" is_root = permission_is_root(actor) if not is_root and issue.privacy != "public" and \ build_user_key(actor) not in issue.cc + issue.assignees + issue.reporters + issue.verifiers: raise NotAllowedError() blob_list = None to_recipients = set([]) if issue.assignees: to_recipients.update(issue.assignees) if issue.reporters: to_recipients.update(issue.reporters) if issue.verifiers: to_recipients.update(issue.verifiers) cc_recipients = set(issue.cc) if is_root or issue.privacy != "secure" or build_user_key(actor) in issue.assignees + issue.verifiers: # Rewrite input types if necessary def fix_users(field): log.debug(field) if is_undefined(field): return undefined if not field: return undefined if isinstance(field, basestring): return set([build_user_key(id) for id in re.split("[\\s,;]+", field) if len(id) > 0]) return [build_user_key(user) for user in field] reporters = fix_users(reporters) assignees = fix_users(assignees) verifiers = fix_users(verifiers) cc = fix_users(cc) if is_defined(depends_on): depends_on = set([issue_key(id) for id in re.split("[\\s,;]+", depends_on) if len(id) > 0]) if is_defined(blocking): blocking = set([issue_key(id) for id in re.split("[\\s,;]+", blocking) if len(id) > 0]) if is_defined(due_date): if due_date == "": due_date = None else: if "timezoneoffset" in args: offset = datetime.timedelta(minutes=int(args["timezoneoffset"])) client_time = datetime.datetime.utcnow() - offset parsed_time = lib.parsedatetime.Calendar().parse(due_date, client_time) log.debug("client_time = %s" % client_time) else: offset = datetime.timedelta(0) parsed_time = lib.parsedatetime.Calendar().parse(due_date) if parsed_time[1] == 1: due_date = datetime.datetime(*parsed_time[0][:3]) + offset else: due_date = datetime.datetime(*parsed_time[0][:6]) + offset # Update all fields if is_defined(summary) and summary != issue.summary: header = header + "**Summary:** " + summary + " \n" issue.summary = summary issue.summary_index = set(re.split("[^\\w\\d]+", summary.lower())) if is_defined(project) and project != issue.project: header = header + "**Project:** " + project + " \n" issue.project = project if is_defined(status) and status != issue.status: if not status in issue_transitions: raise IllegalError("Status not recognized") if not status in issue_transitions[issue.status]: raise IllegalError("Status transition not allowed") header = header + "**Status:** " + status + " \n" issue.status = status if is_defined(priority) and int(priority) != issue.priority: header = header + "**Priority:** " + str(priority) + " \n" issue.priority = int(priority) if is_defined(severity) and int(severity) != issue.severity: header = header + "**Severity:** " + str(severity) + " \n" issue.severity = int(severity) if is_defined(reporters) and reporters != set(issue.reporters): log.debug("Reporters: %s" % reporters) header = header + "**Reporters:** " + ", ".join([user.id() for user in reporters]) + " \n" issue.reporters = reporters if is_defined(assignees) and assignees != set(issue.assignees): header = header + "**Assignees:** " + ", ".join([user.id() for user in assignees]) + " \n" issue.assignees = assignees if is_defined(verifiers) and verifiers != set(issue.verifiers): header = header + "**Verifiers:** " + ", ".join([user.id() for user in verifiers]) + " \n" issue.verifiers = verifiers if is_defined(cc) and cc != set(issue.cc): header = header + "**CC:** " + ", ".join([user.id() for user in cc]) + " \n" issue.cc = list(cc) if is_defined(depends_on) and depends_on != set(issue.depends_on): header = header + "**Depends On:** " + ", ".join([str(iss.id()) for iss in depends_on]) + " \n" issue.depends_on = list(depends_on) if is_defined(blocking) and blocking != set(issue.blocking): header = header + "**Blocking:** " + ", ".join([str(iss.id()) for iss in blocking]) + " \n" issue.blocking = list(blocking) if is_defined(privacy) and privacy != issue.privacy: header = header + "**Privacy:** " + privacy + " \n" issue.privacy = privacy if is_defined(due_date) and due_date != issue.due_date: header = header + "**Due Date:** " + str(due_date) + " UTC \n" issue.due_date = due_date if is_defined(blobs) and blobs: blob_list = build_blob_keys(blobs) header = header + "**Attachments:** %s File(s) \n" % len(blob_list) # Fix up missing stuff if not issue.reporters: issue.reporters = [ build_user_key(actor) ] if not issue.verifiers: issue.verifiers = [ build_user_key(actor) ] if not issue.assignees: issue.assignees = [ build_user_key(actor) ] to_recipients.update(issue.assignees) to_recipients.update(issue.reporters) to_recipients.update(issue.verifiers) cc_recipients.update(issue.cc) issue.score, issue.score_description = calculate_issue_score(issue) issue.text_index = set(issue.text_index) | \ set(issue.summary_index) | \ set(re.split("[^\\w\\d]+", body.lower())) issue.updated_by = build_user_key(actor) issue.put() issue.history = [ remark_create(actor, issue.key, body.strip(), header.strip(), blobs=blob_list) ] if send_mail: settings = get_system_settings() if "host" in settings and settings["host"] and settings["host"] != "": host = settings["host"] else: host = "%s.appspot.com" % app_identity.get_application_id() message_id = "<issue-%s@%s>" % (issue.key.id(), app_identity.get_application_id()) url = "http://%s/issues/%s" % (host, issue.key.id()) text = "%s\n\n%s\n\n%s" % (header, body, url) html = "<div style='font-size: 0.8em'>%s</div><div>%s</div><div>%s</div>" % \ (lib.markdown.markdown(header), lib.markdown.markdown(body), url) try: if len(cc_recipients) > 0: mail.send_mail(sender=actor.user.email(), to=[user.id() for user in to_recipients], cc=[user.id() for user in cc_recipients], reply_to=actor.user.email(), subject="[" + str(issue.key.id()) + "] " + issue.summary, body=text, html=html, headers={"In-Reply-To": message_id, "References": message_id }) else: mail.send_mail(sender=actor.user.email(), to=[user.id() for user in to_recipients], reply_to=actor.user.email(), subject="[" + str(issue.key.id()) + "] " + issue.summary, body=text, html=html, headers={"In-Reply-To": message_id, "References": message_id }) log.debug("Email sent to %s" % (to_recipients | cc_recipients)) except: log.warn("Email quota exceeded, email not sent") log.debug(text) return to_model(actor, issue)