Example #1
0
 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)
Example #2
0
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()
Example #3
0
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()
Example #4
0
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]
        })
Example #5
0
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()
Example #6
0
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()
Example #7
0
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))
Example #8
0
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))
Example #9
0
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])
Example #10
0
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))
Example #11
0
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")
Example #12
0
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")
Example #13
0
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')}])
Example #14
0
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))
Example #15
0
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")
Example #16
0
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")