def revert_api(request, ref, lang, version, revision): """ API for reverting a text segment to a previous revision. """ if not request.user.is_authenticated(): return jsonResponse({"error": "You must be logged in to revert changes."}) if request.method != "POST": return jsonResponse({"error": "Unsupported HTTP method."}) revision = int(revision) version = version.replace("_", " ") ref = norm_ref(ref) if not ref: # pass along the error message if norm_ref failed return jsonResponse(parse_ref(ref)) existing = get_text(ref, commentary=0, version=version, lang=lang) if "error" in existing: return jsonResponse(existing) text = { "versionTitle": version, "versionSource": existing["versionSource"] if lang == "en" else existing["heVersionSource"], "language": lang, "text": text_at_revision(ref, version, lang, revision) } return jsonResponse(save_text(ref, text, request.user.id, type="revert text"))
def count_sources(sources, sheet_id): global refs, texts, categories global sources_count, comments_count, outside_count, untrans_count global untrans_texts, untrans_categories, untrans_refs global fragments, fragments_count for s in sources: if "ref" in s: sources_count += 1 pRef = parse_ref(s["ref"]) if "error" in pRef: continue refs[s["ref"]] += 1 texts[pRef["book"]] += 1 categories[pRef["categories"][0]] += 1 if not is_ref_translated(s["ref"]): untrans_categories[pRef["categories"][0]] +=1 untrans_texts[pRef["book"]] += 1 untrans_refs[s["ref"]] += 1 untrans_count += 1 en = strip_tags(s.get("text", {}).get("en", "")) if len(en) > 25: fragments[s["ref"]].append(sheet_id) fragments_count += 1 if "subsources" in s: count_sources(s["subsources"], sheet_id) elif "comment" in s: comments_count += 1 elif "outsideText" in s or "outsideBiText" in s: outside_count += 1
def get_sheets_for_ref(ref, pad=True, context=1): """ Returns a list of sheets that include ref, formating as need for the Client Sidebar. """ ref = norm_ref(ref, pad=pad, context=context) ref_re = make_ref_re(ref) results = [] sheets = db.sheets.find({"included_refs": {"$regex": ref_re}, "status": {"$in": LISTED_SHEETS}}, {"id": 1, "title": 1, "owner": 1, "included_refs": 1}) for sheet in sheets: # Check for multiple matching refs within this sheet matched = [ref for ref in sheet["included_refs"] if regex.match(ref_re, ref)] for match in matched: com = {} anchorRef = parse_ref(match) com["category"] = "Sheets" com["type"] = "sheet" com["owner"] = sheet["owner"] com["_id"] = str(sheet["_id"]) com["anchorRef"] = match com["anchorVerse"] = anchorRef["sections"][-1] com["public"] = True com["commentator"] = user_link(sheet["owner"]) com["text"] = "<a class='sheetLink' href='/sheets/%d'>%s</a>" % (sheet["id"], strip_tags(sheet["title"])) results.append(com) return results
def url_ref(value): if not value: return "" pRef = parse_ref(value, pad=False) if "error" in pRef: return value link = '<a href="/' + url(value) + '">' + value + '</a>' return mark_safe(link)
def url_ref(value): if value in url_ref_cache: return url_ref_cache[value] if not value: return "" pRef = parse_ref(value, pad=False) if "error" in pRef: return value link = '<a href="/' + url(value) + '">' + value + '</a>' url_ref_cache[value] = mark_safe(link) return url_ref_cache[value]
def reviews_api(request, ref=None, lang=None, version=None, review_id=None): if request.method == "GET": if ref and lang and version: pRef = parse_ref(ref) if "error" in pRef: return jsonResponse(pRef) ref = make_ref(pRef) version = version.replace("_", " ") reviews = get_reviews(ref, lang, version) last_edit = get_last_edit_date(ref, lang, version) score_since_last_edit = get_review_score_since_last_edit(ref, lang, version, reviews=reviews, last_edit=last_edit) for r in reviews: r["date"] = r["date"].isoformat() response = { "ref": ref, "lang": lang, "version": version, "reviews": reviews, "reviewCount": len(reviews), "scoreSinceLastEdit": score_since_last_edit, "lastEdit": last_edit.isoformat() if last_edit else None, } elif review_id: response = {} return jsonResponse(response) elif request.method == "POST": if not request.user.is_authenticated(): return jsonResponse({"error": "You must be logged in to write reviews."}) j = request.POST.get("json") if not j: return jsonResponse({"error": "No post JSON."}) j = json.loads(j) response = save_review(j, request.user.id) return jsonResponse(response) elif request.method == "DELETE": if not review_id: return jsonResponse({"error": "No review ID given for deletion."}) return jsonResponse(delete_review(review_id, request.user.id)) else: return jsonResponse({"error": "Unsuported HTTP method."})
def links_api(request, link_id_or_ref=None): """ API for textual links. Currently also handles post notes. """ #TODO: can we distinguish between a link_id (mongo id) for POSTs and a ref for GETs? if request.method == "GET": if link_id_or_ref is None: return jsonResponse({"error": "Missing text identifier"}) #TODO is there are better way to validate the ref from GET params? pRef = parse_ref(link_id_or_ref) if "error" in pRef: return jsonResponse(pRef) with_text = int(request.GET.get("with_text", 1)) return jsonResponse(get_links(link_id_or_ref, with_text)) if request.method == "POST": j = request.POST.get("json") if not j: return jsonResponse({"error": "Missing 'json' parameter in post data."}) j = json.loads(j) if isinstance(j, list): func = save_link_batch else: # use the correct function if params indicate this is a note save func = save_note if "type" in j and j["type"] == "note" else save_link if not request.user.is_authenticated(): key = request.POST.get("apikey") if not key: return jsonResponse({"error": "You must be logged in or use an API key to add, edit or delete links."}) apikey = db.apikeys.find_one({"key": key}) if not apikey: return jsonResponse({"error": "Unrecognized API key."}) return jsonResponse(func(j, apikey["uid"], method="API")) else: @csrf_protect def protected_link_post(request): response = func(j, request.user.id) return jsonResponse(response) return protected_link_post(request) if request.method == "DELETE": if not link_id_or_ref: return jsonResponse({"error": "No link id given for deletion."}) return jsonResponse(delete_link(link_id_or_ref, request.user.id)) return jsonResponse({"error": "Unsuported HTTP method."})
def ref_link(value, absolute=False): """ Transform a ref into an <a> tag linking to that ref. e.g. "Genesis 1:3" -> "<a href='/Genesis.1.2'>Genesis 1:2</a>" """ if value in ref_link_cache: return ref_link_cache[value] if not value: return "" pRef = parse_ref(value, pad=False) if "error" in pRef: return value link = '<a href="/' + url_ref(value) + '">' + value + '</a>' ref_link_cache[value] = mark_safe(link) return ref_link_cache[value]
def versions_api(request, ref): """ API for retrieving available text versions list of a ref. """ pRef = parse_ref(ref) if "error" in pRef: return jsonResponse(pRef) versions = db.texts.find({"title": pRef["book"]}) results = [] for v in versions: results.append({ "title": v["versionTitle"], "source": v["versionSource"], "langauge": v["language"] }) return jsonResponse(results)
def segment_history(request, ref, lang, version): """ View revision history for the text segment named by ref / lang / version. """ nref = norm_ref(ref) if not nref: return HttpResponse("There was an error in your text reference: %s" % parse_ref(ref)["error"]) version = version.replace("_", " ") filter_type = request.GET.get("type", None) history = text_history(nref, version, lang, filter_type=filter_type) email = request.user.email if request.user.is_authenticated() else False return render_to_response('activity.html', {'activity': history, "single": True, "ref": nref, "lang": lang, "version": version, 'email': email, 'filter_type': filter_type, }, RequestContext(request))
def translation_flow(request, ref): """ Assign a user a paritcular bit of text to translate within 'ref', either a text title or category. """ ref = ref.replace("_", " ") generic_response = { "title": "Help Translate %s" % ref, "content": "" } categories = get_text_categories() next_text = None next_section = None # expire old locks before checking for a currently unlocked text sefaria.locks.expire_locks() pRef = parse_ref(ref, pad=False) if "error" not in pRef and len(pRef["sections"]) == 0: # ref is an exact text Title text = norm_ref(ref) # normalize URL if request.path != "/translate/%s" % url_ref(text): return redirect("/translate/%s" % url_ref(text), permanent=True) # Check for completion if get_percent_available(text) == 100: generic_response["content"] = "<h3>Sefaria now has a complete translation of %s</h3>But you can still contribute in other ways.</h3> <a href='/contribute'>Learn More.</a>" % ref return render_to_response('static/generic.html', generic_response, RequestContext(request)) if "random" in request.GET: # choose a ref from a random section within this text skip = int(request.GET.get("skip")) if "skip" in request.GET else None assigned_ref = random_untranslated_ref_in_text(text, skip=skip) if assigned_ref: next_section = parse_ref(assigned_ref)["sections"][0] elif "section" in request.GET: # choose the next ref within the specified section next_section = int(request.GET["section"]) assigned_ref = next_untranslated_ref_in_text(text, section=next_section) else: # choose the next ref in this text in order assigned_ref = next_untranslated_ref_in_text(text) if not assigned_ref: generic_response["content"] = "All remaining sections in %s are being worked on by other contributors. Work on <a href='/translate/%s'>another text</a> for now." % (text, ref) return render_to_response('static/generic.html', generic_response, RequestContext(request)) elif "error" not in pRef and len(pRef["sections"]) > 0: # ref is a citation to a particular location in a text # for now, send this to the edit_text view return edit_text(request, ref) elif "error" in pRef and ref in categories: # ref is a text Category cat = ref # Check for completion if get_percent_available(cat) == 100: generic_response["content"] = "<h3>Sefaria now has a complete translation of %s</h3>But you can still contribute in other ways.</h3> <a href='/contribute'>Learn More.</a>" % ref return render_to_response('static/generic.html', generic_response, RequestContext(request)) if "random" in request.GET: # choose a random text from this cateogory skip = int(request.GET.get("skip")) if "skip" in request.GET else None text = random_untranslated_text_in_category(cat, skip=skip) assigned_ref = next_untranslated_ref_in_text(text) next_text = text elif "text" in request.GET: # choose the next text requested in URL text = norm_ref(request.GET["text"]) next_text = text if get_percent_available(text) == 100: generic_response["content"] = "%s is complete! Work on <a href='/translate/%s'>another text</a>." % (next, ref) return render_to_response('static/generic.html', generic_response, RequestContext(request)) assigned_ref = next_untranslated_ref_in_text(text) if "error" in assigned_ref: generic_response["content"] = "All remaining sections in %s are being worked on by other contributors. Work on <a href='/translate/%s'>another text</a> for now." % (next, ref) return render_to_response('static/generic.html', generic_response, RequestContext(request)) else: # choose the next text in order skip = 0 assigned_ref = {"error": "haven't chosen yet"} # TODO -- need an escape valve here while "error" in assigned_ref: text = next_untranslated_text_in_category(cat, skip=skip) assigned_ref = next_untranslated_ref_in_text(text) skip += 1 else: # we don't know what this is generic_response["content"] = "<b>%s</b> isn't a known text or category.<br>But you can still contribute in other ways.</h3> <a href='/contribute'>Learn More.</a>" % (ref) return render_to_response('static/generic.html', generic_response, RequestContext(request)) # get the assigned text assigned = get_text(assigned_ref, context=0, commentary=False) # Put a lock on this assignment user = request.user.id if request.user.is_authenticated() else 0 sefaria.locks.set_lock(assigned_ref, "en", "Sefaria Community Translation", user) # if the assigned text is actually empty, run this request again # but leave the new lock in place to skip over it if "he" not in assigned or not len(assigned["he"]): return translation_flow(request, ref) # get percentage and remaining counts # percent = get_percent_available(assigned["book"]) translated = get_translated_count_by_unit(assigned["book"], unit=assigned["sectionNames"][-1]) remaining = get_untranslated_count_by_unit(assigned["book"], unit=assigned["sectionNames"][-1]) percent = 100 * translated / float(translated + remaining) return render_to_response('translate_campaign.html', {"title": "Help Translate %s" % ref, "base_ref": ref, "assigned_ref": assigned_ref, "assigned_ref_url": url_ref(assigned_ref), "assigned_text": assigned["he"], "assigned_segment_name": assigned["sectionNames"][-1], "assigned": assigned, "translated": translated, "remaining": remaining, "percent": percent, "thanks": "thank" in request.GET, "random_param": "&skip=%d" % assigned["sections"][0] if request.GET.get("random") else "", "next_text": next_text, "next_section": next_section, }, RequestContext(request))
def reader(request, ref, lang=None, version=None): # Redirect to standard URLs # Let unknown refs pass through uref = url_ref(ref) if uref and ref != uref: url = "/" + uref if lang and version: url += "/%s/%s" % (lang, version) response = redirect(url, permanent=True) params = request.GET.urlencode() response['Location'] += "?%s" % params if params else "" return response # BANDAID - return the first section only of a spanning ref pRef = parse_ref(ref) if "error" not in pRef and is_spanning_ref(pRef): ref = split_spanning_ref(pRef)[0] url = "/" + ref if lang and version: url += "/%s/%s" % (lang, version) response = redirect(url) params = request.GET.urlencode() response['Location'] += "?%s" % params if params else "" return response version = version.replace("_", " ") if version else None text = get_text(ref, lang=lang, version=version) if not "error" in text: notes = get_notes(ref, uid=request.user.id, context=1) text["commentary"] += notes initJSON = json.dumps(text) lines = True if "error" in text or text["type"] not in ('Tanach', 'Talmud') or text["book"] == "Psalms" else False email = request.user.email if request.user.is_authenticated() else "" zippedText = map(None, text["text"], text["he"]) if not "error" in text else [] # Pull language setting from cookie or Accept-Lanugage header langMode = request.COOKIES.get('langMode') or request.LANGUAGE_CODE or 'en' langMode = 'he' if langMode == 'he-il' else langMode # URL parameter trumps cookie langMode = request.GET.get("lang", langMode) langMode = "bi" if langMode in ("he-en", "en-he") else langMode # Don't allow languages other than what we currently handle langMode = 'en' if langMode not in ('en', 'he', 'bi') else langMode # Substitue language mode if text not available in that language if not "error" in text: if is_text_empty(text["text"]) and not langMode == "he": langMode = "he" if is_text_empty(text["he"]) and not langMode == "en": langMode = "en" langClass = {"en": "english", "he": "hebrew", "bi": "bilingual heLeft"}[langMode] return render_to_response('reader.html', {'text': text, 'initJSON': initJSON, 'zippedText': zippedText, 'lines': lines, 'langClass': langClass, 'page_title': norm_ref(ref) or "Unknown Text", 'title_variants': "(%s)" % ", ".join(text.get("titleVariants", []) + [text.get("heTitle", "")]), 'email': email}, RequestContext(request))