def email_unread_notifications(timeframe): """ Looks for all unread notifcations and sends each user one email with a summary. Marks any sent notifications as "read". timeframe may be: * 'daily' - only send to users who have the daily email setting * 'weekly' - only send to users who have the weekly email setting * 'all' - send all notifications """ users = db.notifications.find({"read": False}).distinct("uid") for uid in users: profile = UserProfile(id=uid) if profile.settings["email_notifications"] != timeframe and timeframe != 'all': continue notifications = NotificationSet().unread_for_user(uid) try: user = User.objects.get(id=uid) except User.DoesNotExist: continue message_html = render_to_string("email/notifications_email.html", { "notifications": notifications, "recipient": user.first_name }) #message_text = util.strip_tags(message_html) subject = "New Activity on Sefaria from %s" % notifications.actors_string() from_email = "Sefaria <*****@*****.**>" to = user.email msg = EmailMultiAlternatives(subject, message_html, from_email, [to]) msg.content_subtype = "html" # Main content is now text/html #msg.attach_alternative(message_text, "text/plain") msg.send() notifications.mark_read(via="email")
def email_unread_notifications(timeframe): """ Looks for all unread notifications and sends each user one email with a summary. Marks any sent notifications as "read". timeframe may be: * 'daily' - only send to users who have the daily email setting * 'weekly' - only send to users who have the weekly email setting * 'all' - send all notifications """ from sefaria.model.notification import NotificationSet users = db.notifications.find({"read": False, "is_global": False}).distinct("uid") for uid in users: profile = UserProfile(id=uid) if profile.settings["email_notifications"] != timeframe and timeframe != 'all': continue notifications = NotificationSet().unread_personal_for_user(uid) if notifications.count() == 0: continue try: user = User.objects.get(id=uid) except User.DoesNotExist: continue if "interface_language" in profile.settings: translation.activate(profile.settings["interface_language"][0:2]) message_html = render_to_string("email/notifications_email.html", {"notifications": notifications, "recipient": user.first_name}) #message_text = util.strip_tags(message_html) actors_string = notifications.actors_string() verb = "have" if " and " in actors_string else "has" subject = "%s %s new activity on Sefaria" % (actors_string, verb) from_email = "Sefaria <*****@*****.**>" to = user.email msg = EmailMultiAlternatives(subject, message_html, from_email, [to]) msg.content_subtype = "html" # Main content is now text/html #msg.attach_alternative(message_text, "text/plain") msg.send() notifications.mark_read(via="email") if "interface_language" in profile.settings: translation.deactivate()
def save_sheet(sheet, user_id, search_override=False, rebuild_nodes=False): """ Saves sheet to the db, with user_id as owner. """ def next_sheet_id(): last_id = db.sheets.find().sort([['id', -1]]).limit(1) if last_id.count(): sheet_id = last_id.next()["id"] + 1 else: sheet_id = 1 return sheet_id sheet["dateModified"] = datetime.now().isoformat() status_changed = False if "id" in sheet: new_sheet = False existing = db.sheets.find_one({"id": sheet["id"]}) if sheet["lastModified"] != existing["dateModified"]: # Don't allow saving if the sheet has been modified since the time # that the user last received an update existing["error"] = "Sheet updated." existing["rebuild"] = True return existing del sheet["lastModified"] if sheet["status"] != existing["status"]: status_changed = True sheet["views"] = existing["views"] # prevent updating views sheet["owner"] = existing["owner"] # prevent updating owner sheet["likes"] = existing["likes"] if "likes" in existing else [ ] # prevent updating likes existing.update(sheet) sheet = existing else: new_sheet = True sheet["dateCreated"] = datetime.now().isoformat() if "status" not in sheet: sheet["status"] = "unlisted" sheet["owner"] = user_id sheet["views"] = 1 #ensure that sheet sources have nodes (primarily for sheets posted via API) nextNode = sheet.get("nextNode", 1) sheet["nextNode"] = nextNode checked_sources = [] for source in sheet["sources"]: if "node" not in source: source["node"] = nextNode nextNode += 1 checked_sources.append(source) sheet["sources"] = checked_sources if status_changed and not new_sheet: if sheet["status"] == "public" and "datePublished" not in sheet: # PUBLISH sheet["datePublished"] = datetime.now().isoformat() record_sheet_publication(sheet["id"], user_id) # record history broadcast_sheet_publication(user_id, sheet["id"]) if sheet["status"] != "public": # UNPUBLISH delete_sheet_publication(sheet["id"], user_id) # remove history UserStorySet({ "storyForm": "publishSheet", "data.publisher": user_id, "data.sheet_id": sheet["id"] }).delete() NotificationSet({ "type": "sheet publish", "content.publisher_id": user_id, "content.sheet_id": sheet["id"] }).delete() sheet["includedRefs"] = refs_in_sources(sheet.get("sources", [])) if rebuild_nodes: sheet = rebuild_sheet_nodes(sheet) if new_sheet: # mongo enforces a unique sheet id, get a new id until a unique one has been found while True: try: sheet["id"] = next_sheet_id() db.sheets.insert_one(sheet) break except DuplicateKeyError: pass else: db.sheets.find_one_and_replace({"id": sheet["id"]}, sheet) if "tags" in sheet: update_sheet_tags(sheet["id"], sheet["tags"]) if sheet[ "status"] == "public" and SEARCH_INDEX_ON_SAVE and not search_override: try: index_name = search.get_new_and_current_index_names( "sheet")['current'] search.index_sheet(index_name, sheet["id"]) except: logger.error(u"Failed index on " + str(sheet["id"])) ''' global last_updated last_updated[sheet["id"]] = sheet["dateModified"] ''' return sheet
def save_sheet(sheet, user_id, search_override=False): """ Saves sheet to the db, with user_id as owner. """ sheet["dateModified"] = datetime.now().isoformat() status_changed = False if "id" in sheet: existing = db.sheets.find_one({"id": sheet["id"]}) if sheet["lastModified"] != existing["dateModified"]: # Don't allow saving if the sheet has been modified since the time # that the user last received an update existing["error"] = "Sheet updated." existing["rebuild"] = True return existing del sheet["lastModified"] if sheet["status"] != existing["status"]: status_changed = True sheet["views"] = existing["views"] # prevent updating views sheet["owner"] = existing["owner"] # prevent updating owner sheet["likes"] = existing["likes"] if "likes" in existing else [ ] # prevent updating likes existing.update(sheet) sheet = existing else: sheet["dateCreated"] = datetime.now().isoformat() lastId = db.sheets.find().sort([['id', -1]]).limit(1) if lastId.count(): sheet["id"] = lastId.next()["id"] + 1 else: sheet["id"] = 1 if "status" not in sheet: sheet["status"] = "unlisted" sheet["owner"] = user_id sheet["views"] = 1 if status_changed: if sheet["status"] == "public" and "datePublished" not in sheet: # PUBLISH sheet["datePublished"] = datetime.now().isoformat() record_sheet_publication(sheet["id"], user_id) broadcast_sheet_publication(user_id, sheet["id"]) if sheet["status"] != "public": # UNPUBLISH delete_sheet_publication(sheet["id"], user_id) NotificationSet({ "type": "sheet publish", "content.publisher_id": user_id, "content.sheet_id": sheet["id"] }).delete() db.sheets.update({"id": sheet["id"]}, sheet, True, False) if sheet[ "status"] == "public" and SEARCH_INDEX_ON_SAVE and not search_override: index_name = search.get_new_and_current_index_names()['current'] search.index_sheet(index_name, sheet["id"]) global last_updated last_updated[sheet["id"]] = sheet["dateModified"] return sheet
def email_unread_notifications(timeframe): """ Looks for all unread notifications and sends each user one email with a summary. Marks any sent notifications as "read". timeframe may be: * 'daily' - only send to users who have the daily email setting * 'weekly' - only send to users who have the weekly email setting * 'all' - send all notifications """ from sefaria.model.notification import NotificationSet detect_potential_spam_message_notifications() users = db.notifications.find({ "read": False, "is_global": False }).distinct("uid") for uid in users: profile = UserProfile(id=uid) if profile.settings[ "email_notifications"] != timeframe and timeframe != 'all': continue notifications = NotificationSet().unread_personal_for_user(uid) if len(notifications) == 0: continue try: user = User.objects.get(id=uid) except User.DoesNotExist: continue if "interface_language" in profile.settings: translation.activate(profile.settings["interface_language"][0:2]) message_html = render_to_string("email/notifications_email.html", { "notifications": notifications, "recipient": user.first_name }) actors_string = notifications.actors_string() # TODO Hebrew subjects if actors_string: verb = "have" if " and " in actors_string else "has" subject = "%s %s new activity on Sefaria" % (actors_string, verb) elif notifications.like_count() > 0: noun = "likes" if notifications.like_count() > 1 else "like" subject = "%d new %s on your Source Sheet" % ( notifications.like_count(), noun) from_email = "Sefaria Notifications <*****@*****.**>" to = user.email msg = EmailMultiAlternatives(subject, message_html, from_email, [to]) msg.content_subtype = "html" try: msg.send() notifications.mark_read(via="email") except AnymailRecipientsRefused: print('bad email address: {}'.format(to)) if "interface_language" in profile.settings: translation.deactivate()
def unread_notification_count(self): from sefaria.model.notification import NotificationSet return NotificationSet().unread_for_user(self.id).count()
def recent_notifications(self): from sefaria.model.notification import NotificationSet return NotificationSet().recent_for_user(self.id)
def unread_notifications_count_for_user(uid): """Returns the number of unread notifications belonging to user uid""" # Check for globals to add... from sefaria.model.notification import NotificationSet return NotificationSet().unread_for_user(uid).count()
def save_sheet(sheet, user_id, search_override=False): """ Saves sheet to the db, with user_id as owner. """ sheet["dateModified"] = datetime.now().isoformat() status_changed = False if "id" in sheet: existing = db.sheets.find_one({"id": sheet["id"]}) if sheet["lastModified"] != existing["dateModified"]: # Don't allow saving if the sheet has been modified since the time # that the user last received an update existing["error"] = "Sheet updated." existing["rebuild"] = True return existing del sheet["lastModified"] if sheet["status"] != existing["status"]: status_changed = True sheet["views"] = existing["views"] # prevent updating views sheet["owner"] = existing["owner"] # prevent updating owner sheet["likes"] = existing["likes"] if "likes" in existing else [ ] # prevent updating likes existing.update(sheet) sheet = existing else: sheet["dateCreated"] = datetime.now().isoformat() lastId = db.sheets.find().sort([['id', -1]]).limit(1) if lastId.count(): sheet["id"] = lastId.next()["id"] + 1 else: sheet["id"] = 1 if "status" not in sheet: sheet["status"] = "unlisted" sheet["owner"] = user_id sheet["views"] = 1 #ensure that sheet sources have nodes (primarily for sheets posted via API) nextNode = sheet.get("nextNode", 1) sheet["nextNode"] = nextNode checked_sources = [] for source in sheet["sources"]: if "node" not in source: source["node"] = nextNode nextNode += 1 checked_sources.append(source) sheet["sources"] = checked_sources if status_changed: if sheet["status"] == "public" and "datePublished" not in sheet: # PUBLISH sheet["datePublished"] = datetime.now().isoformat() record_sheet_publication(sheet["id"], user_id) broadcast_sheet_publication(user_id, sheet["id"]) if sheet["status"] != "public": # UNPUBLISH delete_sheet_publication(sheet["id"], user_id) NotificationSet({ "type": "sheet publish", "content.publisher_id": user_id, "content.sheet_id": sheet["id"] }).delete() db.sheets.update({"id": sheet["id"]}, sheet, True, False) if "tags" in sheet: update_sheet_tags(sheet["id"], sheet["tags"]) if sheet[ "status"] == "public" and SEARCH_INDEX_ON_SAVE and not search_override: try: index_name = search.get_new_and_current_index_names( "sheet")['current'] search.index_sheet(index_name, sheet["id"]) except: logger.error("Failed index on " + str(sheet["id"])) ''' global last_updated last_updated[sheet["id"]] = sheet["dateModified"] ''' return sheet
def save_sheet(sheet, user_id, search_override=False, rebuild_nodes=False): """ Saves sheet to the db, with user_id as owner. """ def next_sheet_id(): last_id = db.sheets.find().sort([['id', -1]]).limit(1) if last_id.count(): sheet_id = last_id.next()["id"] + 1 else: sheet_id = 1 return sheet_id sheet["dateModified"] = datetime.now().isoformat() status_changed = False if "id" in sheet: new_sheet = False existing = db.sheets.find_one({"id": sheet["id"]}) if sheet["lastModified"] != existing["dateModified"]: # Don't allow saving if the sheet has been modified since the time # that the user last received an update existing["error"] = "Sheet updated." existing["rebuild"] = True return existing del sheet["lastModified"] if sheet["status"] != existing["status"]: status_changed = True old_topics = existing.get("topics", []) topics_diff = topic_list_diff(old_topics, sheet.get("topics", [])) # Protected fields -- can't be set from outside sheet["views"] = existing["views"] sheet["owner"] = existing["owner"] sheet["likes"] = existing["likes"] if "likes" in existing else [] if "noindex" in existing: sheet["noindex"] = existing["noindex"] existing.update(sheet) sheet = existing else: new_sheet = True sheet["dateCreated"] = datetime.now().isoformat() if "status" not in sheet: sheet["status"] = "unlisted" sheet["owner"] = user_id sheet["views"] = 1 old_topics = [] topics_diff = topic_list_diff(old_topics, sheet.get("topics", [])) #ensure that sheet sources have nodes (primarily for sheets posted via API) nextNode = sheet.get("nextNode", 1) sheet["nextNode"] = nextNode checked_sources = [] for source in sheet["sources"]: if "node" not in source: source["node"] = nextNode nextNode += 1 checked_sources.append(source) sheet["sources"] = checked_sources if status_changed and not new_sheet: if sheet["status"] == "public" and "datePublished" not in sheet: # PUBLISH sheet["datePublished"] = datetime.now().isoformat() record_sheet_publication(sheet["id"], user_id) # record history broadcast_sheet_publication(user_id, sheet["id"]) if sheet["status"] != "public": # UNPUBLISH delete_sheet_publication(sheet["id"], user_id) # remove history UserStorySet({ "storyForm": "publishSheet", "uid": user_id, "data.publisher": user_id, "data.sheet_id": sheet["id"] }).delete() NotificationSet({ "type": "sheet publish", "uid": user_id, "content.publisher_id": user_id, "content.sheet_id": sheet["id"] }).delete() sheet["includedRefs"] = refs_in_sources(sheet.get("sources", [])) sheet["expandedRefs"] = model.Ref.expand_refs(sheet["includedRefs"]) sheet["sheetLanguage"] = get_sheet_language(sheet) if rebuild_nodes: sheet = rebuild_sheet_nodes(sheet) if new_sheet: # mongo enforces a unique sheet id, get a new id until a unique one has been found while True: try: sheet["id"] = next_sheet_id() db.sheets.insert_one(sheet) break except DuplicateKeyError: pass else: db.sheets.find_one_and_replace({"id": sheet["id"]}, sheet) if len(topics_diff["added"]) or len(topics_diff["removed"]): update_sheet_topics(sheet["id"], sheet.get("topics", []), old_topics) sheet = db.sheets.find_one({"id": sheet["id"]}) if status_changed and sheet["status"] == "public": # Publish, update sheet topic links as though all are new - add links for all update_sheet_topic_links(sheet["id"], sheet["topics"], []) elif status_changed and sheet["status"] != "public": # Unpublish, update sheet topic links as though there are now none - remove links for all update_sheet_topic_links(sheet["id"], [], old_topics) if sheet[ "status"] == "public" and SEARCH_INDEX_ON_SAVE and not search_override: try: index_name = search.get_new_and_current_index_names( "sheet")['current'] search.index_sheet(index_name, sheet["id"]) except: logger.error("Failed index on " + str(sheet["id"])) return sheet