def PATCH(self): req = cherrypy.request versions = library.versions for url, attrs in libraries.normalize(req.json["index"]).iteritems(): name = url.split("/")[-2] if ( # Anyone can add a version (with themselves as admin)... (name not in versions.auth) or # but only admins can change permissions... versions.permit(name, req.login, 'admin')): auth = attrs.get("auth", {}) to_version = versions[name] if name not in versions.version_names: versions.version_names.append(name) to_version.save_works() # ...but they cannot remove themself as an admin. auth.setdefault("permissions", {})[req.login] = "admin" auth.setdefault("locked", False) versions.auth[name] = auth if "notes" in attrs: v = versions[name] if v.version_notes != attrs["notes"]: v.version_notes = attrs["notes"] v.save_version_notes() library.save_auth() cherrypy.response.status = 204
def PATCH(self): version = auth_version('writer') req = cherrypy.request new_works = libraries.normalize(req.json["index"]) # TODO: validation for workurl, work in new_works.iteritems(): workid = workurl.split("/")[-2] if work is None: version.destroy_work(workid) else: if workid in version.works: if "name" in work: version.works[workid]["name"] = work["name"] else: version.works[workid] = { "name": work.get("name", workid), "sections": [] } version.worksorder.append(workid) existing_sections = version.works[workid]["sections"] for sectionid in set( work["sections"]) - set(existing_sections): # Make new (empty) sections s = libraries.Section(version.name, workid, sectionid) version.save_section(s) version.save_works() cherrypy.response.status = 204
def GET(self, strict=False, versions=None, context=6): req = cherrypy.serving.request versions = [v for v in (versions or "").split(",") if v] try: context = int(context) except (TypeError, ValueError): context = 6 try: criteria = libraries.normalize( libraries.simplejson.loads(req.criteria)) except libraries.simplejson.JSONDecodeError: raise cherrypy.HTTPError( 404, "Concordance criteria MUST be valid JSON") entries, passages, complete = library.concordance( versions, criteria, req.version, bool(strict), context) lexical_notes = {} for v in versions: version = auth_version(version=v) lemmas = set(entry['lemma'] for entry in entries[v].itervalues()) lexical_notes[v] = dict( (lemma, version.lexical_notes[lemma]) for lemma in lemmas.intersection(version.lexical_notes)) response = { "self": url(qs=cherrypy.request.query_string), "element": "shoji:entity", "body": { "passages": passages, # These are both {version: {lemma: ...}} trees. "entries": entries, "lexical_notes": lexical_notes, "complete": complete } } # Collect parents and children. # This was migrated from the old lexicon view, which only ever # showed one lemma or wordform. # TODO: expand to show multiple trees, or move it to somewhere # in the API/UI that is more appropriate. # Eventually, the concept of "family" should be in the lexicon instead. if not isinstance(criteria, list): criteria = [criteria] lemmas = set([ wf["lemma"] for wf in criteria if isinstance(wf, dict) and "lemma" in wf and isinstance(wf["lemma"], basestring) ]) if len(lemmas) == 1: response["body"]["family"] = library.family(lemmas.pop()) return response
def PATCH(self): """Apply the given catalog patch document.""" req = cherrypy.serving.request version = auth_version('writer') try: section = version.load_section(req.workid, req.sectionid) except FileNotFound: raise cherrypy.NotFound() section.update(libraries.normalize(req.json["index"])) version.save_section(section) cherrypy.response.status = 204
def PATCH(self): """Apply the given {ids, text, lexicon} patch document.""" req = cherrypy.serving.request version = auth_version('writer') # Bucket the words so we can load each affected section in order. # and also to fail the whole operation if any section cannot be found. index = libraries.normalize(req.json['index']) by_section = {} for id, entry in index.iteritems(): workid, sectionid, phraseid = id.split("/", 3) by_section.setdefault((workid, sectionid), {})[phraseid] = entry for (workid, sectionid), patches in by_section.iteritems(): section = version.load_section(workid, sectionid) section.update(patches) version.save_section(section) cherrypy.response.status = 204
def POST(self): """Accept the given text, tokenize it, and append as new words.""" req = cherrypy.serving.request version = auth_version('writer') proposed_text = libraries.normalize(req.json["body"]["text"]) proposed_text = [ t.strip() for t in re.split(r'(\W+)', proposed_text, flags=re.UNICODE) if t.strip() ] # Look up any existing section. try: section = version.load_section(req.workid, req.sectionid) except FileNotFound: section = libraries.Section(req.version, req.workid, req.sectionid) # Apply morphology as best we can. # Grab a map from original -> most common morphology # from the given other work lexv = req.json["body"].get("lexicon", None) if lexv is None: lexv = version else: lexv = library.versions[lexv] morph_map = lexv.morphology_map() # Assume an id scheme of work/section/x.y, where we increment 'x' by 1 # for this set of text as a whole, and assign each word a 'y' # from 1 to len(text). verses = [id.split(".", 1)[0] for id in section.order] x = max([int(v) for v in verses if v.isdigit()] or [0]) + 1 y = 0 with section.lock: for word in proposed_text: y += 1 verse_number = word.strip(string.punctuation) if verse_number.isdigit(): # Interpret this as a verse marker. Unfortunately, this means # that text like "he bought 5 jars" may be marked incorrectly. # To combat that, we only change x if it is incrementing. verse_number = int(verse_number) if verse_number == x + 1 or (x == verse_number == 1 and not section.order): x = verse_number y = 0 continue phraseid = "%s.%s" % (x, y) section.order.append("%s:0" % phraseid) section.entries[phraseid] = { "parsing": "---------", "lemma": "", "original": word, "text": [word] } match = morph_map.get(word, None) if match: section.entries[phraseid].update(match) section.indices = section.calc_indices() version.save_section(section) cherrypy.response.status = 204