diff --git a/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/borrow_checkout.html b/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/borrow_checkout.html index bdfa05c11b4cb2e7379167eb7c30173537be3f2a..f183fb0ca89dffddc57bab612d52465eac2896b8 100644 --- a/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/borrow_checkout.html +++ b/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/borrow_checkout.html @@ -2,10 +2,10 @@ <html> <body> <div id="caosdb-form"> - <div class="row" id="checkout-form-{containerid}" style="margin:0px"> + <div class="row" id="checkout-form-{boxid}" style="margin:0px"> <div class="col-lg-12"> - <h2>Borrow Container</h2> - <form class="caosdb-container-checkout-form" autocomplete="on"> + <h2>Borrow Box</h2> + <form class="caosdb-awi-box-checkout-form" autocomplete="on"> <div> <h5>Please enter your full name and your email address. If you are doing this for the first time, a new user record will be created.</h5> </div> @@ -40,24 +40,28 @@ </label> </div> </div> + <div class="col-sm-4"> + <div class="form-group"> + <label>Destination: + <input id="loan-destination" required="required"/> + </label> + </div> + </div> </div> <div class="row caosdb-form-row"> <div class="col-sm-12"> <div class="form-group"> - <div class="row" style="margin:0px"> - <label for="loan-comment" style="margin-bottom:0px">Purpose:</label> - </div> - <div class="row" style="margin:0px"> + <label for="loan-comment" style="margin-bottom:0px">Purpose: <textarea id="loan-comment" class="form-control" rows="4" style="width:100%" placeholder="Please indicate the purpose of the loan.">{purpose}</textarea> - </div> + </label> </div> </div> </div> <div class="row caosdb-form-row"> <div class="col-sm-12"> <div class="form-check"> - <label class="form-check-label">I am going to completely exhaust the contents of this container. + <label class="form-check-label">Are you going to completely exhaust the contents of this box? <input type="checkbox" class="form-check-input" id="exhaust-contents" value="no" /> </label> </div> diff --git a/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/return_box.html b/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/return_box.html index c14a2984c0b4d106b1d804068edc74c92f26cd77..df49499263824210cc027e3b1980b84c30d00dc1 100644 --- a/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/return_box.html +++ b/caosdb-server/caosdb-webui/src/ext/html/forms/loan-forms/return_box.html @@ -2,10 +2,10 @@ <html> <body> <div id="caosdb-form"> - <div class="row" id="caosdb-return-form-{containerid}" style="margin:0px"> + <div class="row" id="caosdb-return-form-{boxid}" style="margin:0px"> <div class="col-lg-12"> - <h2>Return Container</h2> - <form class="caosdb-container-return-form" autocomplete="on"> + <h2>Return Box</h2> + <form class="caosdb-awi-box-return-form" autocomplete="on"> <div> <h5>Please enter your full name and your email address. If you are doing this for the first time, a new user record will be created.</h5> </div> @@ -40,6 +40,13 @@ </label> </div> </div> + <div class="col-sm-4"> + <div class="form-group"> + <label>Destination: + <input name="location" type="text" class="form-control" id="current-location" required="required" value="{currentlocation}" /> + </label> + </div> + </div> </div> <div class="row caosdb-form-row"> <div class="col-sm-12"> @@ -49,7 +56,7 @@ </div> <div class="row" style="margin:0px"> <textarea id="loan-comment" class="form-control" rows="4" style="width:100%" - placeholder="Please insert what the container currently contains.">{new_content}</textarea> + placeholder="Please insert what the box currently contains.">{new_content}</textarea> </div> </div> </div> diff --git a/caosdb-server/caosdb-webui/src/ext/js/box_loan.js b/caosdb-server/caosdb-webui/src/ext/js/box_loan.js index 73494a0d06cf48a61c08e3829d51b7b6319e06ab..4f567a92a993e7343aabcdf98a157417b2c780b8 100644 --- a/caosdb-server/caosdb-webui/src/ext/js/box_loan.js +++ b/caosdb-server/caosdb-webui/src/ext/js/box_loan.js @@ -8,49 +8,38 @@ function get_formatted_date(split) { return ((new Date(Date.now() - tzoffset)).toISOString()).split(split)[0]; } -/** - * This function retrieves an html form from the html subdirectory. - * - */ -var getHTMLForm = async function (pageName, variables) { - var site = await connection.get("webinterface/html/forms/" + pageName + ".html", "html"); - var htmltext = site.getElementById("caosdb-form").innerHTML; - var req = /\{([0-9a-z-A-Z_]+)\}/g; - return htmltext.replace(req, (match, p1) => { - return variables[p1] || ""; - }); -} + /** - * GEOMAR loan management, copied and adapted from AWI box-management code. + * AWI Box Loan, refactored code. */ -var loan_management = function () { +var awi_box_loan = function () { - var logger = log.getLogger("loan_management"); + var logger = log.getLogger("awi_box_loan"); var datamodel = { loan: "Loan", - container: "Container", + box: "Box", returned: "returned", } - const _dismiss_button = '<button class="btn btn-secondary container-loan-btn">OK</button>' + const _dismiss_button = '<button class="btn btn-secondary box-loan-btn">OK</button>' const _server_did_not_respond = "The server did not respond. Please reload the page."; - const _return_container_button = '<a title="Return Container." class="btn btn-link container-loan-btn">Return Container</a>'; - const _borrow_container_button = '<a title="Borrow Container." class="btn btn-link container-loan-btn">Borrow Container</a>'; - const _confirm_loan_button = '<a title="Confirm Loan." class="btn btn-link container-loan-btn">Confirm Loan</a>'; - const _manual_return_button = '<a title="Manual Return." class="btn btn-link container-loan-btn">Manual Return</a>'; - const _accept_return_button = '<a title="Accept Return" class="btn btn-link container-loan-btn">Accept Return</a>'; - const _reject_return_button = '<a title="Reject Return." class="btn btn-link container-loan-btn">Reject Return</a>'; - const _accept_loan_button = '<a title="Accept Borrow Request." class="btn btn-link container-loan-btn">Accept Loan Request</a>'; - const _container_update_alert = ` + const _return_box_button = '<a title="Return Box." class="btn btn-link box-loan-btn">Return Box</a>'; + const _borrow_box_button = '<a title="Borrow Box." class="btn btn-link box-loan-btn">Borrow Box</a>'; + const _confirm_loan_button = '<a title="Confirm Loan." class="btn btn-link box-loan-btn">Confirm Loan</a>'; + const _manual_return_button = '<a title="Manual Return." class="btn btn-link box-loan-btn">Manual Return</a>'; + const _accept_return_button = '<a title="Accept Return" class="btn btn-link box-loan-btn">Accept Return</a>'; + const _reject_return_button = '<a title="Reject Return." class="btn btn-link box-loan-btn">Reject Return</a>'; + const _accept_loan_button = '<a title="Accept Borrow Request." class="btn btn-link box-loan-btn">Accept Loan Request</a>'; + const _box_update_alert = ` <div class="alert alert-danger alert-dismissible" role="alert"> - <h4>Update the Container</h4> + <h4>Update the Box</h4> <p> - Are you sure that the container record is up-to-date (references etc.)?<br>Possibly have a look at the notes in the loan by clicking on the "References" button.<br> Do you you want to apply further changes? + Are you sure that the box record is up-to-date (references etc.)?<br>Possibly have a look at the notes in the loan by clicking on the "References" button.<br> Do you you want to apply further changes? </p><p> - <button type="button" class="btn btn-danger caosdb-f-manual-return-cancel">Cancel and change the container manually</button> - <button type="button" class="btn btn-secondary caosdb-f-manual-return-proceed">The container is up-to-date.</button> + <button type="button" class="btn btn-danger caosdb-f-manual-return-cancel">Cancel and change the box manually</button> + <button type="button" class="btn btn-secondary caosdb-f-manual-return-proceed">The box is up-to-date.</button> </p> </div> `; @@ -68,10 +57,10 @@ var loan_management = function () { } /** - * Return the actions panel of a container entity. + * Return the actions panel of a box entity. */ - var get_actions_panel = function (container) { - return $(container).find('.caosdb-entity-actions-panel'); + var get_actions_panel = function (box) { + return $(box).find('.caosdb-entity-actions-panel'); } /** @@ -125,22 +114,22 @@ var loan_management = function () { /** - * Query for a Loan entity which references the container and which has no + * Query for a Loan entity which references the box and which has no * `returned` property. */ - var get_active_loans = async function (container) { - return await query(`FIND ${datamodel.loan} WITH ${datamodel.container} -> ${container.id} AND WHICH DOES NOT HAVE A ${datamodel.returned}`); + var get_active_loans = async function (box) { + return await query(`FIND ${datamodel.loan} WITH ${datamodel.box} -> ${box.id} AND WHICH DOES NOT HAVE A ${datamodel.returned}`); } /** - * Returns detailed information about the loan state of this container: + * Returns detailed information about the loan state of this box: */ - var get_loan_state = async function (container) { + var get_loan_state = async function (box) { const loan_state = { loan: undefined, state: undefined, }; - const loan = (await get_active_loans(container))[0]; + const loan = (await get_active_loans(box))[0]; if (typeof loan === "undefined") { // no loan found return loan_state; @@ -170,7 +159,7 @@ var loan_management = function () { } } - var show_result = function (container, result, container) { + var show_result = function (container, result, box) { const dismissable = $('<div/>'); const restore = $(container).children(); restore.hide(); @@ -196,7 +185,7 @@ var loan_management = function () { dismiss_btn.click(function () { dismissable.remove(); restore.show(); - init(container); + init(box); }); dismissable.append(dismiss_btn); } @@ -208,12 +197,17 @@ var loan_management = function () { const last_name = $(form).find("#last-name").val(); const comment = $(form).find("#loan-comment").val(); const expected_return_date = $(form).find("#expected-return").val(); + const current_location = $(form).find("[name='location']").val(); + const destination = $(form).find("[name='destination']").val(); const exhaust_contents = $(form).find("#exhaust-contents").prop("checked"); window.localStorage["borrower_email"] = email; window.localStorage["borrower_first_name"] = first_name; window.localStorage["borrower_last_name"] = last_name; window.localStorage["borrower_return_date"] = expected_return_date + if (destination) { + window.localStorage["borrower_destination"] = destination; + } return { email: email, @@ -221,18 +215,20 @@ var loan_management = function () { last_name: last_name, comment: comment, expected_return_date: expected_return_date, + current_location: current_location, exhaust_contents: exhaust_contents, + destination: destination }; } /** - * Generate a borrow function for a container. - * This function can also be used to assign values to multiple containers. + * Generate a borrow function for a box. + * This function can also be used to assign values to multiple boxes. */ - var borrow_function = async function (form, container) { + var borrow_function = async function (form, box) { const wait = getPleaseWaitSpan(); - const actions_panel = get_actions_panel(container); + const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() $(form).hide() @@ -240,35 +236,72 @@ var loan_management = function () { $.extend(loan_request, { - container: getEntityID(container), + box: getEntityID(box), }); const result = await run_script("loan_management/request_loan.py", loan_request); $(wait).remove(); $(form).remove(); - show_result(actions_panel, result, container); + show_result(actions_panel, result, box); } - var _add_form = async function (container, form_generator, submit) { - const form = $(await form_generator(container)); - form.insertAfter($(container).find('.caosdb-entity-actions-panel')).hide(); + var _add_form = async function (box, form_generator, submit_callback) { + const form = $(await form_generator(box)); + form.insertAfter($(box).find('.caosdb-entity-actions-panel')).hide(); form.submit(() => { - submit(form[0], container); + submit_callback(form[0], box); return false; }); + + form.find("label").has(":required").prepend('<span style="font-size: 10px; color: red; margin-right: 4px; font-weight: 100;">*</span>'); + + // the footer adds the default styling as well as the *required-marker + const submit = form.find(":input[type='submit']")[0]; + const footer = form_elements.make_footer(); + footer.append(submit); + form.find("form").append(footer); + return form[0]; } + var _init_validator = function (form) { + // initiate validator + // Workaround for missing checkValidity function: + if (form.checkValidity == undefined) { + form.checkValidity = () => true; + } + if (form.reportValidity == undefined) { + form.reportValidity = () => logger.debug("Workaround for validity report"); + } + form_elements.init_validator(form); + const submit = $(form).find(":input[type='submit']")[0]; + form_elements.toggle_submit_button_form_valid(form, submit); + } + /** - * Add Return button to containers' actions panels. + * Add Return button to boxes' actions panels. */ - var add_return_button = async function (container) { - const but = $(_return_container_button); - - get_actions_panel(container).append(but); - const form = await _add_form(container, generate_return_form, return_function); + var add_return_button = async function (box) { + const but = $(_return_box_button); + + get_actions_panel(box).append(but); + + const config = { + type: "reference_drop_down", + name: "location", + label: "", + required: true, + query: "FIND Record Location", + make_desc: getEntityName, + } + const dd = form_elements.make_form_field(config); + const form = await _add_form(box, generate_return_form, return_function); + $(dd).find(".col-sm-9").removeClass("col-sm-9"); + $(dd).find(".col-sm-3").removeClass("col-sm-3"); + $(form).find("#current-location").replaceWith(dd); $(form).find("label").css("display", "block"); + _init_validator(form); but.click(() => { but.remove(); @@ -280,14 +313,14 @@ var loan_management = function () { * If the user is administrator (or a data curator) a button is added * to accept the borrow request. Else a message is added "pending loan request". */ - var add_borrow_accept_button = function (container, loan) { - const actionPanel = get_actions_panel(container); + var add_borrow_accept_button = function (box, loan) { + const actionPanel = get_actions_panel(box); if (userIsAdministrator() === true || userHasRole("curator") === true) { const but = $(_accept_loan_button); but.click(() => { but.remove(); - accept_loan_function(container); + accept_loan_function(box); }); actionPanel.append(but); } else { @@ -299,21 +332,35 @@ var loan_management = function () { const loan_id = getEntityID(loan); const href = `${connection.getBasePath()}Entity/${loan_id}`; const title = "Open loan entity."; - const classes = "btn btn-link container-loan-btn"; + const classes = "btn btn-link box-loan-btn"; return `<a title="${title}" class="${classes}" href="${href}">${text}<a>`; } /** - * Add Borrow buttons to the containers' action panels + * Add Borrow buttons to the boxes' action panels */ - var add_borrow_button = async function (container) { - const but = $(_borrow_container_button); - get_actions_panel(container).append(but); - - const form = await _add_form(container, generate_form_borrow_checkout, borrow_function); + var add_borrow_button = async function (box) { + const but = $(_borrow_box_button); + get_actions_panel(box).append(but); + + const config = { + type: "reference_drop_down", + name: "destination", + label: "", + required: true, + query: "FIND Record Location", + make_desc: getEntityName, + } + const dd = form_elements.make_form_field(config); + const form = await _add_form(box, generate_form_borrow_checkout, borrow_function); + $(dd).find(".col-sm-9").removeClass("col-sm-9"); + $(dd).find(".col-sm-3").removeClass("col-sm-3"); + $(form).find("#loan-destination").replaceWith(dd); $(form).find("label").css("display", "block"); + _init_validator(form); + but.click(() => { but.remove(); $(form).toggle() @@ -321,29 +368,19 @@ var loan_management = function () { } - var _get_label_or_id = function (container) { - const bn = getProperty(container, "recommendedLabel", case_sensitive = false); - if (bn === undefined) { - // ID as fallback - return getEntityID(container); - } - return bn; - - } - - var open_accept_loan_mail = async function (loan, container) { + var open_accept_loan_mail = async function (loan, box) { try { - const borrower = (await retrieve(getProperty(loan, "borrower"), case_sensitive = false))[0]; + const borrower = (await retrieve(getProperty(loan, "Borrower"), case_sensitive = false))[0]; const email = getProperty(borrower, "email", case_sensitive = false); const firstName = getProperty(borrower, "firstName", case_sensitive = false); const lastName = getProperty(borrower, "lastName", case_sensitive = false); - const bn = _get_label_or_id(container); + const bn = getProperty(box, "Number", case_sensitive = false); const date = getProperty(loan, "expectedReturn", case_sensitive = false); - const subject = `Loan Request for Container ${bn} accepted`; + const subject = `Loan Request for Box ${bn} accepted`; const body = `Dear ${firstName} ${lastName}, %0D%0D` + - `You can pickup container ${bn} on ${date}. %0D%0D` + + `You can pickup box number ${bn} on ${date}. %0D%0D` + `Kind Regards,%0D%0D ${getUserName()}`; open_mail_generic(email, subject, body) } catch (err) { @@ -352,13 +389,12 @@ var loan_management = function () { } - var accept_loan_function = async function (container) { - + var accept_loan_function = async function (box) { const wait = getPleaseWaitSpan(); - const actions_panel = get_actions_panel(container); + const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() - const loan = (await get_active_loans(container))[0]; + const loan = (await get_active_loans(box))[0]; const accept_loan_request = { loan: getEntityID(loan), @@ -366,18 +402,17 @@ var loan_management = function () { const result = await run_script("loan_management/accept_loan_request.py", accept_loan_request); $(wait).remove(); - show_result(actions_panel, result, container); - - open_accept_loan_mail(loan, container); + show_result(actions_panel, result, box); + open_accept_loan_mail(loan, box); } - var manual_return_function = async function (container) { + var manual_return_function = async function (box) { const wait = getPleaseWaitSpan(); - const actions_panel = get_actions_panel(container); + const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() - const loan = (await get_active_loans(container))[0]; + const loan = (await get_active_loans(box))[0]; const manual_return_request = { loan: getEntityID(loan), @@ -385,36 +420,36 @@ var loan_management = function () { const result = await run_script("loan_management/manual_return.py", manual_return_request); $(wait).remove(); - show_result(actions_panel, result, container); + show_result(actions_panel, result, box); } /** - * Add Manual Return button to containers' actions panels. + * Add Manual Return button to boxes' actions panels. */ - var add_manual_return_button = function (container, loan) { - const actionPanel = get_actions_panel(container); + var add_manual_return_button = function (box, loan) { + const actionPanel = get_actions_panel(box); if (userIsAdministrator() === true || userHasRole("curator") === true) { const but = $(_manual_return_button); actionPanel.append(but); - const container_update_alert = $(_container_update_alert); - container_update_alert.find(".caosdb-f-manual-return-cancel").click(() => { + const box_update_alert = $(_box_update_alert); + box_update_alert.find(".caosdb-f-manual-return-cancel").click(() => { but.show(); - container_update_alert.hide(); + box_update_alert.hide(); }); - container_update_alert.find(".caosdb-f-manual-return-proceed").click(() => { + box_update_alert.find(".caosdb-f-manual-return-proceed").click(() => { but.remove(); - container_update_alert.remove(); - manual_return_function(container); + box_update_alert.remove(); + manual_return_function(box); }); - actionPanel.append(container_update_alert); - container_update_alert.hide(); + actionPanel.append(box_update_alert); + box_update_alert.hide(); but.click(() => { but.hide(); - container_update_alert.show(); + box_update_alert.show(); }); } else { actionPanel.append($(create_link_to_loan(loan, "Pending return."))); @@ -422,12 +457,12 @@ var loan_management = function () { } - var confirm_loan_function = async function (container) { + var confirm_loan_function = async function (box) { const wait = getPleaseWaitSpan(); - const actions_panel = get_actions_panel(container); + const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() - const loan = (await get_active_loans(container))[0]; + const loan = (await get_active_loans(box))[0]; const confirm_loan_request = { loan: getEntityID(loan), @@ -435,21 +470,22 @@ var loan_management = function () { const result = await run_script("loan_management/confirm_loan.py", confirm_loan_request); $(wait).remove(); - show_result(actions_panel, result, container); + show_result(actions_panel, result, box); } - var open_reject_return_mail = async function (loan, container) { + var open_reject_return_mail = async function (loan, box) { try { - const borrower = (await retrieve(getProperty(loan, "borrower"), case_sensitive = false))[0]; + const borrower = (await retrieve(getProperty(loan, "Borrower"), case_sensitive = false))[0]; const email = getProperty(borrower, "email", case_sensitive = false); const firstName = getProperty(borrower, "firstName", case_sensitive = false); const lastName = getProperty(borrower, "lastName", case_sensitive = false); - const bn = _get_label_or_id(container); - const subject = `Return Request for Container ${bn} rejected`; + const bn = getProperty(box, "Number", case_sensitive = false); + + const subject = `Return Request for Box ${bn} rejected`; const body = `Dear ${firstName} ${lastName}, %0D%0D` + - `your return request for container ${bn} cannot be accepted.` + + `your return request for box number ${bn} cannot be accepted.` + `Kind Regards,%0D ${getUserName()}`; open_mail_generic(email, subject, body) } catch (err) { @@ -458,18 +494,18 @@ var loan_management = function () { } - var open_accept_return_mail = async function (loan, container) { + var open_accept_return_mail = async function (loan, box) { try { - const borrower = (await retrieve(getProperty(loan, "borrower"), case_sensitive = false))[0]; + const borrower = (await retrieve(getProperty(loan, "Borrower"), case_sensitive = false))[0]; const email = getProperty(borrower, "email", case_sensitive = false); const firstName = getProperty(borrower, "firstName", case_sensitive = false); const lastName = getProperty(borrower, "lastName", case_sensitive = false); - const bn = _get_label_or_id(container); + const bn = getProperty(box, "Number", case_sensitive = false); const date = getProperty(loan, "expectedReturn", case_sensitive = false); - const subject = `Return Request for Container ${bn} accepted`; + const subject = `Return Request for Box ${bn} accepted`; const body = `Dear ${firstName} ${lastName}, %0D%0D` + - `please return container ${bn} on ${date}. %0D%0D` + + `please return box number ${bn} on ${date}. %0D%0D` + `Kind Regards,%0D ${getUserName()}`; open_mail_generic(email, subject, body) } catch (err) { @@ -478,12 +514,12 @@ var loan_management = function () { } - var accept_return_function = async function (container) { + var accept_return_function = async function (box) { const wait = getPleaseWaitSpan(); - const actions_panel = get_actions_panel(container); + const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() - const loan = (await get_active_loans(container))[0]; + const loan = (await get_active_loans(box))[0]; const accept_return_request = { loan: getEntityID(loan), @@ -491,18 +527,18 @@ var loan_management = function () { const result = await run_script("loan_management/accept_return_request.py", accept_return_request); $(wait).remove(); - show_result(actions_panel, result, container); + show_result(actions_panel, result, box); - open_accept_return_mail(loan, container); + open_accept_return_mail(loan, box); } - var reject_return_function = async function (container) { + var reject_return_function = async function (box) { const wait = getPleaseWaitSpan(); - const actions_panel = get_actions_panel(container); + const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() - const loan = (await get_active_loans(container))[0]; + const loan = (await get_active_loans(box))[0]; const reject_return_request = { loan: getEntityID(loan), @@ -512,17 +548,17 @@ var loan_management = function () { "loan_management/reject_return_request.py", reject_return_request); $(wait).remove(); - show_result(actions_panel, result, container); + show_result(actions_panel, result, box); - open_reject_return_mail(loan, container); + open_reject_return_mail(loan, box); } /** * Add two buttons for confirming or rejecting a return request. */ - var add_confirm_reject_return = function (container, loan) { - const actionPanel = get_actions_panel(container); + var add_confirm_reject_return = function (box, loan) { + const actionPanel = get_actions_panel(box); if (userIsAdministrator() === true || userHasRole("curator") === true) { const confirmButton = $(_accept_return_button); actionPanel.append(confirmButton); @@ -533,13 +569,13 @@ var loan_management = function () { confirmButton.click(() => { confirmButton.remove(); rejectButton.remove(); - accept_return_function(container); + accept_return_function(box); }); rejectButton.click(() => { confirmButton.remove(); rejectButton.remove(); - reject_return_function(container); + reject_return_function(box); }); } else { actionPanel.append($(create_link_to_loan(loan, "Pending return request."))); @@ -549,51 +585,51 @@ var loan_management = function () { /** * Add a button for confirming the loan. */ - var add_confirm_loan_button = function (container, loan) { - const actionsPanel = get_actions_panel(container); + var add_confirm_loan_button = function (box, loan) { + const actionsPanel = get_actions_panel(box); if (userIsAdministrator() === true || userHasRole("curator") === true) { const but = $(_confirm_loan_button); actionsPanel.append(but); but.click(() => { but.remove(); - confirm_loan_function(container); + confirm_loan_function(box); }); } else { actionsPanel.append($(create_link_to_loan(loan, "Loan request accepted."))); } } - var return_function = async function (form, container) { + var return_function = async function (form, box) { const wait = getPleaseWaitSpan(); - const actions_panel = get_actions_panel(container); + const actions_panel = get_actions_panel(box); $(actions_panel).append(wait).find('btn').hide() $(form).hide(); const return_request = get_request_data(form); - const loan = (await get_active_loans(container))[0]; + const loan = (await get_active_loans(box))[0]; $.extend(return_request, { - container: getEntityID(container), + box: getEntityID(box), loan: getEntityID(loan), }); const result = await run_script("loan_management/request_return.py", return_request); $(wait).remove(); $(form).remove(); - show_result(actions_panel, result, container); + show_result(actions_panel, result, box); } /** - * Add buttons for borrowing containers. + * Add buttons for borrowing boxes. */ - var add_buttons = function (containers) { - $(containers).find('.container-loan-btn').remove(); - $(containers).each(async function () { + var add_buttons = function (boxes) { + $(boxes).find('.box-loan-btn').remove(); + $(boxes).each(async function () { const loan_state = await get_loan_state(this); if (typeof loan_state.loan !== "undefined") { if (loan_state.state == "loan_requested") { @@ -614,14 +650,15 @@ var loan_management = function () { } /** - * This function generates a form for borrowing containers. - * selectform can be used to generate the form for multiple containers. - * In case selectform is true, container will be ignored. + * This function generates a form for borrowing boxes. + * selectform can be used to generate the form for multiple boxes. + * In case selectform is true, box will be ignored. */ - var generate_form_borrow_checkout = async function (container) { + var generate_form_borrow_checkout = async function (box) { const email = window.localStorage["borrower_email"]; const firstname = window.localStorage["borrower_first_name"]; const lastname = window.localStorage["borrower_last_name"]; + const destination = window.localStorage["borrower_destination"]; var exp_return = window.localStorage["borrower_return_date"]; if (exp_return < Date.now()) { exp_return = ""; @@ -629,10 +666,11 @@ var loan_management = function () { return getHTMLForm("loan-forms/borrow_checkout", { - containerid: getEntityID(container), + boxid: getEntityID(box), first_name: firstname, last_name: lastname, email: email, + destination: destination, expected_return_date: exp_return, mindate: get_formatted_date("T") }); @@ -642,16 +680,17 @@ var loan_management = function () { /** * Generate form for a new Return Request, with fields * - * containerid (hidden) + * boxid (hidden) * first_name (pre-filled) * last_name (pre-filled) * email (pre-filled) * expectedreturn (pre-filled) + * currentlocation (pre-filled) * comment */ - var generate_return_form = async function (container) { - const loan = (await get_active_loans(container))[0]; - const borrower = (await retrieve(getProperty(loan, "borrower", case_sensitive = false)))[0]; + var generate_return_form = async function (box) { + const loan = (await get_active_loans(box))[0]; + const borrower = (await retrieve(getProperty(loan, "Borrower", case_sensitive = false)))[0]; var email = getProperty(borrower, "email", case_sensitive = false); var first_name = getProperty(borrower, "firstName", @@ -660,24 +699,26 @@ var loan_management = function () { case_sensitive = false); var exp_return = getProperty(loan, "expectedReturn", case_sensitive = false); + var cur_loc = getProperty(loan, "destination", case_sensitive = false); - return getHTMLForm("loan-forms/return_container", { - containerid: getEntityID(container), + return getHTMLForm("loan-forms/return_box", { + boxid: getEntityID(box), first_name: first_name, last_name: last_name, email: email, expectedreturn: exp_return, + currentlocation: cur_loc, mindate: get_formatted_date("T") }); } - var init = function (containers) { - const init_containers = containers || $(".caosdb-entity-panel") + var init = function (boxes) { + const init_boxes = boxes || $(".caosdb-entity-panel") .not("[data-version-successor]") - /* TODO: This fails easily (ContainerType) */ - .has(".caosdb-parent-name:contains('Container')") - .not(":has(.caosdb-parent-name:contains('ContainerT'))").toArray(); - add_buttons(init_containers); + /* TODO: This fails easily (BoxType) */ + .has(".caosdb-parent-name:contains('Box')") + .not(":has(.caosdb-parent-name:contains('BoxT'))").toArray(); + add_buttons(init_boxes); } return { @@ -695,5 +736,5 @@ var loan_management = function () { */ $(document).ready(function () { // TODO build variable - loan_management.init(); + awi_box_loan.init(); }); diff --git a/caosdb-server/scripting/bin/loan_management/accept_loan_request.py b/caosdb-server/scripting/bin/loan_management/accept_loan_request.py index c651d5d3954b9ddb326986e1fffe14185467e024..c4bc70401eb526130bac037756c350246d2b26a8 100755 --- a/caosdb-server/scripting/bin/loan_management/accept_loan_request.py +++ b/caosdb-server/scripting/bin/loan_management/accept_loan_request.py @@ -23,9 +23,9 @@ Accept a loan request. from __future__ import absolute_import import caosdb as db from caosadvancedtools.serverside.helper import print_success, get_timestamp -from container_loan import (main, get_loan, F_LOAN, assert_loan_state, - LOAN_ACCEPTED, S_LOAN_ACCEPTED, S_LOAN_REQUESTED, - get_borrower_names, set_property) +from box_loan import (main, get_loan, F_LOAN, assert_loan_state, + LOAN_ACCEPTED, S_LOAN_ACCEPTED, S_LOAN_REQUESTED, + get_borrower_names, set_property) def _accept_loan_request(data): diff --git a/caosdb-server/scripting/bin/loan_management/accept_return_request.py b/caosdb-server/scripting/bin/loan_management/accept_return_request.py index efbdc931cb99a29f5bb8e2d4a431048498a7ac67..b28a7d4c0aa2a61c0794566d01cb38f8a6a827cd 100755 --- a/caosdb-server/scripting/bin/loan_management/accept_return_request.py +++ b/caosdb-server/scripting/bin/loan_management/accept_return_request.py @@ -20,12 +20,14 @@ """ Accept a return request. """ +# @review Timm Fitschen 2022-03-16 from __future__ import absolute_import import caosdb as db from caosadvancedtools.serverside.helper import print_success, get_timestamp -from container_loan import (main, get_loan, set_property, F_LOAN, - assert_loan_state, RETURN_ACCEPTED, S_RETURN_ACCEPTED, - S_RETURN_REQUESTED, get_borrower_names) +from box_loan import (BOX_BORROWED, CONTENT, main, get_loan, set_property, + F_LOAN, assert_loan_state, RETURN_ACCEPTED, + RETURNLOCATION, S_RETURN_ACCEPTED, S_RETURN_REQUESTED, + get_borrower_names, set_location) def _accept_return_request(data): @@ -38,8 +40,16 @@ def _accept_return_request(data): # To be sure that it worked: assert_loan_state(loan, S_RETURN_ACCEPTED) + box = set_location(loan, RETURNLOCATION, update=False) + if loan.get_property(CONTENT) is not None and loan.get_property(CONTENT).value: + if box.get_property(CONTENT) is not None: + box.get_property(CONTENT).value = loan.get_property(CONTENT).value + else: + box.add_property(id=CONTENT.retrieve().id, + value=loan.get_property(CONTENT).value) db.Container().extend([ + box, loan ]).update() @@ -53,10 +63,15 @@ def accept_return_request(data): """ loan = _accept_return_request(data) fn, ln = get_borrower_names(loan) + box_id = loan.get_property(BOX_BORROWED).value.split("@")[0] - print_success('Thank you for accepting the return request by {fn} {ln}. See ' - '<a href="{loan}" title="Go to the accepted loan ' - 'request.">here</a>'.format(fn=fn, ln=ln, loan=loan.id)) + print_success('Thank you for accepting the return request by {fn} {ln}.<br>' + 'If necessary, location and content of the box have been ' + 'updated.<br>' + '<a href="/Entity/{bid}" title="Reload this page.">Reload</a> ' + 'to view the new Location.<br>' + 'See <a href="{loan}" title="Go to the accepted loan ' + 'request.">here</a>'.format(fn=fn, ln=ln, loan=loan.id, bid=box_id)) return 0 diff --git a/caosdb-server/scripting/bin/loan_management/box_loan.py b/caosdb-server/scripting/bin/loan_management/box_loan.py old mode 100755 new mode 100644 index beba112aaec660461ef419c0f4c46e90e7fcf288..393c715c36c34f647be10f69acf877dee0ea9bc9 --- a/caosdb-server/scripting/bin/loan_management/box_loan.py +++ b/caosdb-server/scripting/bin/loan_management/box_loan.py @@ -37,18 +37,20 @@ from caosadvancedtools.serverside.logging import configure_server_side_logging from caosdb.exceptions import EmptyUniqueQueryError from validate_email import validate_email -LOGGER_NAME = "container_loan" +LOGGER_NAME = "box_loan" LOGGER = logging.getLogger(LOGGER_NAME) # Form names -F_CONTAINER = "container" +F_BOX = "box" F_FIRST_NAME = "first_name" F_LAST_NAME = "last_name" F_EMAIL = "email" F_EXPECTED_RETURN_DATE = "expected_return_date" F_EXHAUST_CONTENTS = "exhaust_contents" F_COMMENT = "comment" +F_DESTINATION = "destination" F_LOAN = "loan" +F_CURRENT_LOCATION = "current_location" S_RETURNED = "returned" S_RETURN_ACCEPTED = "return_accepted" @@ -59,24 +61,26 @@ S_LOAN_REQUESTED = "loan_requested" _EMTPY_DATA = { - F_CONTAINER: None, + F_BOX: None, F_FIRST_NAME: None, F_LAST_NAME: None, F_EMAIL: None, F_EXPECTED_RETURN_DATE: None, F_EXHAUST_CONTENTS: None, F_COMMENT: None, + F_DESTINATION: None, F_LOAN: None, + F_CURRENT_LOCATION: None } -CONTAINER = db.RecordType(name="Container") -CONTAINER_RETURNED = "Container (returned)" -CONTAINER_BORROWED = "Container (borrowed)" +BOX = db.RecordType(name="Box") +BOX_RETURNED = "Box (returned)" +BOX_BORROWED = "Box (borrowed)" PERSON = db.RecordType(name="Person") LOAN = db.RecordType(name="Loan") RECORD_TYPES = [ - CONTAINER, + BOX, PERSON, LOAN, ] @@ -85,6 +89,9 @@ RECORD_TYPES = [ FIRST_NAME = db.Property(name="firstName", datatype=db.TEXT) LAST_NAME = db.Property(name="lastName", datatype=db.TEXT) EMAIL = db.Property(name="email", datatype=db.TEXT) +LOCATION = db.RecordType(name="Location") +DESTINATION = db.Property(name="LoanLocation", datatype="Location") +RETURNLOCATION = db.Property(name="ReturnLocation", datatype="Location") COMMENT = db.Property(name="comment", datatype=db.TEXT) EXHAUST_CONTENTS = db.Property(name="exhaustContents", datatype=db.BOOLEAN) BORROWER = db.Property(name="Borrower", datatype=PERSON.name) @@ -96,11 +103,15 @@ LENT = db.Property(name="lent", datatype=db.DATETIME) RETURN_REQUESTED = db.Property(name="returnRequested", datatype=db.DATETIME) RETURN_ACCEPTED = db.Property(name="returnAccepted", datatype=db.DATETIME) RETURNED = db.Property(name="returned", datatype=db.DATETIME) +BOX_NUMBER = db.Property(name="Number", datatype=db.TEXT) PROPERTIES = [ FIRST_NAME, LAST_NAME, EMAIL, + LOCATION, + DESTINATION, + RETURNLOCATION, COMMENT, EXHAUST_CONTENTS, BORROWER, @@ -112,6 +123,7 @@ PROPERTIES = [ RETURN_REQUESTED, RETURN_ACCEPTED, RETURNED, + BOX_NUMBER, ] @@ -211,10 +223,10 @@ def get_record_by_id(parent, entity_id): unique=True) -def get_container(container): - """ Retrieve a container record by id. """ +def get_box(box): + """ Retrieve a box record by id. """ - return get_record_by_id(CONTAINER.name, container) + return get_record_by_id(BOX.name, box) def get_loan(loan): @@ -294,10 +306,12 @@ def assert_email_pattern(email): EmailPatternError """ - if email is None or not validate_email(email_address=email, - check_format=True, check_smtp=False, - check_dns=False, - check_blacklist=False): + if email is None or not validate_email( + email_address=email, + check_format=True, + check_blacklist=False, + check_dns=False, + check_smtp=False): raise EmailPatternError(email) @@ -338,24 +352,25 @@ def get_external_server_uri(): def send_loan_request_mail(data, borrower, loan): try: - containerid = str(loan.get_property(CONTAINER).value).split("@")[0] - container = get_container(containerid) + boxid = str(loan.get_property(BOX).value).split("@")[0] + box = get_box(boxid) + boxnumber = get_property_value(box, BOX_NUMBER, "UNKNOWN") - link = "{uri}Entity/{containerid}&{loanid}".format( + link = "{uri}Entity/{boxid}&{loanid}".format( uri=get_external_server_uri(), - containerid=containerid, + boxid=boxid, loanid=loan.id) body = """Dear Curator, - a new loan has been request by {borrower} for container {containername}. + a new loan has been request by {borrower} for box number {boxnumber}. Loan request: {data} - View container and loan records: + View box and loan records: {link} - """.format(containername=container.name, borrower=get_requester_string(borrower), + """.format(boxnumber=boxnumber, borrower=get_requester_string(borrower), data=data, link=link) send_mail( @@ -363,11 +378,11 @@ def send_loan_request_mail(data, borrower, loan): to=EMAIL_CURATOR, subject="loan request", body=body) - print_info("An email has been sent to the responsible container curator.") + print_info("An email has been sent to the responsible box curator.") except BaseException as e: - print_error("""Sending an email to the responsible container curator failed + print_error("""Sending an email to the responsible box curator failed -for an unknown reason. Please inform the container curator by yourself about your +for an unknown reason. Please inform the box curator by yourself about your loan request and that the email sending failed. Thank you very much for your help.""") LOGGER.error(e) @@ -375,36 +390,40 @@ help.""") def send_return_request_mail(data, returner, loan): try: - containerid = str(loan.get_property(CONTAINER).value).split("@")[0] - container = get_container(containerid) + boxid = str(loan.get_property(BOX).value).split("@")[0] + box = get_box(boxid) + boxnumber = get_property_value(box, BOX_NUMBER, "UNKNOWN") + current_location = data[F_CURRENT_LOCATION] - link = "{uri}Entity/{containerid}&{loanid}".format( + link = "{uri}Entity/{boxid}&{loanid}".format( uri=get_external_server_uri(), - containerid=containerid, + boxid=boxid, loanid=loan.id) body = """Dear Curator, - a new return request by {returner} for container {containername} is pending. + a new return request by {returner} for box number {boxnumber} is pending. + The current location of the box according to the return request is + "{current_location}". Return request: {data} - View container and loan records: + View box and loan records: {link} - """.format(containername=container.name, returner=get_requester_string(returner), - data=data, link=link) + """.format(boxnumber=boxnumber, returner=get_requester_string(returner), + data=data, link=link, current_location=current_location) send_mail( from_addr=EMAIL_FROM, to=EMAIL_CURATOR, subject="return request", body=body) - print_info("An email has been sent to the responsible container curator.") + print_info("An email has been sent to the responsible box curator.") except BaseException as e: - print_error("""Sending an email to the responsible container curator failed + print_error("""Sending an email to the responsible box curator failed -for an unknown reason. Please inform the container curator by yourself about this +for an unknown reason. Please inform the box curator by yourself about this return request and that the email sending failed. Thank you very much for your help.""") @@ -526,6 +545,23 @@ def _caller(func, args): return func(data) +def set_location(loan, kind, update=True): + """Set the location of the box to a location of the loan. + + kind can be RETURNLOCATION or DESTINATION + """ + # @review Timm Fitschen 2022-03-16 + + if loan.get_property(kind) is not None: + box_id = loan.get_property(BOX_BORROWED).value.split("@")[0] + box = db.Record(id=box_id).retrieve() + set_property(box, LOCATION, loan.get_property(kind).value) + if update: + box.update() + else: + return box + + def main(func, args=None): # configure_server_side_logging(LOGGER_NAME) try: diff --git a/caosdb-server/scripting/bin/loan_management/confirm_loan.py b/caosdb-server/scripting/bin/loan_management/confirm_loan.py index 07739e479c6ab2eb067b6a8dda9cbd6db3682bff..4b463b7cad7435d2484e8c9ce5d63585b7dfc339 100755 --- a/caosdb-server/scripting/bin/loan_management/confirm_loan.py +++ b/caosdb-server/scripting/bin/loan_management/confirm_loan.py @@ -25,19 +25,24 @@ from __future__ import absolute_import import caosdb as db from caosadvancedtools.serverside.helper import get_timestamp, print_success -from container_loan import (CONTAINER, CONTAINER_BORROWED, F_LOAN, LENT, S_LENT, - S_LOAN_ACCEPTED, assert_loan_state, get_borrower_names, - get_loan, main, set_property) +from box_loan import (BOX, BOX_BORROWED, DESTINATION, F_LOAN, LENT, S_LENT, + S_LOAN_ACCEPTED, assert_loan_state, get_borrower_names, + get_loan, main, set_location, set_property) -def _set_lent_container(loan): - """Set the container version to HEAD. +def _set_lent_box(loan): + """Set the box version to HEAD. - This stores the version of the container when it was delivered to the borrower. + This stores the version of the box when it was delivered to the borrower. """ - container_prop = loan.get_property(CONTAINER) - container_prop.name = CONTAINER_BORROWED - container_prop.value = str(container_prop.value) + "@HEAD" + box_prop = loan.get_property(BOX) + box_prop.name = BOX_BORROWED + box_prop.value = str(box_prop.value) + "@HEAD" + + +def set_loan_location(loan): + """Set the location of the box to the return location of the loan. """ + set_location(loan, DESTINATION) def _confirm_loan(data): @@ -47,7 +52,10 @@ def _confirm_loan(data): # This changes the state from "loan_accepted" to "lent". set_property(loan, LENT, get_timestamp()) - _set_lent_container(loan) + _set_lent_box(loan) + + # updates the box location + set_loan_location(loan) # To be sure that it worked: assert_loan_state(loan, S_LENT) @@ -67,11 +75,15 @@ def confirm_loan(data): loan = _confirm_loan(data) fn, ln = get_borrower_names(loan) loan = get_loan(data[F_LOAN]) - container_id = loan.get_property(CONTAINER_BORROWED).value.split("@")[0] + box_id = loan.get_property(BOX_BORROWED).value.split("@")[0] print_success('Thank you for confirming the loan request by {fn} {ln}.<br>' + 'The Location of the Box was updated. ' + '<a href="/Entity/{bid}" title="Reload this page.">Reload</a> ' + 'to view the new Location.<br>' 'You can also checkout the new loan state ' '<a href="{loan}" title="Go to the confirmed loan ' - 'record.">here</a>'.format(fn=fn, ln=ln, loan=loan.id)) + 'record.">here</a>'.format(fn=fn, ln=ln, loan=loan.id, + bid=box_id)) return 0 diff --git a/caosdb-server/scripting/bin/loan_management/manual_return.py b/caosdb-server/scripting/bin/loan_management/manual_return.py index 68ff990feb3fd4c993acc5740bcf47cb2ba22159..9aa7e83c3f3210ad719d41a0b1aa00896e7e5aca 100755 --- a/caosdb-server/scripting/bin/loan_management/manual_return.py +++ b/caosdb-server/scripting/bin/loan_management/manual_return.py @@ -18,36 +18,41 @@ # # """ -Manually return a container. +Manually return a box. """ from __future__ import absolute_import import caosdb as db from caosadvancedtools.serverside.helper import get_timestamp, print_success -from container_loan import (CONTAINER, CONTAINER_BORROWED, CONTAINER_RETURNED, - CONTENT, F_LOAN, RETURNED, S_RETURN_ACCEPTED, - S_RETURNED, assert_loan_state, get_borrower_names, - get_loan, main, set_property) +from box_loan import (BOX, BOX_BORROWED, BOX_RETURNED, CONTENT, F_LOAN, + RETURNED, RETURNLOCATION, S_RETURN_ACCEPTED, S_RETURNED, + assert_loan_state, get_borrower_names, get_loan, main, + set_location, set_property) -def _set_returned_container(loan): - """Add a `Container (returned)` property to the loan. +def _set_returned_box(loan): + """Add a `Box (returned)` property to the loan. - This stores the version of the container that was returned by a borrower. + This stores the version of the box that was returned by a borrower. """ - container_id = loan.get_property(CONTAINER_BORROWED).value.split("@")[0] - loan.add_property(property=CONTAINER, - name=CONTAINER_RETURNED, - value=container_id + "@HEAD") + box_id = loan.get_property(BOX_BORROWED).value.split("@")[0] + loan.add_property(property=BOX, + name=BOX_RETURNED, + value=box_id + "@HEAD") + + +def set_return_location(loan): + """Set the location of the box to the return location of the loan. """ + set_location(loan, RETURNLOCATION) def set_content(loan): - """Set the content to the container to the one given in the loan. """ - container_id = loan.get_property(CONTAINER_BORROWED).value.split("@")[0] - container = db.Record(id=container_id).retrieve() - set_property(container, CONTENT, loan.get_property(CONTENT).value) - container.update() + """Set the content to the box to the one given in the loan. """ + box_id = loan.get_property(BOX_BORROWED).value.split("@")[0] + box = db.Record(id=box_id).retrieve() + set_property(box, CONTENT, loan.get_property(CONTENT).value) + box.update() def _manual_return(data): @@ -59,11 +64,16 @@ def _manual_return(data): set_property(loan, RETURNED, get_timestamp()) set_property(loan, RETURNED, get_timestamp()) + # updates the box location and content + # *currently this is not wanted* + # set_return_location(loan) + # set_content(loan) + # To be sure that it worked: assert_loan_state(loan, S_RETURNED) - # add returned container - _set_returned_container(loan) + # add returned box + _set_returned_box(loan) db.Container().extend([ loan @@ -73,16 +83,19 @@ def _manual_return(data): def manual_return(data): - """Return a container from a borrower. + """Return a box from a borrower. I.e. update the `Loan` Record and add the `returned` Property. """ loan = _manual_return(data) fn, ln = get_borrower_names(loan) loan = get_loan(data[F_LOAN]) + box_id = loan.get_property(BOX_BORROWED).value.split("@")[0] - print_success('The container borrowed by {fn} {ln} '.format(fn=fn, ln=ln) - + 'has been returned.<br>') + print_success('The box borrowed by {fn} {ln} '.format(fn=fn, ln=ln) + + 'has been returned and the Box Location has been updated.' + '<a href="/Entity/{bid}" title="Reload this page.">Reload</a> ' + 'to view the new Location.<br>'.format(bid=box_id)) return 0 diff --git a/caosdb-server/scripting/bin/loan_management/reject_return_request.py b/caosdb-server/scripting/bin/loan_management/reject_return_request.py index 6809f1fc73ee5167dad33778a818616e6ff30b53..0d8227edc96ec1482d30d44448dfa1788fafac7a 100755 --- a/caosdb-server/scripting/bin/loan_management/reject_return_request.py +++ b/caosdb-server/scripting/bin/loan_management/reject_return_request.py @@ -23,9 +23,9 @@ Reject a return request. from __future__ import absolute_import import caosdb as db from caosadvancedtools.serverside.helper import print_success -from container_loan import (main, get_loan, F_LOAN, assert_loan_state, - S_RETURN_REQUESTED, RETURN_REQUESTED, S_LENT, - get_borrower_names) +from box_loan import (main, get_loan, F_LOAN, assert_loan_state, + S_RETURN_REQUESTED, RETURN_REQUESTED, S_LENT, + get_borrower_names) def _reject_return_request(data): diff --git a/caosdb-server/scripting/bin/loan_management/request_loan.py b/caosdb-server/scripting/bin/loan_management/request_loan.py index 486f3e328349500c25d25b895a741d93895147d0..93714405aa3332def925695eeadc2d4ceb21ebe0 100755 --- a/caosdb-server/scripting/bin/loan_management/request_loan.py +++ b/caosdb-server/scripting/bin/loan_management/request_loan.py @@ -25,25 +25,27 @@ from __future__ import absolute_import import caosdb as db from caosadvancedtools.serverside.helper import get_timestamp, print_success -from container_loan import (BORROWER, CONTAINER, COMMENT, EXHAUST_CONTENTS, - EXPECTED_RETURN, F_CONTAINER, F_COMMENT, F_EMAIL, - F_EXHAUST_CONTENTS, F_EXPECTED_RETURN_DATE, - F_FIRST_NAME, F_LAST_NAME, FIRST_NAME, LAST_NAME, - LOAN, LOAN_REQUESTED, assert_date_in_future, - assert_key_in_data, get_container, get_person, main, - send_loan_request_mail) +from box_loan import (BORROWER, BOX, COMMENT, DESTINATION, EXHAUST_CONTENTS, + EXPECTED_RETURN, F_BOX, F_COMMENT, F_DESTINATION, + F_EMAIL, F_EXHAUST_CONTENTS, F_EXPECTED_RETURN_DATE, + F_FIRST_NAME, F_LAST_NAME, FIRST_NAME, LAST_NAME, LOAN, + LOAN_REQUESTED, assert_date_in_future, + assert_key_in_data, get_box, get_person, main, + send_loan_request_mail) -def create_loan(container, borrower, expected_return, exhaust_contents, comment): +def create_loan(box, borrower, expected_return, exhaust_contents, comment, + destination): """ Create a new loan record. """ loan = db.Record().add_parent(LOAN) - loan.add_property(CONTAINER, container) + loan.add_property(BOX, box) loan.add_property(BORROWER, borrower) loan.add_property(EXPECTED_RETURN, expected_return) loan.add_property(EXHAUST_CONTENTS, exhaust_contents) loan.add_property(COMMENT, comment) + loan.add_property(DESTINATION, destination) loan.add_property(LOAN_REQUESTED, get_timestamp()) return loan @@ -51,8 +53,9 @@ def create_loan(container, borrower, expected_return, exhaust_contents, comment) _OBLIGATORY = [ F_EXPECTED_RETURN_DATE, - F_CONTAINER, + F_BOX, F_COMMENT, + F_DESTINATION, F_EMAIL, F_LAST_NAME, F_FIRST_NAME, @@ -77,22 +80,23 @@ def _check_data(data): def _issue_loan_request(data): """ Insert a loan record a insert/update a person record. """ data = _check_data(data) - container = get_container(data[F_CONTAINER]) + box = get_box(data[F_BOX]) borrower = get_person(firstname=data[F_FIRST_NAME], lastname=data[F_LAST_NAME], email=data[F_EMAIL]) - loan = create_loan(container=data[F_CONTAINER], + loan = create_loan(box=data[F_BOX], borrower=borrower, expected_return=data[F_EXPECTED_RETURN_DATE], exhaust_contents=data[F_EXHAUST_CONTENTS], - comment=data[F_COMMENT]) + comment=data[F_COMMENT], + destination=data[F_DESTINATION]) c = db.Container().extend([ borrower, loan ]) c.insert() - return borrower, loan, container + return borrower, loan, box def issue_loan_request(data): @@ -103,8 +107,9 @@ def issue_loan_request(data): * borrower * expectedReturn * exhaustContents - * Container + * Box * comment + * destination The borrower is a Person Record, with firstName, lastName and email, identified by either email, oder firstName+lastName. diff --git a/caosdb-server/scripting/bin/loan_management/request_return.py b/caosdb-server/scripting/bin/loan_management/request_return.py index 778f5d0fce689d3ab189c0ec78e391884c52787f..d64e72e49c7cbcf51d6277d2d8d3ca34b1c81838 100755 --- a/caosdb-server/scripting/bin/loan_management/request_return.py +++ b/caosdb-server/scripting/bin/loan_management/request_return.py @@ -28,19 +28,20 @@ from __future__ import absolute_import from caosadvancedtools.serverside.helper import get_timestamp, print_success -from container_loan import (BORROWER, COMMENT, CONTENT, EXPECTED_RETURN, F_COMMENT, - F_EMAIL, F_EXPECTED_RETURN_DATE, - F_FIRST_NAME, F_LAST_NAME, F_LOAN, FIRST_NAME, LAST_NAME, - RETURN_REQUESTED, S_LENT, - assert_date_in_future, assert_key_in_data, - assert_loan_state, get_loan, get_person, main, - send_return_request_mail, set_property) +from box_loan import (BORROWER, COMMENT, CONTENT, EXPECTED_RETURN, F_COMMENT, + F_CURRENT_LOCATION, F_EMAIL, F_EXPECTED_RETURN_DATE, + F_FIRST_NAME, F_LAST_NAME, F_LOAN, FIRST_NAME, LAST_NAME, + RETURN_REQUESTED, RETURNLOCATION, S_LENT, + assert_date_in_future, assert_key_in_data, + assert_loan_state, get_loan, get_person, main, + send_return_request_mail, set_property) def _check_data(data): assert_key_in_data(data, F_EXPECTED_RETURN_DATE) assert_key_in_data(data, F_LOAN) assert_key_in_data(data, F_COMMENT) + assert_key_in_data(data, F_CURRENT_LOCATION) assert_date_in_future( data[F_EXPECTED_RETURN_DATE], @@ -60,6 +61,7 @@ def _issue_return_request(data): set_property(loan, BORROWER, returner.id) set_property(loan, EXPECTED_RETURN, data[F_EXPECTED_RETURN_DATE]) set_property(loan, CONTENT, data[F_COMMENT]) + set_property(loan, RETURNLOCATION, data[F_CURRENT_LOCATION]) loan.update() diff --git a/caosdb-server/scripting/bin/test/loan_management/box_loan.py b/caosdb-server/scripting/bin/test/loan_management/box_loan.py new file mode 120000 index 0000000000000000000000000000000000000000..034b4c7dfca3b7d5c0e6ac80ad830c176658934a --- /dev/null +++ b/caosdb-server/scripting/bin/test/loan_management/box_loan.py @@ -0,0 +1 @@ +../../loan_management/box_loan.py \ No newline at end of file diff --git a/caosdb-server/scripting/bin/test/loan_management/manual_return.py b/caosdb-server/scripting/bin/test/loan_management/manual_return.py new file mode 120000 index 0000000000000000000000000000000000000000..f0eab4f92842389daf8a57e71480f8238d93e421 --- /dev/null +++ b/caosdb-server/scripting/bin/test/loan_management/manual_return.py @@ -0,0 +1 @@ +../../loan_management/manual_return.py \ No newline at end of file diff --git a/caosdb-server/scripting/bin/test/loan_management/request_loan.py b/caosdb-server/scripting/bin/test/loan_management/request_loan.py new file mode 120000 index 0000000000000000000000000000000000000000..0c060509fbb552f9e92d8b890acbbdb1db3e2e7c --- /dev/null +++ b/caosdb-server/scripting/bin/test/loan_management/request_loan.py @@ -0,0 +1 @@ +../../loan_management/request_loan.py \ No newline at end of file diff --git a/caosdb-server/scripting/bin/test/loan_management/request_loan_form.json b/caosdb-server/scripting/bin/test/loan_management/request_loan_form.json new file mode 100644 index 0000000000000000000000000000000000000000..3db881151c2bf48b006a98cfcd90609fcf7d7c8b --- /dev/null +++ b/caosdb-server/scripting/bin/test/loan_management/request_loan_form.json @@ -0,0 +1,12 @@ +{ + "loan": 12345, + "box": 2345, + "first_name": "Anna", + "last_name": "Lytik", + "email": "a.lytik@example.com", + "expected_return_date": "2020-12-24", + "exhaust_contents": "true", + "comment": "this is a comment", + "destination": "blablabla destination", + "current_location": "blublublub" +} diff --git a/caosdb-server/scripting/bin/test/loan_management/request_return.py b/caosdb-server/scripting/bin/test/loan_management/request_return.py new file mode 120000 index 0000000000000000000000000000000000000000..b080e7193350a231c118aa9d493966ca5ffe45d8 --- /dev/null +++ b/caosdb-server/scripting/bin/test/loan_management/request_return.py @@ -0,0 +1 @@ +../../loan_management/request_return.py \ No newline at end of file diff --git a/caosdb-server/scripting/bin/test/loan_management/test_box_loan.py b/caosdb-server/scripting/bin/test/loan_management/test_box_loan.py new file mode 100644 index 0000000000000000000000000000000000000000..a1b17f253e8014b855a9462d3e8d3dc30cc7e1dc --- /dev/null +++ b/caosdb-server/scripting/bin/test/loan_management/test_box_loan.py @@ -0,0 +1,99 @@ +from os.path import abspath, dirname, join +from pytest import raises +from caosdb import get_connection, configure_connection +from caosdb.connection.mockup import (MockUpServerConnection, MockUpResponse) +from box_loan import (_caller, create_person, query_person, get_person, + EMAIL, FIRST_NAME, LAST_NAME, PERSON, + assert_date_in_future, DataError, EmailPatternError, + assert_email_pattern) + + +def setup(): + configure_connection(url="unittests", username="testuser", + password_method="plain", + password="testpassword", timeout=200, + implementation=MockUpServerConnection) + + +def get_data_example(): + return abspath(join(dirname(__file__), "request_loan_form.json")) + + +def test_caller(): + args = [get_data_example()] + + def test_func(data): + assert data["box"] == 2345, "should contain the data from json" + return 1337 + + assert _caller(test_func, args) == 1337 + + +def test_create_person(): + p = create_person("anna", "lytik", "a@b.com") + assert p.get_parents()[0].name == PERSON.name + assert p.get_property(FIRST_NAME.name).value == "anna" + assert p.get_property(LAST_NAME.name).value == "lytik" + assert p.get_property(EMAIL.name).value == "a@b.com" + + +def test_query_person(): + connection = get_connection() + entities = ('<Response><Query results="1"/>' + '<RecordType id="1234"/></Response>') + + def query_resource(**kwargs): + query = kwargs["path"].split("query=")[1] + assert query.startswith("FIND%20RECORD%20" + PERSON.name) + return MockUpResponse(200, {}, entities) + connection._delegate_connection.resources.append(query_resource) + + p = query_person("petri", "schale", "c@d.com") + assert p.id == 1234 + + +def test_get_person_with_update(): + connection = get_connection() + person_xml = ( + '<Response>{query}' + '<Record id="1234">' + '<Property name="email">{email}</Property>' + '</Record>' + '</Response>') + + def query_resource(**kwargs): + if kwargs["method"] == "GET": + return MockUpResponse(200, {}, + person_xml.format( + query='<Query results="1"/>', + email="old@email")) + else: + return MockUpResponse(200, {}, + person_xml.format( + query='', + email="new@email")) + connection._delegate_connection.resources.append(query_resource) + + p = get_person("firstname", None, "old@email") + assert p.get_property(EMAIL.name).value == "new@email" + + +def test_create_person_with_wrong_email_pattern(): + with raises(EmailPatternError): + assert_email_pattern("@asdf") + with raises(EmailPatternError): + assert_email_pattern("asdf") + with raises(EmailPatternError): + assert_email_pattern("asdfa.de") + with raises(EmailPatternError): + assert_email_pattern("sdfg@sdfg") + + # the following shoudl be ok + assert_email_pattern("a@b.da") + assert_email_pattern("\"#\"@รถ.de") + + +def test_assert_date_in_future(): + assert assert_date_in_future("2050-01-01") is None + with raises(DataError): + assert_date_in_future("1971-01-01") diff --git a/caosdb-server/scripting/bin/test/loan_management/test_manual_return.py b/caosdb-server/scripting/bin/test/loan_management/test_manual_return.py new file mode 100644 index 0000000000000000000000000000000000000000..790575c73acc1c8d4fa5bb66f37c49d84b6845a3 --- /dev/null +++ b/caosdb-server/scripting/bin/test/loan_management/test_manual_return.py @@ -0,0 +1,18 @@ +from caosdb import Record +from box_loan import BOX, BOX_RETURNED, BOX_BORROWED +from manual_return import _set_returned_box + + +def test_set_returned_box(): + loan = Record() + loan.add_property(BOX, name=BOX_BORROWED, value="1234@abcd") + + assert len(loan.get_properties()) == 1 + assert loan.get_property(BOX_BORROWED).value == "1234@abcd" + assert loan.get_property(BOX_RETURNED) is None + + _set_returned_box(loan) + + assert len(loan.get_properties()) == 2 + assert loan.get_property(BOX_BORROWED).value == "1234@abcd" + assert loan.get_property(BOX_RETURNED).value == "1234@HEAD" diff --git a/caosdb-server/scripting/bin/test/loan_management/test_request_loan.py b/caosdb-server/scripting/bin/test/loan_management/test_request_loan.py new file mode 100644 index 0000000000000000000000000000000000000000..d13537bd954ed4aa9d21be36785ee7d27ab235a7 --- /dev/null +++ b/caosdb-server/scripting/bin/test/loan_management/test_request_loan.py @@ -0,0 +1,102 @@ +from os.path import abspath, dirname, join +from pytest import raises +from caosdb import get_connection, configure_connection, Record +from caosdb.connection.mockup import (MockUpServerConnection, MockUpResponse) +from caosadvancedtools.serverside.helper import get_data +from box_loan import (PERSON, FIRST_NAME, EMAIL, BOX, BORROWER, + EXPECTED_RETURN, EXHAUST_CONTENTS, COMMENT, + DESTINATION, F_FIRST_NAME, F_EMAIL, + F_EXPECTED_RETURN_DATE, DataError, BOX_NUMBER) +from request_loan import (create_loan, _issue_loan_request) + + +def setup(): + configure_connection(url="unittests", username="testuser", + password_method="plain", + password="testpassword", timeout=200, + implementation=MockUpServerConnection) + + +def get_data_example(): + return abspath(join(dirname(__file__), "request_loan_form.json")) + + +def test_issue_loan_request_with_wrong_return_date(): + data = get_data(get_data_example()) + data[F_EXPECTED_RETURN_DATE] = "1983-02-03" + with raises(DataError): + _issue_loan_request(data) + + +def test_create_loan(): + borrower = Record(name="Person1") + l = create_loan(1234, + borrower, + "2020-03-23", + False, + "blablabla", + "my office") + assert l.get_property(BOX.name).value == 1234 + assert l.get_property(BORROWER.name).value == borrower + assert l.get_property(EXPECTED_RETURN.name).value == "2020-03-23" + assert l.get_property(EXHAUST_CONTENTS.name).value == False + assert l.get_property(COMMENT.name).value == "blablabla" + assert l.get_property(DESTINATION.name).value == "my office" + + +def test_issue_loan_request(): + data = get_data(get_data_example()) + person_xml = ( + '<Response>{{query}}' + '<Record id="1234">' + '<Property name="{FN}">{FIRST_NAME}</Property>' + '<Property name="{EMAIL}">{{email}}</Property>' + '</Record>' + '</Response>').format(FN=FIRST_NAME.name, EMAIL=EMAIL.name, + FIRST_NAME=data[F_FIRST_NAME]) + box_xml = ( + '<Response>{{query}}' + '<Record id="2345">' + '<Property name="{BN}">{BOXNUM}</Property>' + '</Record>' + '</Response>').format(BN=BOX_NUMBER.name, BOXNUM="0815") + + def query_resource(**kwargs): + if kwargs["method"] == "GET" and BOX.name in kwargs["path"]: + query = kwargs["path"].split("query=")[1] + assert query.startswith("FIND%20RECORD%20" + BOX.name) + return MockUpResponse(200, {}, + box_xml.format( + query='<Query results="1"/>')) + if kwargs["method"] == "GET" and PERSON.name in kwargs["path"]: + query = kwargs["path"].split("query=")[1] + assert query.startswith("FIND%20RECORD%20" + PERSON.name) + return MockUpResponse(200, {}, + person_xml.format( + query='<Query results="1"/>', + email="old@email")) + elif kwargs["method"] == "PUT": + assert data[F_EMAIL] in kwargs["body"].decode("utf-8") + return MockUpResponse(200, {}, + person_xml.format( + query='', + email=data[F_EMAIL])) + else: + assert "" in kwargs["body"].decode("utf-8") + body = kwargs["body"].decode( + "utf-8").replace('"-1"', '"4567"').replace("Insert", "Response") + return MockUpResponse(200, {}, body) + + connection = get_connection() + connection._delegate_connection.resources.append(query_resource) + + borrower, loan, box = _issue_loan_request(data) + + assert str(loan.id) == "4567" + assert str(loan.get_property(BOX).value) == "2345" + assert str(loan.get_property(BORROWER).value) == str(borrower.id) + assert str(borrower.id) == "1234" + assert borrower.get_property(EMAIL).value == data[F_EMAIL] + assert borrower.get_property(FIRST_NAME).value == data[F_FIRST_NAME] + assert str(box.id) == "2345" + assert box.get_property(BOX_NUMBER).value == "0815" diff --git a/caosdb-server/scripting/bin/test/loan_management/test_request_return.py b/caosdb-server/scripting/bin/test/loan_management/test_request_return.py new file mode 100644 index 0000000000000000000000000000000000000000..fa7f52c7379ec9e8066740573ecdb0289413af82 --- /dev/null +++ b/caosdb-server/scripting/bin/test/loan_management/test_request_return.py @@ -0,0 +1,134 @@ +from os.path import abspath, dirname, join +from pytest import raises +from caosdb import get_connection, configure_connection +from caosdb.connection.mockup import (MockUpServerConnection, MockUpResponse) +from caosadvancedtools.serverside.helper import get_data +from box_loan import (FIRST_NAME, EMAIL, LAST_NAME, F_FIRST_NAME, + F_LAST_NAME, F_EMAIL, EXPECTED_RETURN, LENT, + RETURN_REQUESTED, PERSON, BORROWER, BOX, + LOAN_REQUESTED, StateError, F_EXPECTED_RETURN_DATE, + DataError, LOAN) +from request_return import get_loan, _issue_return_request + + +def setup(): + configure_connection(url="unittests", username="testuser", + password_method="plain", + password="testpassword", timeout=200, + implementation=MockUpServerConnection) + + +def get_data_example(): + return abspath(join(dirname(__file__), "request_loan_form.json")) + + +def append_resource(resource): + connection = get_connection() + connection._delegate_connection.resources.append(resource) + + +def test_get_loan(): + entities = '<Response><Query results="1"/><Record id="1234"/></Response>' + + def resource(**kwargs): + assert "RECORD%20Loan%20WITH%20ID%20%3D%201234" in kwargs["path"] + return MockUpResponse(200, {}, entities) + append_resource(resource) + + loan = get_loan(1234) + assert loan.id == 1234 + + +def test_return_request_with_wrong_return_date(): + data = get_data(get_data_example()) + data[F_EXPECTED_RETURN_DATE] = "1983-02-03" + with raises(DataError): + _issue_return_request(data) + + data[F_EXPECTED_RETURN_DATE] = "asdf" + with raises(DataError): + _issue_return_request(data) + + data[F_EXPECTED_RETURN_DATE] = "" + with raises(DataError): + _issue_return_request(data) + + del data[F_EXPECTED_RETURN_DATE] + with raises(DataError): + _issue_return_request(data) + + +def test_return_request_with_wrong_loan_states(): + data = get_data(get_data_example()) + loan_xml = ( + '<Response><Query results="1"/><Record id="12345">' + '<Parent name="{LO}"/>' + '<Property name="{ER}">1985-05-21</Property>' + '<Property name="{BOX}">2345</Property>' + '<Property name="{BOR}">1234</Property>' + '<Property name="{L_REQ}">2020-03-02</Property>' + '</Record></Response>').format(ER=EXPECTED_RETURN.name, + BOX=BOX.name, BOR=BORROWER.name, + L_REQ=LOAN_REQUESTED.name, + LO=LOAN.name) + + def resource(**kwargs): + return MockUpResponse(200, {}, loan_xml) + append_resource(resource) + + with raises(StateError): + _issue_return_request(data) + + +def test_issue_return_request(): + data = get_data(get_data_example()) + person_xml = ( + '<Response><Query results="1"/>' + '<Record id="1234">' + '<Property name="{FN}">{FIRST_NAME}</Property>' + '<Property name="{LN}">{LAST_NAME}</Property>' + '<Property name="{EM}">{EMAIL}</Property>' + '</Record>' + '</Response>').format(FN=FIRST_NAME.name, EM=EMAIL.name, + LN=LAST_NAME.name, FIRST_NAME=data[F_FIRST_NAME], + LAST_NAME=data[F_LAST_NAME], EMAIL=data[F_EMAIL]) + loan_xml = ( + '<Response><Query results="1"/><Record id="12345">' + '<Parent name="{LO}"/>' + '<Property name="{ER}">1985-05-21</Property>' + '<Property name="{BOX}">2345</Property>' + '<Property name="{BOR}">1234</Property>' + '<Property name="{LENT}">2020-03-02</Property>' + '{{RR}}' + '</Record></Response>').format(ER=EXPECTED_RETURN.name, BOX=BOX.name, + BOR=BORROWER.name, LENT=LENT.name, + LO=LOAN.name) + + def resource(**kwargs): + if kwargs["method"] == "GET": + if "12345" in kwargs["path"]: + # retrieve loan + return MockUpResponse(200, {}, + loan_xml.format(RR="")) + + query = kwargs["path"].split("query=")[1] + assert query.startswith("FIND%20RECORD%20" + PERSON.name) + return MockUpResponse(200, {}, person_xml) + elif kwargs["method"] == "PUT": + assert RETURN_REQUESTED.name in kwargs["body"].decode("utf-8") + p = '<Property name="{RR}">asdf</Property>'.format( + RR=RETURN_REQUESTED.name) + return MockUpResponse(200, {}, loan_xml.format(RR=p)) + + append_resource(resource) + + returner, loan = _issue_return_request(data) + + assert loan.id == 12345 + assert loan.get_property(BOX.name).value == "2345" + assert loan.get_property(BORROWER.name).value == str(returner.id) + assert loan.get_property(RETURN_REQUESTED.name).value == "asdf" + + assert returner.id == 1234 + assert returner.get_property(EMAIL.name).value == data[F_EMAIL] + assert returner.get_property(EMAIL.name).value == data[F_EMAIL]