def wrapped_f(*args, **kwargs): user = current_app.get_current_user() if user.is_anonymous or not user.is_admin: msg = "This resource is only available to teachers and admins" print(msg) return make_403(details=msg) return view_function(*args, **kwargs)
def clone_commentary(doc_id, user_id, type_id): com_to_be_cloned = Commentary.query.filter( Commentary.user_id == user_id, Commentary.doc_id == doc_id, Commentary.type_id == type_id).first() if not com_to_be_cloned: return make_404() is_not_allowed = forbid_if_not_in_whitelist( current_app, Document.query.filter(Document.id == doc_id).first()) if is_not_allowed: return is_not_allowed teacher = current_app.get_current_user() teacher_com = Commentary.query.filter(Commentary.user_id == teacher.id, Commentary.type_id == type_id, Commentary.doc_id == doc_id).first() if teacher_com is None: teacher_com = Commentary(doc_id=doc_id, user_id=teacher.id, content=com_to_be_cloned.content) else: # replace the teacher's com content teacher_com.content = com_to_be_cloned.content # remove the old teacher's notes # for note in teacher_com.notes: # # MUST delete commentary_has_note and not the note itself # # (since the latest might be used somewhere else)! for chn in CommentaryHasNote.query.filter( CommentaryHasNote.commentary_id == teacher_com.id).all(): db.session.delete(chn) # clone notes for chn_to_be_cloned in com_to_be_cloned.commentary_has_note: note = Note(type_id=chn_to_be_cloned.note.type_id, user_id=teacher.id, content=chn_to_be_cloned.note.content) db.session.add(note) db.session.flush() teacher_com.transcription_has_note.append( CommentaryHasNote(ptr_start=chn_to_be_cloned.ptr_start, ptr_end=chn_to_be_cloned.ptr_end, note_id=note.id, commentary_id=teacher_com.id), ) db.session.add(teacher_com) try: db.session.commit() except Exception as e: db.session.rollback() print(str(e)) return make_400(str(e)) return make_200()
def clone_transcription(doc_id, user_id): print("cloning transcription (doc %s) from user %s" % (doc_id, user_id)) tr_to_be_cloned = Transcription.query.filter(Transcription.user_id == user_id, Transcription.doc_id == doc_id).first() if not tr_to_be_cloned: return make_404() is_not_allowed = forbid_if_not_in_whitelist(current_app, Document.query.filter(Document.id == doc_id).first()) if is_not_allowed: return is_not_allowed teacher = current_app.get_current_user() teacher_tr = Transcription.query.filter(Transcription.user_id == teacher.id, Transcription.doc_id == doc_id).first() if teacher_tr is None: teacher_tr = Transcription(doc_id=doc_id, user_id=teacher.id, content=tr_to_be_cloned.content) else: # replace the teacher's tr content teacher_tr.content = tr_to_be_cloned.content # remove the old teacher's notes for note in teacher_tr.notes: db.session.delete(note) # teacher_tr.notes = [] # clone notes for thn_to_be_cloned in tr_to_be_cloned.transcription_has_note: note = Note(type_id=thn_to_be_cloned.note.type_id, user_id=teacher.id, content=thn_to_be_cloned.note.content) db.session.add(note) db.session.flush() teacher_tr.transcription_has_note.append( TranscriptionHasNote(ptr_start=thn_to_be_cloned.ptr_start, ptr_end=thn_to_be_cloned.ptr_end, note_id=note.id, transcription_id=teacher_tr.id), ) db.session.add(teacher_tr) try: db.session.commit() except Exception as e: db.session.rollback() print(str(e)) return make_400(str(e)) return make_200()
def api_get_dashboard_manage_documents(api_version): page_number = request.args.get('num-page', 1) page_size = request.args.get('page-size', 50) query = Document.query user = current_app.get_current_user() if user.is_student and not (user.is_admin or user.is_teacher): print("filter docs by wl", [w.id for w in user.whitelists]) query = query.filter( Document.whitelist_id.in_([w.id for w in user.whitelists])) total = query.count() sort = request.args.get('sort-by', None) if sort: field, order = sort.split('.') query = query.order_by( text("%s %s" % (field, "desc" if order == "asc" else "asc"))) docs = query.paginate(int(page_number), int(page_size), max_per_page=100, error_out=False).items return make_200( data={ "total": total, "documents": [{ "whitelist": { "id": d.whitelist.id, "label": d.whitelist.label }, "id": d.id, "title": d.title, "pressmark": d.pressmark, "bookmark_order": d.bookmark_order, "owner": d.user.serialize(), "is-published": d.is_published, "is-closed": d.is_closed, "validation": d.validation_flags, "exist": d.exist_flags, "thumbnail-url": d.images[0].url if len(d.images) > 0 else None } for d in docs] })
def api_delete_user(api_version, user_id): try: target_user = User.query.filter(User.id == user_id).one() user = current_app.get_current_user() if target_user.is_admin and not user.is_admin: return make_403() try: db.session.delete(target_user) db.session.commit() return make_200(data=[]) except Exception as e: db.session.rollback() print(str(e)) return make_400(str(e)) except NoResultFound: return make_404()
def api_post_users_roles(api_version, user_id): """ { "data": [ { "name": "admin" }, { "name": "teacher" } ] } :param api_version: :param user_id: :return: """ data = request.get_json() if "data" in data: data = data["data"] user = current_app.get_current_user() target_user = User.query.filter(User.id == user_id).first() for role_name in [r.get("name", None) for r in data]: if not role_name in target_user.roles: if role_name == "admin" and not user.is_admin: return make_403() else: role = Role.query.filter(Role.name == role_name).first() if role: target_user.roles.append(role) db.session.add(target_user) try: db.session.commit() return make_200(data=[r.serialize() for r in target_user.roles]) except Exception as e: db.session.rollback() return make_400(str(e)) else: return make_409()
def api_delete_users_roles(api_version, user_id): try: target_user = User.query.filter(User.id == user_id).one() except NoResultFound: return make_404() user = current_app.get_current_user() if target_user.is_admin and not user.is_admin: return make_403() target_user.roles = [Role.query.filter(Role.name == "student").first()] db.session.add(target_user) try: db.session.commit() return make_200() except Exception as e: db.session.rollback() print(str(e)) return make_400(str(e))
def delete_commentary(doc_id, user_id, type_id): user = current_app.get_current_user() # only teacher and admin can see everything access_is_forbidden = forbid_if_nor_teacher_nor_admin_and_wants_user_data( current_app, user_id) if access_is_forbidden: return access_is_forbidden if not (user.is_teacher or user.is_admin) and type_id is not None and user_id is None: user_id = user.id if type_id is None: type_id = Commentary.type_id if user_id is None: if user.is_teacher: user_id = Commentary.user_id else: user_id = user.id commentaries = Commentary.query.filter( Commentary.doc_id == doc_id, Commentary.user_id == user_id, Commentary.type_id == type_id).all() for c in commentaries: db.session.delete(c) try: db.session.commit() # change validation step return make_200( data={ # "validation_step": doc.validation_step, # "validation_step_label": get_validation_step_label(doc.validation_step) }) except (Exception, KeyError) as e: db.session.rollback() return make_400(str(e))
def api_commentary_from_user(api_version, doc_id, user_id): print(current_app.get_current_user()) forbid = forbid_if_nor_teacher_nor_admin_and_wants_user_data( current_app, user_id) if forbid: return forbid # teachers can still post notes in validated transcription #current_user = current_app.get_current_user() #if not current_user.is_teacher and get_doc(doc_id).is_transcription_validated: # return make_403() commentaries = Commentary.query.filter( Commentary.doc_id == doc_id, Commentary.user_id == user_id, ).all() if len(commentaries) == 0: return make_404() return make_200(data=[c.serialize() for c in commentaries])
def api_post_whitelist(api_version): """ { data: { label : 'My new whitelist' } } :return: """ data = request.get_json() try: new_whitelist_name = data.get("label") whitelist = Whitelist(label=new_whitelist_name) whitelist.users = [current_app.get_current_user()] db.session.add(whitelist) db.session.commit() return make_200(data=whitelist.serialize()) except Exception as e: db.session.rollback() print(str(e)) return make_400(str(e))
def api_put_commentary(api_version, doc_id, user_id): """ { "data": { "content" : "My first transcription" "notes": [{ "id": 1, "type_id": 0 (by default), "content": "aaa", "ptr_start": 3, "ptr_end": 12 }] } } :param api_version: :param doc_id: :return: """ doc = get_doc(doc_id) # teachers can still post notes in if the commentaries are validated current_user = current_app.get_current_user() if not current_user.is_teacher and doc.is_commentaries_validated: return make_403() forbid = is_closed(doc_id) if forbid: return forbid data = request.get_json() if "data" in data: data = data["data"] c = Commentary.query.filter( data["type_id"] == Commentary.type_id, doc_id == Commentary.doc_id, user_id == Commentary.user_id, ).first() if c is None: return make_404() try: if "content" in data: error = check_no_XMLParserError(data["content"]) if error: raise Exception('Commentary content is malformed: %s', str(error)) c.content = data["content"] db.session.add(c) db.session.commit() if "notes" in data: current_commentary_notes = CommentaryHasNote.query.filter( CommentaryHasNote.commentary_id == c.id).all() # remove all notes not present in the transcription anymore for current_chn in current_commentary_notes: if (current_chn.note.id, current_chn.ptr_start, current_chn.ptr_end) not in \ [(note.get('id', None), note["ptr_start"], note["ptr_end"]) for note in data["notes"]]: note = current_chn.note db.session.delete(current_chn) print("delete chn", note) db.session.flush() note.delete_if_unused() for note in data["notes"]: note_id = note.get('id', None) chn = CommentaryHasNote.query.filter( CommentaryHasNote.note_id == note_id, CommentaryHasNote.commentary_id == c.id, CommentaryHasNote.ptr_start == note["ptr_start"], CommentaryHasNote.ptr_end == note["ptr_end"]).first() if chn is None: # try to find a note in other contents reused_note = findNoteInDoc(doc_id, user_id, note_id) if reused_note is None: raise Exception( 'Cannot reuse note: note %s unknown' % note_id) chn = CommentaryHasNote(commentary_id=c.id, note_id=reused_note.id, ptr_start=note["ptr_start"], ptr_end=note["ptr_end"]) db.session.add(chn) db.session.flush() error = check_no_XMLParserError(note["content"]) if error: raise Exception('Note content is malformed: %s', str(error)) chn.ptr_start = note['ptr_start'] chn.ptr_end = note['ptr_end'] chn.note.content = note['content'] chn.note.type_id = note['type_id'] db.session.add(chn) db.session.add(chn.note) db.session.commit() except Exception as e: db.session.rollback() print('Error', str(e)) return make_400(str(e)) return make_200(data=c.serialize()) else: return make_400("no data")
def api_post_commentary(api_version, doc_id, user_id): """ { "data": { "type_id": 2, "content" : "This is a commentary", "notes": [ { "content": "note1 content", "ptr_start": 5, "ptr_end": 7 } ] } } :param api_version: :param doc_id: :return: """ doc = get_doc(doc_id) if doc is None: return make_404() # teachers can still post notes in if the commentaries are validated user = current_app.get_current_user() if not user.is_teacher and doc.is_commentaries_validated: return make_403() data = request.get_json() if "data" in data: data = data["data"] data["user_id"] = user_id # user.id try: c = None # case 1) "content" in data if "content" in data: error = check_no_XMLParserError(data["content"]) if error: raise Exception('Commentary content is malformed: %s', str(error)) c = Commentary(doc_id=doc_id, user_id=user_id, type_id=data["type_id"], content=data["content"]) db.session.add(c) db.session.flush() # case 2) there's "notes" in data elif "notes" in data: c = Commentary.query.filter( Commentary.doc_id == doc_id, Commentary.user_id == user_id, Commentary.type_id == data["type_id"]).first() print(doc, c, user, data) if c is None: return make_404() if "notes" in data: print("======= notes =======") for note in data["notes"]: # 1) simply reuse notes which come with an id note_id = note.get('id', None) if note_id is not None: reused_note = Note.query.filter( Note.id == note_id, Note.user_id == user_id).first() if reused_note is None: return make_400(details="Wrong note id %s" % note_id) db.session.add(reused_note) db.session.flush() chn = CommentaryHasNote.query.filter( CommentaryHasNote.note_id == reused_note.id, CommentaryHasNote.commentary_id == c.id).first() # 1.a) the note is already present in the commentary, so update its ptrs if chn is not None: raise Exception( "Commentary note already exists. Consider using PUT method" ) else: # 1.b) the note is not present on the transcription side, so create it chn = CommentaryHasNote( commentary_id=c.id, note_id=reused_note.id, ptr_start=note["ptr_start"], ptr_end=note["ptr_end"]) db.session.add(chn) print("reuse:", chn.transcription_id, chn.note_id) else: # 2) make new note error = check_no_XMLParserError(note["content"]) if error: raise Exception('Note content is malformed: %s', str(error)) new_note = Note(type_id=note.get("type_id", 0), user_id=user_id, content=note["content"]) db.session.add(new_note) db.session.flush() chn = CommentaryHasNote(commentary_id=c.id, note_id=new_note.id, ptr_start=note["ptr_start"], ptr_end=note["ptr_end"]) db.session.add(chn) print("make:", chn.commentary_id, chn.note_id) db.session.flush() print("====================") db.session.add(chn) db.session.commit() except (Exception, KeyError) as e: db.session.rollback() print(str(e)) return make_400(str(e)) return make_200(c.serialize()) else: return make_400("no data")
def get_auth_token(api_version): user = current_app.get_current_user() token = user.generate_auth_token() return make_200(data=[{'token': token.decode('ascii')}])
def api_documents_annotations_list_by_canvas(api_version, doc_id, motivation, canvas_idx): """ """ user = current_app.get_current_user() doc = Document.query.filter(Document.id == doc_id).first() if user.is_anonymous and doc.is_published is False: annotation_list = make_annotation_list(request.base_url, []) return annotation_list try: manifest = make_manifest(api_version, doc_id) sequence = manifest["sequences"][0] canvas = sequence["canvases"][int(canvas_idx)] annotations = [] img = Image.query.filter(Image.doc_id == doc_id).first() img = Image.query.filter(Image.manifest_url == img.manifest_url, Image.doc_id == doc_id, Image.canvas_idx == canvas_idx).first() # TODO s'il y a plusieurs images dans un seul et même canvas ? #img_json = canvas["images"][0] kwargs = { "doc_id": doc_id, "api_version": api_version } manifest_url = current_app.with_url_prefix( url_for("api_bp.api_documents_manifest", api_version=1.0, doc_id=doc_id)) for img_zone in [zone for zone in img.zones]: kwargs["zone_id"] = img_zone.zone_id res_uri = current_app.with_url_prefix(url_for("api_bp.api_documents_annotations", **kwargs)) print(img_zone, img_zone.zone_type.label) if img_zone.zone_type.label == "describing": from app.api.transcriptions.routes import get_reference_transcription tr = get_reference_transcription(doc_id) if tr is None: annotation_list = make_annotation_list(request.base_url, []) return annotation_list img_al = AlignmentImage.query.filter( AlignmentImage.transcription_id == tr.id, AlignmentImage.manifest_url == img_zone.manifest_url, AlignmentImage.canvas_idx == img_zone.canvas_idx, AlignmentImage.img_idx == img_zone.img_idx, AlignmentImage.zone_id == img_zone.zone_id ).first() # is there a text segment bound to this image zone? if img_al is not None: text_content = tr.content[img_al.ptr_transcription_start:img_al.ptr_transcription_end] else: text_content = "" else: text_content = img_zone.note new_annotation = make_annotation( manifest_url, canvas["@id"], img_zone.fragment, img_zone.svg, res_uri, content=text_content, format="text/html" ) annotations.append(new_annotation) annotation_list = make_annotation_list(request.base_url, annotations) return annotation_list except Exception as e: print(str(e)) return make_400(str(e))
def api_post_documents_translations(api_version, doc_id, user_id): """ at least one of "content" or "notes" is required { "data": { "content" : "My first translation" "notes": [ { "content": "note1 content", "ptr_start": 5, "ptr_end": 7 } ] } } :param user_id: :param api_version: :param doc_id: :return: """ #forbid = forbid_if_other_user(current_app, user_id) #if forbid: # return forbid # teachers can still post notes in validated translation current_user = current_app.get_current_user() if not current_user.is_teacher and get_doc(doc_id).is_translation_validated: return make_403() forbid = is_closed(doc_id) if forbid: return forbid data = request.get_json() if "data" in data: data = data["data"] try: tr = None # case 1) "content" in data if "content" in data: error = check_no_XMLParserError(data["content"]) if error: raise Exception('Translation content is malformed: %s', str(error)) tr = Translation(doc_id=doc_id, content=data["content"], user_id=user_id) db.session.add(tr) db.session.flush() # case 2) there's only "notes" in data if "notes" in data: tr = Translation.query.filter(Translation.doc_id == doc_id, Translation.user_id == user_id).first() if tr is None: return make_404() if "notes" in data: print("======= notes =======") for note in data["notes"]: # 1) simply reuse notes which come with an id note_id = note.get('id', None) if note_id is not None: reused_note = Note.query.filter(Note.id == note_id).first() if reused_note is None: return make_400(details="Wrong note id %s" % note_id) db.session.add(reused_note) db.session.flush() thn = TranslationHasNote.query.filter(TranslationHasNote.note_id == reused_note.id, TranslationHasNote.translation_id == tr.id).first() # 1.a) the note is already present in the translation, so update its ptrs if thn is not None: raise Exception("Translation note already exists. Consider using PUT method") else: # 1.b) the note is not present on the translation side, so create it thn = TranslationHasNote(translation_id=tr.id, note_id=reused_note.id, ptr_start=note["ptr_start"], ptr_end=note["ptr_end"]) db.session.add(thn) print("reuse:", thn.translation_id, thn.note_id) else: # 2) make new note error = check_no_XMLParserError(note["content"]) if error: raise Exception('Note content is malformed: %s', str(error)) new_note = Note(type_id=note.get("type_id", 0), user_id=user_id, content=note["content"]) db.session.add(new_note) db.session.flush() thn = TranslationHasNote(translation_id=tr.id, note_id=new_note.id, ptr_start=note["ptr_start"], ptr_end=note["ptr_end"]) db.session.add(thn) print("make:", thn.translation_id, thn.note_id) db.session.flush() print("thn:", [thn.note.id for thn in tr.translation_has_note]) print("====================") db.session.add(tr) db.session.commit() except Exception as e: db.session.rollback() print("Error", str(e)) return make_400(str(e)) return make_200(data=tr.serialize_for_user(user_id)) else: return make_400("no data")
def api_put_documents_translations(api_version, doc_id, user_id): """ { "data": { "content" : "My first translation" "notes": [{ "id": 1, "type_id": 0 (by default), "content": "aaa", "ptr_start": 3, "ptr_end": 12 }] } } :param user_id: :param api_version: :param doc_id: :return: """ #forbid = forbid_if_other_user(current_app, user_id) #if forbid: # return forbid # teachers can still update validated translation current_user = current_app.get_current_user() if not current_user.is_teacher and get_doc(doc_id).is_translation_validated: return make_403() forbid = is_closed(doc_id) if forbid: return forbid data = request.get_json() if "data" in data: data = data["data"] tr = get_translation(doc_id=doc_id, user_id=user_id) if tr is None: return make_404() try: if "content" in data: error = check_no_XMLParserError(data["content"]) if error: raise Exception('Translation content is malformed: %s', str(error)) tr.content = data["content"] db.session.add(tr) db.session.commit() if "notes" in data: current_translation_notes = TranslationHasNote.query.filter( TranslationHasNote.translation_id == tr.id).all() # remove all notes not present anymore in the translation print("current thn", current_translation_notes) for current_thn in current_translation_notes: if (current_thn.note.id, current_thn.ptr_start, current_thn.ptr_end) not in \ [(note.get('id', None), note["ptr_start"], note["ptr_end"]) for note in data["notes"]]: note = current_thn.note db.session.delete(current_thn) print("delete thn", note) db.session.flush() note.delete_if_unused() for note in data["notes"]: note_id = note.get('id', None) thn = TranslationHasNote.query.filter(TranslationHasNote.note_id == note_id, TranslationHasNote.translation_id == tr.id, TranslationHasNote.ptr_start == note["ptr_start"], TranslationHasNote.ptr_end == note["ptr_end"] ).first() if thn is None or note_id is None: # try to find the note in other contents reused_note = findNoteInDoc(doc_id, user_id, note_id) if reused_note is None: raise Exception('Cannot reuse note: note %s unknown' % note_id) # bind the note on the translation side thn = TranslationHasNote(translation_id=tr.id, note_id=reused_note.id, ptr_start=note["ptr_start"], ptr_end=note["ptr_end"]) db.session.add(thn) db.session.flush() error = check_no_XMLParserError(note["content"]) if error: raise Exception('Note content is malformed: %s', str(error)) thn.ptr_start = note['ptr_start'] thn.ptr_end = note['ptr_end'] thn.note.content = note['content'] thn.note.type_id = note['type_id'] db.session.add(thn) db.session.add(thn.note) db.session.commit() except Exception as e: db.session.rollback() print('Error', str(e)) return make_400(str(e)) return make_200(data=tr.serialize_for_user(user_id)) else: return make_400("no data")