Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Batched sample registration form with Paste capabilities #2658

Open
wants to merge 16 commits into
base: 2.x
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Changelog
2.6.0 (unreleased)
------------------

- #2658 Batched sample registration form with Paste capabilities
- #2654 Show Batch title or ID in Sample reference field
- #2657 Methods from analyses are not updated on instrument change in worksheet
- #2656 Fix AnalysisProfile keyword validator fail with non-ascii value
Expand Down
10 changes: 10 additions & 0 deletions src/bika/lims/browser/analysisrequest/add2.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
AR_CONFIGURATION_STORAGE = "bika.lims.browser.analysisrequest.manage.add"
SKIP_FIELD_ON_COPY = ["Sample", "PrimaryAnalysisRequest", "Remarks",
"NumSamples", "_ARAttachment"]
NO_COPY_FIELDS = ["_ARAttachment"]


def cache_key(method, self, obj):
Expand Down Expand Up @@ -563,6 +564,15 @@ def is_service_selected(self, service):
return True
return False

def show_copy_button_for(self, field=None):
""" Show copy/paste button for field
"""
if self.ar_count <= 1:
return False
if field and field.getName() in NO_COPY_FIELDS:
return False
return True


class AnalysisRequestManageView(BrowserView):
"""AR Manage View
Expand Down
128 changes: 88 additions & 40 deletions src/bika/lims/browser/analysisrequest/templates/ar_add2.pt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,18 @@

<!-- Handlebars JS Templates -->

<!-- Add row paste popup -->
<script id="paste-template" type="text/x-handlebars-template">
<div tal:attributes="title string:Enter {{fieldLabel}} fields" i18n:attributes="title">
<div class="paste-container">
<div class="line-numbers"><tal:block tal:repeat="arnum python:range(view.ar_count)"><span tal:replace="python: arnum + 1"/>
</tal:block>
</div>
<textarea class="paste-editor"></textarea>
</div>
</div>
</script>

<!-- Add dependent services popup -->
<script id="dependency-add-template" type="text/x-handlebars-template">
<div title="Service dependencies" i18n:attributes="title">
Expand Down Expand Up @@ -358,47 +370,57 @@
<input type="hidden" id="ar_count" name="ar_count"
tal:attributes="value view/ar_count"/>

<!-- ADD Form -->
<table class="ar-table table table-sm table-bordered">

<tr class="ar-table-row">
<td>
<div id="manage-ar-fields"
tal:condition="python:user.has_role('LabManager') or user.has_role('Manager')">
<a id="manage-ar-fields-link"
href=""
class="btn btn-link"
target="_blank"
tal:attributes="href python:context.absolute_url() + '/ar_add_manage'">
&#9998;
<span i18n:translate="">Manage Form Fields</span>
</a>
</div>
</td>
<td></td>
<tal:columns tal:repeat="arnum python:range(view.ar_count)">
<td class="ar-column-header">
<ul class="nav nav-tabs float-right" id="sample-tabs" style="margin-bottom:-1px;">
<tal:columns tal:repeat="arnum python:range(view.ar_count)">
<li class="nav-item" tal:attributes="class string:nav-item nav-item-${arnum}">
<a tal:attributes="class python:'nav-link active' if arnum == 0 else 'nav-link';
data-primary-sample-index string:${arnum};
data-target string:sample-column-${arnum}"
href="#">
<span i18n:translate="">Sample</span>
<span tal:content="python: arnum + 1"></span>
</td>
</tal:columns>
</tr>
</a>
</li>
</tal:columns>
<li class="nav-item">
<a class="nav-link"
data-target="show-all"
data-primary-sample-index="0"
href="#">
<span i18n:translate="">Show all</span>
</a>
</li>
<li class="nav-item" tal:condition="python:user.has_role('LabManager') or user.has_role('Manager')">
<a class="nav-link"
data-target="manage-form-fields"
i18n:attributes="title"
title="Manage Form Fields"
target="_blank"
tal:attributes="href python:context.absolute_url() + '/ar_add_manage'">
<i class="fas fa-tasks"></i>
</a>
</li>
</ul>

<!-- ADD Form -->
<table class="ar-table table table-sm table-bordered">

<!-- All edit fields with fields with add=visible -->
<tal:field tal:repeat="field python:view.get_fields_with_visibility('edit')">
<tal:def define="fieldName python:field.getName();
<tal:def define="widget python:field.widget;
fieldName python:field.getName();
fieldLabel python:view.context.translate(widget.Label(here));
required python:bool(field.required);
widget python:field.widget;
errors python:{};
mode python:'edit';">
<tr class="ar-table-row"
tal:attributes="fieldName python:fieldName;
fieldLabel python:widget.Label(context)">
fieldLabel python:fieldLabel">
<td class="field-label">
<label class="formQuestion">
<span
tal:attributes="class python:required and 'text-primary' or ''"
tal:content="python:view.context.translate(widget.Label(here))">
tal:content="python:fieldLabel">
Label
</span>
<span class="fieldRequired"
Expand All @@ -412,16 +434,35 @@
</label>
</td>
<td>
<!-- Copy Button -->
<img class="copybutton btn-link"
width="16"
tal:condition="python:view.ar_count > 1"
tal:attributes="src senaite_theme/icon_url/copy;"/>
<div class="text-center" tal:condition="python:view.show_copy_button_for(field)">
<!-- Copy Button -->
<a class="copy d-block"
href="#"
title="Copy the value of the first sample to the others"
data_toggle="tooltip"
tal:attributes="fieldName python:fieldName;
fieldLabel python:fieldLabel">
<i class="fas fa-angle-double-right"></i>
</a>
<!-- Paste Button -->
<a class="paste d-block"
href="#"
title="Enter individual values ​​for this field per sample"
data_toggle="tooltip"
tal:attributes="fieldName python:fieldName;
fieldLabel python:fieldLabel">
<i class="fas fa-list-ol"></i>
</a>
</div>
</td>

<tal:columns tal:repeat="arnum python:range(view.ar_count)">
<td tal:define="newFieldName python:view.get_fieldname(field, arnum)"
tal:attributes="arnum arnum; fieldName newFieldName">
<td tal:define="newFieldName python:view.get_fieldname(field, arnum);
cls string: sample-column sample-column-${arnum};
cls python:cls + ' d-none' if arnum > 0 else cls;"
tal:attributes="class python:cls;
arnum arnum;
fieldName newFieldName">
<!-- The input field is rendered here -->
<metal:field use-macro="python:view.get_input_widget(fieldName, arnum, mode='edit')"/>
</td>
Expand Down Expand Up @@ -516,20 +557,27 @@
</div>
</td>
<td>
<img class="copybutton btn-link"
width="16"
tal:condition="python:view.ar_count > 1"
tal:attributes="src python:senaite_theme.icon_url('copy');"/>
<div class="text-center" tal:condition="python:view.show_copy_button_for()">
<!-- Copy Button -->
<a class="copy d-block"
title="Copy the currently displayed column value to the other columns on the right"
data_toggle="tooltip">
<i class="fas fa-angle-double-right"></i>
</a>
</div>
</td>

<tal:columns tal:repeat="arnum python:range(ar_count)">
<td tal:define="fieldname python:'Analyses-{}'.format(arnum);
analyses python:view.fieldvalues.get(fieldname) or [];
service_uids python:map(view.get_service_uid_from, analyses);
checked python:service_uid in service_uids;"
checked python:service_uid in service_uids;
cls python:'{}-column service-column'.format(service_uid);
cls string: ${cls} sample-column sample-column-${arnum};
cls python:cls + ' d-none' if arnum > 0 else cls;"
tal:attributes="fieldname python:'Analyses-{}'.format(arnum);
uid python:service_uid;
class python:'{}-column service-column'.format(service_uid);
class python:cls;
arnum python:arnum;">

<!-- Service locked button -->
Expand Down
1 change: 1 addition & 0 deletions src/senaite/core/browser/static/assets/icons/clipboard.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,38 @@
display:none;
}
/* /Service conditions */


/* Paste popup */
.paste-container {
display: flex;
border: 1px solid #ccc;
font-family: monospace;
height: 300px;
overflow: hidden;
}

.line-numbers {
background-color: #f4f4f4;
border-right: 1px solid #ccc;
text-align: right;
color: #666;
overflow: hidden;
line-height: 1.5;
padding: 0 .5em;
}

.paste-editor {
flex: 1;
border: none;
outline: none;
resize: both;
margin: 0;
line-height: 1.5;
}

.paste-container .line-numbers,
.paste-container .paste-editor {
white-space: pre-line;
line-height: 1.5;
}
Loading