feat: add edit/sync support for Log Quantities
er, just Standard Quantities so far..and just supported enough to move the ball forward, it still needs lots more polish
This commit is contained in:
parent
1d303a818c
commit
a43f98c304
8 changed files with 555 additions and 26 deletions
|
|
@ -151,6 +151,32 @@ class WuttaFarmAppHandler(base.AppHandler):
|
||||||
factory = self.load_object(spec)
|
factory = self.load_object(spec)
|
||||||
return factory(self.config, farmos_client)
|
return factory(self.config, farmos_client)
|
||||||
|
|
||||||
|
def get_quantity_types(self, session=None):
|
||||||
|
"""
|
||||||
|
Returns a list of all known quantity types.
|
||||||
|
"""
|
||||||
|
model = self.model
|
||||||
|
with self.short_session(session=session) as sess:
|
||||||
|
return (
|
||||||
|
sess.query(model.QuantityType).order_by(model.QuantityType.name).all()
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_measures(self, session=None):
|
||||||
|
"""
|
||||||
|
Returns a list of all known measures.
|
||||||
|
"""
|
||||||
|
model = self.model
|
||||||
|
with self.short_session(session=session) as sess:
|
||||||
|
return sess.query(model.Measure).order_by(model.Measure.name).all()
|
||||||
|
|
||||||
|
def get_units(self, session=None):
|
||||||
|
"""
|
||||||
|
Returns a list of all known units.
|
||||||
|
"""
|
||||||
|
model = self.model
|
||||||
|
with self.short_session(session=session) as sess:
|
||||||
|
return sess.query(model.Unit).order_by(model.Unit.name).all()
|
||||||
|
|
||||||
def auto_sync_to_farmos(self, obj, model_name=None, client=None, require=True):
|
def auto_sync_to_farmos(self, obj, model_name=None, client=None, require=True):
|
||||||
"""
|
"""
|
||||||
Export the given object to farmOS, using configured handler.
|
Export the given object to farmOS, using configured handler.
|
||||||
|
|
|
||||||
|
|
@ -181,9 +181,13 @@ class Quantity(model.Base):
|
||||||
creator=make_log_quantity,
|
creator=make_log_quantity,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_value_decimal(self):
|
||||||
|
# TODO: should actually return a decimal here?
|
||||||
|
return self.value_numerator / self.value_denominator
|
||||||
|
|
||||||
def render_as_text(self, config=None):
|
def render_as_text(self, config=None):
|
||||||
measure = str(self.measure or self.measure_id or "")
|
measure = str(self.measure or self.measure_id or "")
|
||||||
value = self.value_numerator / self.value_denominator
|
value = self.get_value_decimal()
|
||||||
if config:
|
if config:
|
||||||
app = config.get_app()
|
app = config.get_app()
|
||||||
value = app.render_quantity(value)
|
value = app.render_quantity(value)
|
||||||
|
|
|
||||||
|
|
@ -437,21 +437,46 @@ class AssetRefs(WuttaSet):
|
||||||
return AssetRefsWidget(self.request, **kwargs)
|
return AssetRefsWidget(self.request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class LogQuantityRefs(WuttaSet):
|
class QuantityRefs(colander.List):
|
||||||
"""
|
"""
|
||||||
Schema type for Quantities field (on a Log record)
|
Schema type for Quantities field (on a Log record)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
super().__init__()
|
||||||
|
self.request = request
|
||||||
|
self.config = self.request.wutta_config
|
||||||
|
self.app = self.config.get_app()
|
||||||
|
|
||||||
def serialize(self, node, appstruct):
|
def serialize(self, node, appstruct):
|
||||||
if not appstruct:
|
if not appstruct:
|
||||||
return colander.null
|
return colander.null
|
||||||
|
|
||||||
return {qty.uuid for qty in appstruct}
|
quantities = []
|
||||||
|
for qty in appstruct:
|
||||||
|
quantities.append(
|
||||||
|
{
|
||||||
|
"uuid": qty.uuid.hex,
|
||||||
|
"quantity_type": {
|
||||||
|
"id": qty.quantity_type_id,
|
||||||
|
"name": qty.quantity_type.name,
|
||||||
|
},
|
||||||
|
"measure": qty.measure_id,
|
||||||
|
"value": qty.get_value_decimal(),
|
||||||
|
"units": {
|
||||||
|
"uuid": qty.units.uuid.hex,
|
||||||
|
"name": qty.units.name,
|
||||||
|
},
|
||||||
|
"as_text": qty.render_as_text(self.config),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return quantities
|
||||||
|
|
||||||
def widget_maker(self, **kwargs):
|
def widget_maker(self, **kwargs):
|
||||||
from wuttafarm.web.forms.widgets import LogQuantityRefsWidget
|
from wuttafarm.web.forms.widgets import QuantityRefsWidget
|
||||||
|
|
||||||
return LogQuantityRefsWidget(self.request, **kwargs)
|
return QuantityRefsWidget(self.request, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class OwnerRefs(WuttaSet):
|
class OwnerRefs(WuttaSet):
|
||||||
|
|
|
||||||
|
|
@ -526,11 +526,19 @@ class AssetRefsWidget(Widget):
|
||||||
return set(pstruct.split(","))
|
return set(pstruct.split(","))
|
||||||
|
|
||||||
|
|
||||||
class LogQuantityRefsWidget(WuttaCheckboxChoiceWidget):
|
class QuantityRefsWidget(Widget):
|
||||||
"""
|
"""
|
||||||
Widget for Quantities field (on a Log record)
|
Widget for Quantities field (on a Log record)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
template = "quantityrefs"
|
||||||
|
|
||||||
|
def __init__(self, request, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.request = request
|
||||||
|
self.config = self.request.wutta_config
|
||||||
|
self.app = self.config.get_app()
|
||||||
|
|
||||||
def serialize(self, field, cstruct, **kw):
|
def serialize(self, field, cstruct, **kw):
|
||||||
""" """
|
""" """
|
||||||
model = self.app.model
|
model = self.app.model
|
||||||
|
|
@ -538,24 +546,71 @@ class LogQuantityRefsWidget(WuttaCheckboxChoiceWidget):
|
||||||
|
|
||||||
readonly = kw.get("readonly", self.readonly)
|
readonly = kw.get("readonly", self.readonly)
|
||||||
if readonly:
|
if readonly:
|
||||||
|
if not cstruct:
|
||||||
|
return ""
|
||||||
|
|
||||||
quantities = []
|
quantities = []
|
||||||
for uuid in cstruct or []:
|
|
||||||
qty = session.get(model.Quantity, uuid)
|
for qty in cstruct:
|
||||||
quantities.append(
|
# TODO: support more quantity types
|
||||||
HTML.tag(
|
url = self.request.route_url(
|
||||||
"li",
|
"quantities_standard.view", uuid=qty["uuid"]
|
||||||
c=tags.link_to(
|
|
||||||
qty.render_as_text(self.config),
|
|
||||||
# TODO
|
|
||||||
self.request.route_url(
|
|
||||||
"quantities_standard.view", uuid=qty.uuid
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
quantities.append(HTML.tag("li", c=tags.link_to(qty["as_text"], url)))
|
||||||
|
|
||||||
return HTML.tag("ul", c=quantities)
|
return HTML.tag("ul", c=quantities)
|
||||||
|
|
||||||
return super().serialize(field, cstruct, **kw)
|
tmpl_values = self.get_template_values(field, cstruct, kw)
|
||||||
|
return field.renderer(self.template, **tmpl_values)
|
||||||
|
|
||||||
|
def get_template_values(self, field, cstruct, kw):
|
||||||
|
model = self.app.model
|
||||||
|
session = Session()
|
||||||
|
values = super().get_template_values(field, cstruct, kw)
|
||||||
|
|
||||||
|
qtypes = []
|
||||||
|
for qtype in self.app.get_quantity_types(session):
|
||||||
|
# TODO: add support for other quantity types
|
||||||
|
if qtype.drupal_id == "standard":
|
||||||
|
qtypes.append(
|
||||||
|
{
|
||||||
|
"uuid": qtype.uuid.hex,
|
||||||
|
"drupal_id": qtype.drupal_id,
|
||||||
|
"name": qtype.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
values["quantity_types"] = qtypes
|
||||||
|
|
||||||
|
measures = []
|
||||||
|
for measure in self.app.get_measures(session):
|
||||||
|
measures.append(
|
||||||
|
{
|
||||||
|
"uuid": measure.uuid.hex,
|
||||||
|
"drupal_id": measure.drupal_id,
|
||||||
|
"name": measure.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
values["measures"] = measures
|
||||||
|
|
||||||
|
units = []
|
||||||
|
for unit in self.app.get_units(session):
|
||||||
|
units.append(
|
||||||
|
{
|
||||||
|
"uuid": unit.uuid.hex,
|
||||||
|
"drupal_id": unit.drupal_id,
|
||||||
|
"name": unit.name,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
values["units"] = units
|
||||||
|
|
||||||
|
return values
|
||||||
|
|
||||||
|
def deserialize(self, field, pstruct):
|
||||||
|
""" """
|
||||||
|
if not pstruct:
|
||||||
|
return set()
|
||||||
|
|
||||||
|
return json.loads(pstruct)
|
||||||
|
|
||||||
|
|
||||||
class OwnerRefsWidget(WuttaCheckboxChoiceWidget):
|
class OwnerRefsWidget(WuttaCheckboxChoiceWidget):
|
||||||
|
|
|
||||||
13
src/wuttafarm/web/templates/deform/quantityrefs.pt
Normal file
13
src/wuttafarm/web/templates/deform/quantityrefs.pt
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<div tal:define="
|
||||||
|
name name|field.name;
|
||||||
|
oid oid|field.oid;
|
||||||
|
vmodel vmodel|'modelData.'+oid;"
|
||||||
|
tal:omit-tag="">
|
||||||
|
|
||||||
|
<quantities-editor tal:attributes="name name;
|
||||||
|
v-model vmodel;
|
||||||
|
:quantity-types quantity_types;
|
||||||
|
:measures measures;
|
||||||
|
:units units;" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
<%def name="make_wuttafarm_components()">
|
<%def name="make_wuttafarm_components()">
|
||||||
${self.make_assets_picker_component()}
|
${self.make_assets_picker_component()}
|
||||||
${self.make_animal_type_picker_component()}
|
${self.make_animal_type_picker_component()}
|
||||||
|
${self.make_quantity_editor_component()}
|
||||||
|
${self.make_quantities_editor_component()}
|
||||||
${self.make_plant_types_picker_component()}
|
${self.make_plant_types_picker_component()}
|
||||||
${self.make_seasons_picker_component()}
|
${self.make_seasons_picker_component()}
|
||||||
</%def>
|
</%def>
|
||||||
|
|
@ -239,6 +241,356 @@
|
||||||
</script>
|
</script>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_quantity_editor_component()">
|
||||||
|
<script type="text/x-template" id="quantity-editor-template">
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<b-field label="Measure" horizontal
|
||||||
|
## TODO: why is this needed?
|
||||||
|
style="margin-bottom: 1rem;">
|
||||||
|
<b-select v-model="value.measure"
|
||||||
|
ref="measure">
|
||||||
|
<option v-for="m in measures"
|
||||||
|
:value="m.drupal_id">
|
||||||
|
{{ m.name }}
|
||||||
|
</option>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Value" horizontal
|
||||||
|
## TODO: why is this needed?
|
||||||
|
style="margin-bottom: 1rem;">
|
||||||
|
<b-input v-model="value.value"
|
||||||
|
ref="value"
|
||||||
|
@keydown.native="inputKeydown" />
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Units" horizontal
|
||||||
|
style="margin-bottom: 1rem;">
|
||||||
|
|
||||||
|
<b-button v-if="value.units.uuid"
|
||||||
|
@click="changeUnit()">
|
||||||
|
{{ value.units.name }} (click to change)
|
||||||
|
</b-button>
|
||||||
|
|
||||||
|
<b-autocomplete v-show="!value.units.uuid"
|
||||||
|
v-model="unitsName"
|
||||||
|
ref="unitsName"
|
||||||
|
:data="unitsNameData"
|
||||||
|
field="name"
|
||||||
|
open-on-focus
|
||||||
|
keep-first
|
||||||
|
@select="unitsNameSelected"
|
||||||
|
clear-on-select>
|
||||||
|
<template #empty>No results found</template>
|
||||||
|
</b-autocomplete>
|
||||||
|
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<div class="buttons" style="margin-left: 8rem;">
|
||||||
|
<b-button type="is-primary"
|
||||||
|
ref="save"
|
||||||
|
@click="save"
|
||||||
|
:disabled="saveDisabled">
|
||||||
|
{{ creating ? "Create" : "Update" }} Quantity
|
||||||
|
</b-button>
|
||||||
|
<b-button @click="$emit('cancel')">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const QuantityEditor = {
|
||||||
|
template: '#quantity-editor-template',
|
||||||
|
props: {
|
||||||
|
name: String,
|
||||||
|
value: Object,
|
||||||
|
measures: Array,
|
||||||
|
units: Array,
|
||||||
|
creating: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
measure: this.value.measure,
|
||||||
|
valueAmount: this.value.value,
|
||||||
|
unit: this.value.units,
|
||||||
|
unitsName: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
|
||||||
|
saveDisabled() {
|
||||||
|
if (!this.value.measure) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (!this.value.value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (!this.value.units.uuid) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
|
||||||
|
unitsNameData() {
|
||||||
|
if (!this.unitsName) {
|
||||||
|
return this.units
|
||||||
|
}
|
||||||
|
return this.units.filter((unit) => {
|
||||||
|
return unit.name.toLowerCase().indexOf(this.unitsName.toLowerCase()) >= 0
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
focusMeasure() {
|
||||||
|
this.$refs.measure.focus()
|
||||||
|
},
|
||||||
|
|
||||||
|
focusValue() {
|
||||||
|
this.$refs.value.focus()
|
||||||
|
},
|
||||||
|
|
||||||
|
changeUnit() {
|
||||||
|
this.value.units = {}
|
||||||
|
this.$emit('input', this.value)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.unitsName.focus()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
unitsNameSelected(option) {
|
||||||
|
if (option) {
|
||||||
|
this.value.units = option
|
||||||
|
this.$emit('input', this.value)
|
||||||
|
this.unitsName = null
|
||||||
|
if (this.value.measure && this.value.value) {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.save.$el.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
inputKeydown(event) {
|
||||||
|
// nb. must prevent main form submit on ENTER
|
||||||
|
// (since ultimately this lives within an outer form)
|
||||||
|
// but also we can submit the modal pseudo-form
|
||||||
|
if (event.which == 13) {
|
||||||
|
event.preventDefault()
|
||||||
|
this.save()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
save() {
|
||||||
|
this.$emit('save', this.value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Vue.component('quantity-editor', QuantityEditor)
|
||||||
|
<% request.register_component('quantity-editor', 'QuantityEditor') %>
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="make_quantities_editor_component()">
|
||||||
|
<script type="text/x-template" id="quantities-editor-template">
|
||||||
|
<div>
|
||||||
|
<input type="hidden" :name="name" :value="value ? JSON.stringify(value) : ''" />
|
||||||
|
|
||||||
|
<${b}-table ref="table"
|
||||||
|
:data="value || []"
|
||||||
|
detailed
|
||||||
|
detail-key="uuid"
|
||||||
|
icon-pack="fas"
|
||||||
|
:show-detail-icon="false">
|
||||||
|
|
||||||
|
<${b}-table-column field="as_text"
|
||||||
|
v-slot="props"
|
||||||
|
label="Quantity">
|
||||||
|
<span>{{ props.row.as_text }}</span>
|
||||||
|
</${b}-table-column>
|
||||||
|
|
||||||
|
<${b}-table-column field="quantity_type"
|
||||||
|
v-slot="props"
|
||||||
|
label="Quantity Type">
|
||||||
|
<span>{{ props.row.quantity_type.name }}</span>
|
||||||
|
</${b}-table-column>
|
||||||
|
|
||||||
|
<${b}-table-column v-slot="props">
|
||||||
|
<div v-show="!editing[props.row.uuid]">
|
||||||
|
<a href="#"
|
||||||
|
@click.prevent="editInit(props.row)">
|
||||||
|
<i class="fas fa-edit" /> Edit
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a href="#"
|
||||||
|
class="has-text-danger"
|
||||||
|
@click.prevent="removeQuantity(props.row)">
|
||||||
|
<i class="fas fa-trash" /> Remove
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</${b}-table-column>
|
||||||
|
|
||||||
|
<template #detail="props">
|
||||||
|
<quantity-editor v-model="props.row"
|
||||||
|
:measures="measures"
|
||||||
|
:units="units"
|
||||||
|
@save="editSave"
|
||||||
|
@cancel="editCancel(props.row)" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</${b}-table>
|
||||||
|
|
||||||
|
<b-field grouped
|
||||||
|
v-show="!creating">
|
||||||
|
|
||||||
|
<b-select v-model="quantityType">
|
||||||
|
<option v-for="qtype of quantityTypes"
|
||||||
|
:value="qtype.drupal_id">
|
||||||
|
{{ qtype.name }}
|
||||||
|
</option>
|
||||||
|
</b-select>
|
||||||
|
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="plus"
|
||||||
|
@click="createInit()">
|
||||||
|
Add New Quantity
|
||||||
|
</b-button>
|
||||||
|
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<quantity-editor v-show="creating"
|
||||||
|
v-model="newQuantity"
|
||||||
|
ref="newQuantity"
|
||||||
|
creating
|
||||||
|
@save="createSave"
|
||||||
|
@cancel="createCancel"
|
||||||
|
:measures="measures"
|
||||||
|
:units="units" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
|
const QuantitiesEditor = {
|
||||||
|
template: '#quantities-editor-template',
|
||||||
|
props: {
|
||||||
|
name: String,
|
||||||
|
value: Array,
|
||||||
|
quantityTypes: Array,
|
||||||
|
defaultQuantityType: {
|
||||||
|
type: String,
|
||||||
|
default: 'standard',
|
||||||
|
},
|
||||||
|
measures: Array,
|
||||||
|
units: Array,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
|
||||||
|
const editing = {}
|
||||||
|
if (this.value) {
|
||||||
|
for (qty of this.value) {
|
||||||
|
editing[qty.uuid] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const measureMap = {}
|
||||||
|
for (let m of this.measures) {
|
||||||
|
measureMap[m.drupal_id] = m.name
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
measureMap,
|
||||||
|
quantityType: this.defaultQuantityType,
|
||||||
|
editShowDialog: false,
|
||||||
|
editNew: true,
|
||||||
|
editQuantity: null,
|
||||||
|
editMeasure: null,
|
||||||
|
editValue: null,
|
||||||
|
editUnits: null,
|
||||||
|
editing: editing,
|
||||||
|
creating: false,
|
||||||
|
newQuantity: {
|
||||||
|
measure: null,
|
||||||
|
value: null,
|
||||||
|
units: {},
|
||||||
|
},
|
||||||
|
newCounter: 1,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
|
||||||
|
createSave(qty) {
|
||||||
|
qty = Object.fromEntries(Object.entries(qty))
|
||||||
|
|
||||||
|
qty.uuid = 'new_' + this.newCounter++
|
||||||
|
qty.as_text = "( " + this.measureMap[qty.measure] + " ) " + qty.value + " " + qty.units.name
|
||||||
|
|
||||||
|
const value = Array.from(this.value || [])
|
||||||
|
value.push(qty)
|
||||||
|
this.$emit('input', value)
|
||||||
|
|
||||||
|
this.creating = false
|
||||||
|
},
|
||||||
|
|
||||||
|
createCancel() {
|
||||||
|
this.creating = false
|
||||||
|
},
|
||||||
|
|
||||||
|
createInit() {
|
||||||
|
|
||||||
|
this.newQuantity.quantity_type = {
|
||||||
|
drupal_id: this.quantityType,
|
||||||
|
## TODO: add support for other quantity types
|
||||||
|
name: "Standard",
|
||||||
|
}
|
||||||
|
|
||||||
|
this.newQuantity.measure = null
|
||||||
|
this.newQuantity.value = null
|
||||||
|
this.newQuantity.units = {}
|
||||||
|
|
||||||
|
this.creating = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.newQuantity.focusMeasure()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
editInit(qty) {
|
||||||
|
this.$refs.table.openDetailRow(qty)
|
||||||
|
this.editing[qty.uuid] = true
|
||||||
|
},
|
||||||
|
|
||||||
|
editSave(row) {
|
||||||
|
row.as_text = "( " + this.measureMap[row.measure] + " ) " + row.value + " " + row.units.name
|
||||||
|
this.$emit('input', this.value)
|
||||||
|
this.editing[row.uuid] = false
|
||||||
|
this.$refs.table.closeDetailRow(row)
|
||||||
|
},
|
||||||
|
|
||||||
|
editCancel(qty) {
|
||||||
|
this.$refs.table.closeDetailRow(qty)
|
||||||
|
this.editing[qty.uuid] = false
|
||||||
|
},
|
||||||
|
|
||||||
|
removeQuantity(qty) {
|
||||||
|
let value = Array.from(this.value)
|
||||||
|
const i = value.indexOf(qty)
|
||||||
|
value.splice(i, 1)
|
||||||
|
this.$emit('input', value)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Vue.component('quantities-editor', QuantitiesEditor)
|
||||||
|
<% request.register_component('quantities-editor', 'QuantitiesEditor') %>
|
||||||
|
</script>
|
||||||
|
</%def>
|
||||||
|
|
||||||
<%def name="make_plant_types_picker_component()">
|
<%def name="make_plant_types_picker_component()">
|
||||||
<script type="text/x-template" id="plant-types-picker-template">
|
<script type="text/x-template" id="plant-types-picker-template">
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ from wuttaweb.forms.widgets import WuttaDateTimeWidget
|
||||||
|
|
||||||
from wuttafarm.web.views import WuttaFarmMasterView
|
from wuttafarm.web.views import WuttaFarmMasterView
|
||||||
from wuttafarm.db.model import LogType, Log
|
from wuttafarm.db.model import LogType, Log
|
||||||
from wuttafarm.web.forms.schema import AssetRefs, LogQuantityRefs, OwnerRefs
|
from wuttafarm.web.forms.schema import AssetRefs, QuantityRefs, OwnerRefs
|
||||||
from wuttafarm.util import get_log_type_enum
|
from wuttafarm.util import get_log_type_enum
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -287,10 +287,8 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
f.set_readonly("log_type")
|
f.set_readonly("log_type")
|
||||||
|
|
||||||
# quantities
|
# quantities
|
||||||
if self.creating or self.editing:
|
f.set_node("quantities", QuantityRefs(self.request))
|
||||||
f.remove("quantities") # TODO: need to support this
|
if not self.creating:
|
||||||
else:
|
|
||||||
f.set_node("quantities", LogQuantityRefs(self.request))
|
|
||||||
# nb. must explicity declare value for non-standard field
|
# nb. must explicity declare value for non-standard field
|
||||||
f.set_default("quantities", log.quantities)
|
f.set_default("quantities", log.quantities)
|
||||||
|
|
||||||
|
|
@ -332,6 +330,7 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
self.set_assets(log, data["assets"])
|
self.set_assets(log, data["assets"])
|
||||||
self.set_locations(log, data["locations"])
|
self.set_locations(log, data["locations"])
|
||||||
self.set_groups(log, data["groups"])
|
self.set_groups(log, data["groups"])
|
||||||
|
self.set_quantities(log, data["quantities"])
|
||||||
|
|
||||||
return log
|
return log
|
||||||
|
|
||||||
|
|
@ -386,6 +385,58 @@ class LogMasterView(WuttaFarmMasterView):
|
||||||
assert group
|
assert group
|
||||||
log.groups.remove(group)
|
log.groups.remove(group)
|
||||||
|
|
||||||
|
def set_quantities(self, log, desired):
|
||||||
|
model = self.app.model
|
||||||
|
session = self.Session()
|
||||||
|
|
||||||
|
current = {qty.uuid.hex: qty for qty in log.quantities}
|
||||||
|
for new_qty in desired:
|
||||||
|
units = session.get(model.Unit, new_qty["units"]["uuid"])
|
||||||
|
assert units
|
||||||
|
if new_qty["uuid"].startswith("new_"):
|
||||||
|
assert new_qty["quantity_type"]["drupal_id"] == "standard"
|
||||||
|
factory = model.StandardQuantity
|
||||||
|
qty = factory(
|
||||||
|
quantity_type_id=new_qty["quantity_type"]["drupal_id"],
|
||||||
|
measure_id=new_qty["measure"],
|
||||||
|
value_numerator=int(new_qty["value"]),
|
||||||
|
value_denominator=1,
|
||||||
|
units=units,
|
||||||
|
)
|
||||||
|
# nb. must ensure "typed" quantity record persists!
|
||||||
|
session.add(qty)
|
||||||
|
# but must add "generic" quantity record to log
|
||||||
|
log.quantities.append(qty.quantity)
|
||||||
|
else:
|
||||||
|
old_qty = current[new_qty["uuid"]]
|
||||||
|
old_qty.measure_id = new_qty["measure"]
|
||||||
|
old_qty.value_numerator = int(new_qty["value"])
|
||||||
|
old_qty.value_denominator = 1
|
||||||
|
old_qty.units = units
|
||||||
|
|
||||||
|
desired = [qty["uuid"] for qty in desired]
|
||||||
|
for old_qty in list(log.quantities):
|
||||||
|
# nb. "old_qty" may be newly-created, w/ no uuid yet
|
||||||
|
# (this logic may break if session gets flushed early!)
|
||||||
|
if old_qty.uuid and old_qty.uuid.hex not in desired:
|
||||||
|
log.quantities.remove(old_qty)
|
||||||
|
|
||||||
|
def auto_sync_to_farmos(self, client, log):
|
||||||
|
model = self.app.model
|
||||||
|
session = self.Session()
|
||||||
|
|
||||||
|
# nb. ensure quantities have uuid keys
|
||||||
|
session.flush()
|
||||||
|
|
||||||
|
for qty in log.quantities:
|
||||||
|
# TODO: support more quantity types
|
||||||
|
if qty.quantity_type_id == "standard":
|
||||||
|
qty = session.get(model.StandardQuantity, qty.uuid)
|
||||||
|
assert qty
|
||||||
|
self.app.auto_sync_to_farmos(qty, client=client)
|
||||||
|
|
||||||
|
self.app.auto_sync_to_farmos(log, client=client)
|
||||||
|
|
||||||
def get_farmos_url(self, log):
|
def get_farmos_url(self, log):
|
||||||
return self.app.get_farmos_url(f"/log/{log.drupal_id}")
|
return self.app.get_farmos_url(f"/log/{log.drupal_id}")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,10 @@ class WuttaFarmMasterView(MasterView):
|
||||||
# maybe also sync change to farmOS
|
# maybe also sync change to farmOS
|
||||||
if self.app.is_farmos_mirror():
|
if self.app.is_farmos_mirror():
|
||||||
client = get_farmos_client_for_user(self.request)
|
client = get_farmos_client_for_user(self.request)
|
||||||
self.app.auto_sync_to_farmos(obj, client=client, require=False)
|
self.auto_sync_to_farmos(client, obj)
|
||||||
|
|
||||||
|
def auto_sync_to_farmos(self, client, obj):
|
||||||
|
self.app.auto_sync_to_farmos(obj, client=client, require=False)
|
||||||
|
|
||||||
def get_farmos_entity_type(self):
|
def get_farmos_entity_type(self):
|
||||||
if self.farmos_entity_type:
|
if self.farmos_entity_type:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue